mirror of
https://github.com/bringout/oca-ocb-core.git
synced 2026-04-20 07:32:08 +02:00
vanilla 17.0
This commit is contained in:
parent
d72e748793
commit
a9bcec8e91
1986 changed files with 1613876 additions and 568976 deletions
|
|
@ -1,136 +0,0 @@
|
|||
/** @odoo-module **/
|
||||
|
||||
import { getFixture, patchDate, patchWithCleanup } from "@web/../tests/helpers/utils";
|
||||
import { browser } from "@web/core/browser/browser";
|
||||
import { ControlPanel } from "@web/search/control_panel/control_panel";
|
||||
import {
|
||||
getFacetTexts,
|
||||
makeWithSearch,
|
||||
removeFacet,
|
||||
setupControlPanelServiceRegistry,
|
||||
toggleComparisonMenu,
|
||||
toggleFilterMenu,
|
||||
toggleMenuItem,
|
||||
toggleMenuItemOption,
|
||||
} from "./helpers";
|
||||
|
||||
let target;
|
||||
let serverData;
|
||||
QUnit.module("Search", (hooks) => {
|
||||
hooks.beforeEach(async () => {
|
||||
serverData = {
|
||||
models: {
|
||||
foo: {
|
||||
fields: {
|
||||
birthday: { string: "Birthday", type: "date", store: true, sortable: true },
|
||||
date_field: { string: "Date", type: "date", store: true, sortable: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
views: {
|
||||
"foo,false,search": `
|
||||
<search>
|
||||
<filter name="birthday" date="birthday"/>
|
||||
<filter name="date_field" date="date_field"/>
|
||||
</search>
|
||||
`,
|
||||
},
|
||||
};
|
||||
setupControlPanelServiceRegistry();
|
||||
patchWithCleanup(browser, {
|
||||
setTimeout: (fn) => fn(),
|
||||
clearTimeout: () => {},
|
||||
});
|
||||
target = getFixture();
|
||||
});
|
||||
|
||||
QUnit.module("Comparison");
|
||||
|
||||
QUnit.test("simple rendering", async function (assert) {
|
||||
patchDate(1997, 0, 9, 12, 0, 0);
|
||||
await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
searchMenuTypes: ["filter", "comparison"],
|
||||
searchViewId: false,
|
||||
});
|
||||
assert.containsOnce(target, ".dropdown.o_filter_menu");
|
||||
assert.containsNone(target, ".dropdown.o_comparison_menu");
|
||||
await toggleFilterMenu(target);
|
||||
await toggleMenuItem(target, "Birthday");
|
||||
await toggleMenuItemOption(target, "Birthday", "January");
|
||||
assert.containsOnce(target, "div.o_comparison_menu > button i.fa.fa-adjust");
|
||||
assert.strictEqual(
|
||||
target
|
||||
.querySelector("div.o_comparison_menu > button span")
|
||||
.innerText.trim()
|
||||
.toUpperCase() /** @todo why do I need to upperCase */,
|
||||
"COMPARISON"
|
||||
);
|
||||
await toggleComparisonMenu(target);
|
||||
assert.containsN(target, ".o_comparison_menu .dropdown-item", 2);
|
||||
assert.containsN(target, ".o_comparison_menu .dropdown-item[role=menuitemcheckbox]", 2);
|
||||
const comparisonOptions = [...target.querySelectorAll(".o_comparison_menu .dropdown-item")];
|
||||
assert.deepEqual(
|
||||
comparisonOptions.map((e) => e.innerText.trim()),
|
||||
["Birthday: Previous Period", "Birthday: Previous Year"]
|
||||
);
|
||||
assert.deepEqual(
|
||||
comparisonOptions.map((e) => e.ariaChecked),
|
||||
["false", "false"]
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test("activate a comparison works", async function (assert) {
|
||||
patchDate(1997, 0, 9, 12, 0, 0);
|
||||
await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
searchMenuTypes: ["filter", "comparison"],
|
||||
searchViewId: false,
|
||||
});
|
||||
await toggleFilterMenu(target);
|
||||
await toggleMenuItem(target, "Birthday");
|
||||
await toggleMenuItemOption(target, "Birthday", "January");
|
||||
await toggleComparisonMenu(target);
|
||||
await toggleMenuItem(target, "Birthday: Previous Period");
|
||||
assert.deepEqual(getFacetTexts(target), [
|
||||
"Birthday: January 1997",
|
||||
"Birthday: Previous Period",
|
||||
]);
|
||||
await toggleFilterMenu(target);
|
||||
await toggleMenuItem(target, "Date");
|
||||
await toggleMenuItemOption(target, "Date", "December");
|
||||
await toggleComparisonMenu(target);
|
||||
await toggleMenuItem(target, "Date: Previous Year");
|
||||
assert.deepEqual(getFacetTexts(target), [
|
||||
["Birthday: January 1997", "Date: December 1996"].join("or"),
|
||||
"Date: Previous Year",
|
||||
]);
|
||||
await toggleFilterMenu(target);
|
||||
await toggleMenuItem(target, "Date");
|
||||
await toggleMenuItemOption(target, "Date", "1996");
|
||||
assert.deepEqual(getFacetTexts(target), ["Birthday: January 1997"]);
|
||||
await toggleComparisonMenu(target);
|
||||
await toggleMenuItem(target, "Birthday: Previous Year");
|
||||
assert.containsN(target, ".o_comparison_menu .dropdown-item", 2);
|
||||
assert.containsN(target, ".o_comparison_menu .dropdown-item[role=menuitemcheckbox]", 2);
|
||||
const comparisonOptions = [...target.querySelectorAll(".o_comparison_menu .dropdown-item")];
|
||||
assert.deepEqual(
|
||||
comparisonOptions.map((e) => e.innerText.trim()),
|
||||
["Birthday: Previous Period", "Birthday: Previous Year"]
|
||||
);
|
||||
assert.deepEqual(
|
||||
comparisonOptions.map((e) => e.ariaChecked),
|
||||
["false", "true"]
|
||||
);
|
||||
assert.deepEqual(getFacetTexts(target), [
|
||||
"Birthday: January 1997",
|
||||
"Birthday: Previous Year",
|
||||
]);
|
||||
await removeFacet(target);
|
||||
assert.deepEqual(getFacetTexts(target), []);
|
||||
});
|
||||
});
|
||||
|
|
@ -37,16 +37,15 @@ QUnit.module("Search", (hooks) => {
|
|||
searchMenuTypes: [],
|
||||
});
|
||||
|
||||
assert.containsOnce(target, ".o_cp_top");
|
||||
assert.containsOnce(target, ".o_cp_top_left");
|
||||
assert.strictEqual(target.querySelector(".o_cp_top_right").innerHTML, "");
|
||||
assert.containsOnce(target, ".o_cp_bottom");
|
||||
assert.containsOnce(target, ".o_cp_bottom_left");
|
||||
assert.containsOnce(target, ".o_cp_bottom_right");
|
||||
assert.containsOnce(target, ".o_control_panel_breadcrumbs");
|
||||
assert.containsOnce(target, ".o_control_panel_actions");
|
||||
assert.strictEqual(target.querySelector(".o_control_panel_actions").innerHTML, "");
|
||||
assert.containsOnce(target, ".o_control_panel_navigation");
|
||||
assert.strictEqual(target.querySelector(".o_control_panel_navigation").innerHTML, "");
|
||||
|
||||
assert.containsNone(target, ".o_cp_switch_buttons");
|
||||
|
||||
assert.containsOnce(target, ".breadcrumb");
|
||||
assert.containsOnce(target, ".o_breadcrumb");
|
||||
});
|
||||
|
||||
QUnit.test("breadcrumbs", async (assert) => {
|
||||
|
|
@ -63,8 +62,9 @@ QUnit.module("Search", (hooks) => {
|
|||
searchMenuTypes: [],
|
||||
});
|
||||
|
||||
assert.containsN(target, ".breadcrumb li.breadcrumb-item", 2);
|
||||
const breadcrumbItems = target.querySelectorAll("li.breadcrumb-item");
|
||||
const breadcrumbsSelector = ".o_breadcrumb li.breadcrumb-item, .o_breadcrumb .active";
|
||||
assert.containsN(target, breadcrumbsSelector, 2);
|
||||
const breadcrumbItems = target.querySelectorAll(breadcrumbsSelector);
|
||||
assert.strictEqual(breadcrumbItems[0].innerText, "Previous");
|
||||
assert.hasClass(breadcrumbItems[1], "active");
|
||||
assert.strictEqual(breadcrumbItems[1].innerText, "Current");
|
||||
|
|
@ -84,29 +84,25 @@ QUnit.module("Search", (hooks) => {
|
|||
Component: ControlPanel,
|
||||
config: {
|
||||
viewSwitcherEntries: [
|
||||
{
|
||||
type: "list",
|
||||
active: true,
|
||||
icon: "oi-view-list",
|
||||
name: "List",
|
||||
accessKey: "l",
|
||||
},
|
||||
{ type: "kanban", icon: "oi-view-kanban", name: "Kanban", accessKey: "k" },
|
||||
{ type: "list", active: true, icon: "oi-view-list", name: "List" },
|
||||
{ type: "kanban", icon: "oi-view-kanban", name: "Kanban" },
|
||||
],
|
||||
},
|
||||
searchMenuTypes: [],
|
||||
});
|
||||
|
||||
assert.containsOnce(target, ".o_cp_switch_buttons");
|
||||
assert.containsOnce(
|
||||
target,
|
||||
".o_control_panel_navigation .d-xl-inline-flex.o_cp_switch_buttons"
|
||||
);
|
||||
assert.containsN(target, ".o_switch_view", 2);
|
||||
const views = target.querySelectorAll(".o_switch_view");
|
||||
|
||||
assert.strictEqual(views[0].getAttribute("data-tooltip"), "List");
|
||||
assert.strictEqual(views[0].getAttribute("data-hotkey"), "l");
|
||||
assert.hasClass(views[0], "active");
|
||||
assert.containsOnce(views[0], ".oi-view-list");
|
||||
assert.strictEqual(views[1].getAttribute("data-tooltip"), "Kanban");
|
||||
assert.strictEqual(views[1].getAttribute("data-hotkey"), "k");
|
||||
assert.hasClass(views[1], "oi-view-kanban");
|
||||
assert.containsOnce(views[1], ".oi-view-kanban");
|
||||
|
||||
controlPanel.env.services.action.switchView = (viewType) => {
|
||||
assert.step(viewType);
|
||||
|
|
|
|||
|
|
@ -3,8 +3,7 @@
|
|||
import { getFixture, patchWithCleanup, triggerEvent } from "@web/../tests/helpers/utils";
|
||||
import { browser } from "@web/core/browser/browser";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { ControlPanel } from "@web/search/control_panel/control_panel";
|
||||
import { FavoriteMenu } from "@web/search/favorite_menu/favorite_menu";
|
||||
import { SearchBarMenu } from "@web/search/search_bar_menu/search_bar_menu";
|
||||
import { useSetupAction } from "@web/webclient/actions/action_hook";
|
||||
import {
|
||||
editFavoriteName,
|
||||
|
|
@ -14,12 +13,13 @@ import {
|
|||
saveFavorite,
|
||||
setupControlPanelFavoriteMenuRegistry,
|
||||
setupControlPanelServiceRegistry,
|
||||
toggleFavoriteMenu,
|
||||
toggleSaveFavorite,
|
||||
toggleSearchBarMenu,
|
||||
validateSearch,
|
||||
} from "./helpers";
|
||||
|
||||
import { Component, xml } from "@odoo/owl";
|
||||
import { SearchBar } from "@web/search/search_bar/search_bar";
|
||||
const serviceRegistry = registry.category("services");
|
||||
|
||||
/**
|
||||
|
|
@ -79,7 +79,7 @@ QUnit.module("Search", (hooks) => {
|
|||
await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
Component: SearchBar,
|
||||
searchMenuTypes: ["favorite"],
|
||||
searchViewId: false,
|
||||
config: {
|
||||
|
|
@ -87,17 +87,23 @@ QUnit.module("Search", (hooks) => {
|
|||
},
|
||||
});
|
||||
|
||||
await toggleFavoriteMenu(target);
|
||||
await toggleSearchBarMenu(target);
|
||||
await toggleSaveFavorite(target);
|
||||
assert.strictEqual(
|
||||
target.querySelector('.o_add_favorite input[type="text"]').value,
|
||||
target.querySelector('.o_add_favorite + .o_accordion_values input[type="text"]').value,
|
||||
"Action Name"
|
||||
);
|
||||
assert.containsN(target, '.o_add_favorite .form-check input[type="checkbox"]', 2);
|
||||
const labelEls = target.querySelectorAll(".o_add_favorite .form-check label");
|
||||
assert.containsN(
|
||||
target,
|
||||
'.o_add_favorite + .o_accordion_values .form-check input[type="checkbox"]',
|
||||
2
|
||||
);
|
||||
const labelEls = target.querySelectorAll(
|
||||
".o_add_favorite + .o_accordion_values .form-check label"
|
||||
);
|
||||
assert.deepEqual(
|
||||
[...labelEls].map((e) => e.innerText.trim()),
|
||||
["Use by default", "Share with all users"]
|
||||
["Default filter", "Shared"]
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -107,12 +113,12 @@ QUnit.module("Search", (hooks) => {
|
|||
await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
Component: SearchBar,
|
||||
searchMenuTypes: ["favorite"],
|
||||
searchViewId: false,
|
||||
});
|
||||
|
||||
await toggleFavoriteMenu(target);
|
||||
await toggleSearchBarMenu(target);
|
||||
await toggleSaveFavorite(target);
|
||||
const checkboxes = target.querySelectorAll('input[type="checkbox"]');
|
||||
|
||||
|
|
@ -166,8 +172,8 @@ QUnit.module("Search", (hooks) => {
|
|||
});
|
||||
}
|
||||
}
|
||||
TestComponent.components = { FavoriteMenu };
|
||||
TestComponent.template = xml`<div><FavoriteMenu/></div>`;
|
||||
TestComponent.components = { SearchBarMenu };
|
||||
TestComponent.template = xml`<div><SearchBarMenu/></div>`;
|
||||
|
||||
const comp = await makeWithSearch({
|
||||
serverData,
|
||||
|
|
@ -183,11 +189,11 @@ QUnit.module("Search", (hooks) => {
|
|||
Component: TestComponent,
|
||||
searchViewId: false,
|
||||
});
|
||||
comp.env.bus.on("CLEAR-CACHES", comp, () => assert.step("CLEAR-CACHES"));
|
||||
comp.env.bus.addEventListener("CLEAR-CACHES", () => assert.step("CLEAR-CACHES"));
|
||||
|
||||
assert.verifySteps([]);
|
||||
|
||||
await toggleFavoriteMenu(target);
|
||||
await toggleSearchBarMenu(target);
|
||||
await toggleSaveFavorite(target);
|
||||
await editFavoriteName(target, "aaa");
|
||||
await saveFavorite(target);
|
||||
|
|
@ -211,8 +217,8 @@ QUnit.module("Search", (hooks) => {
|
|||
}
|
||||
},
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
searchMenuTypes: ["favorite"],
|
||||
Component: SearchBar,
|
||||
searchMenuTypes: ["filter", "favorite"],
|
||||
searchViewId: false,
|
||||
searchViewArch: `
|
||||
<search>
|
||||
|
|
@ -224,7 +230,7 @@ QUnit.module("Search", (hooks) => {
|
|||
|
||||
assert.deepEqual(getFacetTexts(target), ["Filter"]);
|
||||
|
||||
await toggleFavoriteMenu(target);
|
||||
await toggleSearchBarMenu(target);
|
||||
await toggleSaveFavorite(target);
|
||||
await editFavoriteName(target, "My favorite");
|
||||
await saveFavorite(target);
|
||||
|
|
@ -245,7 +251,7 @@ QUnit.module("Search", (hooks) => {
|
|||
}
|
||||
},
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
Component: SearchBar,
|
||||
searchMenuTypes: ["favorite"],
|
||||
searchViewId: false,
|
||||
searchViewArch: `
|
||||
|
|
@ -262,7 +268,7 @@ QUnit.module("Search", (hooks) => {
|
|||
|
||||
assert.deepEqual(getFacetTexts(target), ["Foo\na"]);
|
||||
|
||||
await toggleFavoriteMenu(target);
|
||||
await toggleSearchBarMenu(target);
|
||||
await toggleSaveFavorite(target);
|
||||
await editFavoriteName(target, "My favorite");
|
||||
await saveFavorite(target);
|
||||
|
|
@ -312,7 +318,7 @@ QUnit.module("Search", (hooks) => {
|
|||
}
|
||||
},
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
Component: SearchBar,
|
||||
searchMenuTypes: ["favorite"],
|
||||
searchViewId: false,
|
||||
irFilters: [
|
||||
|
|
@ -328,7 +334,7 @@ QUnit.module("Search", (hooks) => {
|
|||
],
|
||||
});
|
||||
|
||||
await toggleFavoriteMenu(target);
|
||||
await toggleSearchBarMenu(target);
|
||||
await toggleSaveFavorite(target);
|
||||
|
||||
// first try: should fail
|
||||
|
|
@ -377,11 +383,11 @@ QUnit.module("Search", (hooks) => {
|
|||
}
|
||||
},
|
||||
resModel: "foo",
|
||||
Component: FavoriteMenu,
|
||||
Component: SearchBarMenu,
|
||||
searchViewId: false,
|
||||
});
|
||||
|
||||
await toggleFavoriteMenu(target);
|
||||
await toggleSearchBarMenu(target);
|
||||
await toggleSaveFavorite(target);
|
||||
await saveFavorite(target);
|
||||
}
|
||||
|
|
@ -406,12 +412,8 @@ QUnit.module("Search", (hooks) => {
|
|||
await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
searchMenuTypes: ["favorite"],
|
||||
Component: SearchBarMenu,
|
||||
searchViewId: false,
|
||||
config: {
|
||||
displayName: "Action Name",
|
||||
},
|
||||
irFilters: [
|
||||
{
|
||||
context: "{}",
|
||||
|
|
@ -425,15 +427,10 @@ QUnit.module("Search", (hooks) => {
|
|||
],
|
||||
});
|
||||
|
||||
await toggleFavoriteMenu(target);
|
||||
await toggleSearchBarMenu(target);
|
||||
await toggleSaveFavorite(target);
|
||||
await editFavoriteName(target, "My favorite");
|
||||
triggerEvent(
|
||||
target,
|
||||
`.o_favorite_menu .o_add_favorite .dropdown-menu input[type="text"]`,
|
||||
"keydown",
|
||||
{ key: "Enter" }
|
||||
);
|
||||
triggerEvent(target, `.o_favorite_menu input[type="text"]`, "keydown", { key: "Enter" });
|
||||
assert.verifySteps(["warning dialog"]);
|
||||
});
|
||||
|
||||
|
|
@ -551,7 +548,7 @@ QUnit.module("Search", (hooks) => {
|
|||
// await applyFilter(".modal");
|
||||
// assert.containsNone(document.body, "tr.o_data_row", "should display 0 records");
|
||||
// // Save this search
|
||||
// await toggleFavoriteMenu(".modal");
|
||||
// await toggleSearchBarMenu(".modal");
|
||||
// await toggleSaveFavorite(".modal");
|
||||
// const filterNameInput = document.querySelector('.o_add_favorite input[type="text"]');
|
||||
// assert.isVisible(filterNameInput, "should display an input field for the filter name");
|
||||
|
|
|
|||
|
|
@ -1,863 +0,0 @@
|
|||
/** @odoo-module **/
|
||||
|
||||
import {
|
||||
click,
|
||||
getFixture,
|
||||
patchDate,
|
||||
patchTimeZone,
|
||||
patchWithCleanup,
|
||||
} from "@web/../tests/helpers/utils";
|
||||
import { localization } from "@web/core/l10n/localization";
|
||||
import { ControlPanel } from "@web/search/control_panel/control_panel";
|
||||
import { browser } from "@web/core/browser/browser";
|
||||
import {
|
||||
addCondition,
|
||||
applyFilter,
|
||||
editConditionField,
|
||||
editConditionOperator,
|
||||
editConditionValue,
|
||||
getFacetTexts,
|
||||
isItemSelected,
|
||||
makeWithSearch,
|
||||
removeFacet,
|
||||
setupControlPanelServiceRegistry,
|
||||
toggleAddCustomFilter,
|
||||
toggleFilterMenu,
|
||||
toggleMenuItem,
|
||||
} from "./helpers";
|
||||
|
||||
function getDomain(controlPanel) {
|
||||
return controlPanel.env.searchModel.domain;
|
||||
}
|
||||
|
||||
let target;
|
||||
let serverData;
|
||||
QUnit.module("Search", (hooks) => {
|
||||
hooks.beforeEach(async () => {
|
||||
serverData = {
|
||||
models: {
|
||||
foo: {
|
||||
fields: {
|
||||
date_field: {
|
||||
name: "date_field",
|
||||
string: "A date",
|
||||
type: "date",
|
||||
searchable: true,
|
||||
},
|
||||
date_time_field: {
|
||||
name: "date_time_field",
|
||||
string: "DateTime",
|
||||
type: "datetime",
|
||||
searchable: true,
|
||||
},
|
||||
boolean_field: {
|
||||
name: "boolean_field",
|
||||
string: "Boolean Field",
|
||||
type: "boolean",
|
||||
default: true,
|
||||
searchable: true,
|
||||
},
|
||||
char_field: {
|
||||
name: "char_field",
|
||||
string: "Char Field",
|
||||
type: "char",
|
||||
default: "foo",
|
||||
trim: true,
|
||||
searchable: true,
|
||||
},
|
||||
float_field: {
|
||||
name: "float_field",
|
||||
string: "Floaty McFloatface",
|
||||
type: "float",
|
||||
digits: [999, 1],
|
||||
searchable: true,
|
||||
},
|
||||
color: {
|
||||
name: "color",
|
||||
string: "Color",
|
||||
type: "selection",
|
||||
selection: [
|
||||
["black", "Black"],
|
||||
["white", "White"],
|
||||
],
|
||||
searchable: true,
|
||||
},
|
||||
image: {
|
||||
name: "image",
|
||||
string: "Binary field",
|
||||
type: "binary",
|
||||
searchable: true,
|
||||
},
|
||||
json_field: {
|
||||
name: "json_field",
|
||||
string: "Jason",
|
||||
type: "json",
|
||||
searchable: true,
|
||||
},
|
||||
},
|
||||
records: {},
|
||||
},
|
||||
},
|
||||
views: {
|
||||
"foo,false,search": `<search/>`,
|
||||
},
|
||||
};
|
||||
setupControlPanelServiceRegistry();
|
||||
patchWithCleanup(browser, {
|
||||
setTimeout: (fn) => fn(),
|
||||
clearTimeout: () => {},
|
||||
});
|
||||
target = getFixture();
|
||||
});
|
||||
|
||||
QUnit.module("CustomFilterItem");
|
||||
|
||||
QUnit.test("basic rendering", async function (assert) {
|
||||
assert.expect(14);
|
||||
|
||||
await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
searchViewId: false,
|
||||
searchMenuTypes: ["filter"],
|
||||
});
|
||||
|
||||
await toggleFilterMenu(target);
|
||||
|
||||
const customFilterItem = target.querySelector(".o_add_custom_filter_menu");
|
||||
|
||||
assert.strictEqual(customFilterItem.innerText.trim(), "Add Custom Filter");
|
||||
|
||||
await toggleAddCustomFilter(target);
|
||||
|
||||
// Single condition
|
||||
assert.containsOnce(customFilterItem, ".o_filter_condition");
|
||||
assert.containsOnce(
|
||||
customFilterItem,
|
||||
".o_filter_condition > select.o_generator_menu_field"
|
||||
);
|
||||
assert.containsOnce(
|
||||
customFilterItem,
|
||||
".o_filter_condition > select.o_generator_menu_operator"
|
||||
);
|
||||
assert.containsOnce(customFilterItem, ".o_filter_condition > span.o_generator_menu_value");
|
||||
assert.containsNone(customFilterItem, ".o_filter_condition .o_or_filter");
|
||||
assert.containsNone(customFilterItem, ".o_filter_condition .o_generator_menu_delete");
|
||||
|
||||
// no deletion allowed on single condition
|
||||
assert.containsNone(customFilterItem, ".o_filter_condition > i.o_generator_menu_delete");
|
||||
|
||||
// Buttons
|
||||
assert.containsOnce(customFilterItem, "button.o_apply_filter");
|
||||
assert.containsOnce(customFilterItem, "button.o_add_condition");
|
||||
|
||||
assert.containsOnce(customFilterItem, ".o_filter_condition");
|
||||
|
||||
await click(customFilterItem, "button.o_add_condition");
|
||||
|
||||
assert.containsN(customFilterItem, ".o_filter_condition", 2);
|
||||
assert.containsOnce(customFilterItem, ".o_filter_condition .o_or_filter");
|
||||
assert.containsN(customFilterItem, ".o_filter_condition .o_generator_menu_delete", 2);
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
'should have Date and ID field proposed in that order in "Add custom Filter" submenu',
|
||||
async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
searchViewId: false,
|
||||
searchMenuTypes: ["filter"],
|
||||
searchViewFields: {
|
||||
date_field: {
|
||||
name: "date_field",
|
||||
string: "Date",
|
||||
type: "date",
|
||||
store: true,
|
||||
sortable: true,
|
||||
searchable: true,
|
||||
},
|
||||
foo: { string: "Foo", type: "char", store: true, sortable: true },
|
||||
},
|
||||
});
|
||||
|
||||
await toggleFilterMenu(target);
|
||||
await toggleAddCustomFilter(target);
|
||||
const optionEls = target.querySelectorAll(
|
||||
".o_filter_condition > select.o_generator_menu_field option"
|
||||
);
|
||||
assert.strictEqual(optionEls[0].innerText.trim(), "Date");
|
||||
assert.strictEqual(optionEls[1].innerText.trim(), "ID");
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test("deactivate a new custom filter works", async function (assert) {
|
||||
assert.expect(4);
|
||||
|
||||
patchDate(2020, 1, 5, 12, 20, 0);
|
||||
|
||||
await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
searchViewId: false,
|
||||
searchMenuTypes: ["filter"],
|
||||
searchViewFields: {
|
||||
date_field: {
|
||||
name: "date_field",
|
||||
string: "Date",
|
||||
type: "date",
|
||||
store: true,
|
||||
sortable: true,
|
||||
searchable: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await toggleFilterMenu(target);
|
||||
await toggleAddCustomFilter(target);
|
||||
await applyFilter(target);
|
||||
|
||||
assert.ok(isItemSelected(target, 'Date is equal to "02/05/2020"'));
|
||||
assert.deepEqual(getFacetTexts(target), ['Date is equal to "02/05/2020"']);
|
||||
|
||||
await toggleMenuItem(target, 'Date is equal to "02/05/2020"');
|
||||
|
||||
assert.notOk(isItemSelected(target, 'Date is equal to "02/05/2020"'));
|
||||
assert.deepEqual(getFacetTexts(target), []);
|
||||
});
|
||||
|
||||
QUnit.test("custom OR filter presets new condition from preceding", async function (assert) {
|
||||
assert.expect(4);
|
||||
|
||||
await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
searchViewId: false,
|
||||
searchMenuTypes: ["filter"],
|
||||
});
|
||||
|
||||
await toggleFilterMenu(target);
|
||||
await toggleAddCustomFilter(target);
|
||||
|
||||
// Retrieve second selectable values for field and operator dropdowns
|
||||
const secondFieldName = target.querySelector(
|
||||
".o_generator_menu_field option:nth-of-type(2)"
|
||||
).value;
|
||||
const secondOperator = target.querySelector(
|
||||
".o_generator_menu_operator option:nth-of-type(2)"
|
||||
).value;
|
||||
|
||||
// Check if they really exist…
|
||||
assert.ok(!!secondFieldName);
|
||||
assert.ok(!!secondOperator);
|
||||
|
||||
// Add first filter condition
|
||||
await editConditionField(target, 0, secondFieldName);
|
||||
await editConditionOperator(target, 0, secondOperator);
|
||||
|
||||
// Add a second conditon on the filter being created
|
||||
await addCondition(target);
|
||||
|
||||
// Check the defaults for field and operator dropdowns
|
||||
assert.strictEqual(
|
||||
target.querySelector(".o_filter_condition:nth-of-type(2) .o_generator_menu_field")
|
||||
.value,
|
||||
secondFieldName
|
||||
);
|
||||
assert.strictEqual(
|
||||
target.querySelector(".o_filter_condition:nth-of-type(2) .o_generator_menu_operator")
|
||||
.value,
|
||||
secondOperator
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test("add a custom filter works", async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const controlPanel = await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
searchViewId: false,
|
||||
searchMenuTypes: ["filter"],
|
||||
searchViewFields: {},
|
||||
});
|
||||
|
||||
await toggleFilterMenu(target);
|
||||
await toggleAddCustomFilter(target);
|
||||
// choose ID field in 'Add Custom filter' menu and value 1
|
||||
await editConditionField(target, 0, "id");
|
||||
await editConditionValue(target, 0, 1);
|
||||
|
||||
await applyFilter(target);
|
||||
|
||||
assert.deepEqual(getFacetTexts(target), ['ID is "1"']);
|
||||
assert.deepEqual(getDomain(controlPanel), [["id", "=", 1]]);
|
||||
});
|
||||
|
||||
QUnit.test("adding a simple filter works", async function (assert) {
|
||||
assert.expect(11);
|
||||
|
||||
const controlPanel = await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
searchViewId: false,
|
||||
searchMenuTypes: ["filter"],
|
||||
searchViewFields: {
|
||||
boolean_field: {
|
||||
name: "boolean_field",
|
||||
string: "Boolean Field",
|
||||
type: "boolean",
|
||||
default: true,
|
||||
searchable: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await toggleFilterMenu(target);
|
||||
|
||||
assert.deepEqual(getFacetTexts(target), []);
|
||||
assert.deepEqual(getDomain(controlPanel), []);
|
||||
|
||||
assert.containsNone(target, ".o_menu_item");
|
||||
assert.containsOnce(target, ".o_add_custom_filter_menu button.dropdown-toggle");
|
||||
// the 'Add Custom Filter' menu should be closed;
|
||||
assert.containsNone(target, ".o_add_custom_filter_menu .dropdown-menu");
|
||||
|
||||
await toggleAddCustomFilter(target);
|
||||
// the 'Add Custom Filter' menu should be open;
|
||||
assert.containsOnce(target, ".o_add_custom_filter_menu .dropdown-menu");
|
||||
|
||||
await applyFilter(target);
|
||||
|
||||
assert.deepEqual(getFacetTexts(target), ["Boolean Field is Yes"]);
|
||||
assert.deepEqual(getDomain(controlPanel), [["boolean_field", "=", true]]);
|
||||
|
||||
assert.containsOnce(target, ".o_menu_item");
|
||||
assert.containsOnce(target, ".o_add_custom_filter_menu button.dropdown-toggle");
|
||||
// the 'Add Custom Filter' menu should still be opened;
|
||||
assert.containsOnce(target, ".o_add_custom_filter_menu .dropdown-menu");
|
||||
});
|
||||
|
||||
QUnit.test("binary field is available", async function (assert) {
|
||||
assert.expect(11);
|
||||
|
||||
const controlPanel = await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
searchViewId: false,
|
||||
searchMenuTypes: ["filter"],
|
||||
searchViewFields: {
|
||||
image: {
|
||||
name: "image",
|
||||
string: "Binary Field",
|
||||
type: "binary",
|
||||
default: true,
|
||||
searchable: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await toggleFilterMenu(target);
|
||||
|
||||
assert.deepEqual(getFacetTexts(target), []);
|
||||
assert.deepEqual(getDomain(controlPanel), []);
|
||||
|
||||
assert.containsNone(target, ".o_menu_item");
|
||||
assert.containsOnce(target, ".o_add_custom_filter_menu button.dropdown-toggle");
|
||||
// the 'Add Custom Filter' menu should be closed;
|
||||
assert.containsNone(target, ".o_add_custom_filter_menu .dropdown-menu");
|
||||
|
||||
await toggleAddCustomFilter(target);
|
||||
// the 'Add Custom Filter' menu should be open;
|
||||
assert.containsOnce(target, ".o_add_custom_filter_menu .dropdown-menu");
|
||||
|
||||
await applyFilter(target);
|
||||
|
||||
assert.deepEqual(getFacetTexts(target), ["Binary Field is set"]);
|
||||
assert.deepEqual(getDomain(controlPanel), [["image", "!=", false]]);
|
||||
|
||||
assert.containsOnce(target, ".o_menu_item");
|
||||
assert.containsOnce(target, ".o_add_custom_filter_menu button.dropdown-toggle");
|
||||
// the 'Add Custom Filter' menu should still be opened;
|
||||
assert.containsOnce(target, ".o_add_custom_filter_menu .dropdown-menu");
|
||||
});
|
||||
|
||||
QUnit.test("json field is available", async function (assert) {
|
||||
assert.expect(10);
|
||||
|
||||
const controlPanel = await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
searchViewId: false,
|
||||
searchMenuTypes: ["filter"],
|
||||
searchViewFields: {
|
||||
json_field: {
|
||||
name: "json_field",
|
||||
string: "Json",
|
||||
type: "json",
|
||||
default: "{'1': 50}",
|
||||
searchable: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await toggleFilterMenu(target);
|
||||
|
||||
assert.deepEqual(getFacetTexts(target), []);
|
||||
assert.deepEqual(getDomain(controlPanel), []);
|
||||
|
||||
assert.containsNone(target, ".o_menu_item");
|
||||
assert.containsOnce(target, ".o_add_custom_filter_menu button.dropdown-toggle");
|
||||
// the 'Add Custom Filter' menu should be closed;
|
||||
assert.containsNone(target, ".o_add_custom_filter_menu .dropdown-menu");
|
||||
|
||||
await toggleAddCustomFilter(target);
|
||||
// the 'Add Custom Filter' menu should be open;
|
||||
assert.containsOnce(target, ".o_add_custom_filter_menu .dropdown-menu");
|
||||
|
||||
await editConditionField(target, 0, "json_field");
|
||||
await applyFilter(target);
|
||||
|
||||
assert.deepEqual(getFacetTexts(target), ['Json contains ""']);
|
||||
assert.deepEqual(getDomain(controlPanel), [["json_field", "ilike", ""]]);
|
||||
|
||||
await removeFacet(target);
|
||||
await toggleFilterMenu(target);
|
||||
await toggleAddCustomFilter(target);
|
||||
await editConditionField(target, 0, "json_field");
|
||||
await editConditionOperator(target, 0, "!=");
|
||||
await applyFilter(target);
|
||||
|
||||
assert.deepEqual(getFacetTexts(target), ['Json is not equal to ""']);
|
||||
assert.deepEqual(getDomain(controlPanel), [["json_field", "!=", ""]]);
|
||||
});
|
||||
|
||||
QUnit.test("selection field: default and updated value", async function (assert) {
|
||||
assert.expect(11);
|
||||
|
||||
const controlPanel = await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
searchViewId: false,
|
||||
searchMenuTypes: ["filter"],
|
||||
});
|
||||
|
||||
// Default value
|
||||
await toggleFilterMenu(target);
|
||||
|
||||
assert.containsN(target, ".o_menu_item", 0);
|
||||
assert.containsN(target, ".dropdown-divider", 0);
|
||||
|
||||
await toggleAddCustomFilter(target);
|
||||
await editConditionField(target, 0, "color");
|
||||
await applyFilter(target);
|
||||
|
||||
assert.deepEqual(getFacetTexts(target), ['Color is "Black"']);
|
||||
assert.deepEqual(getDomain(controlPanel), [["color", "=", "black"]]);
|
||||
|
||||
assert.containsN(target, ".o_menu_item", 1);
|
||||
assert.containsN(target, ".dropdown-divider", 1);
|
||||
|
||||
// deactivate custom filter
|
||||
await removeFacet(target);
|
||||
|
||||
// Updated value
|
||||
await toggleFilterMenu(target);
|
||||
await toggleAddCustomFilter(target);
|
||||
await editConditionField(target, 0, "color");
|
||||
await editConditionValue(target, 0, "white");
|
||||
assert.strictEqual(
|
||||
target.querySelector(".o_generator_menu_value input,.o_generator_menu_value select")
|
||||
.value,
|
||||
"white"
|
||||
);
|
||||
await applyFilter(target);
|
||||
|
||||
assert.deepEqual(getFacetTexts(target), ['Color is "White"']);
|
||||
assert.deepEqual(getDomain(controlPanel), [["color", "=", "white"]]);
|
||||
|
||||
assert.containsN(target, ".o_menu_item", 2);
|
||||
assert.containsN(target, ".dropdown-divider", 2);
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
"commit search with an extended proposition with field char does not cause a crash",
|
||||
async function (assert) {
|
||||
assert.expect(12);
|
||||
|
||||
const controlPanel = await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
searchViewId: false,
|
||||
searchMenuTypes: ["filter"],
|
||||
searchViewFields: {
|
||||
many2one_field: {
|
||||
name: "many2one_field",
|
||||
string: "AAA",
|
||||
type: "many2one",
|
||||
searchable: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const steps = [
|
||||
{
|
||||
value: `a`,
|
||||
domain: [["many2one_field", "ilike", `a`]],
|
||||
facetContent: `AAA contains "a"`,
|
||||
},
|
||||
{
|
||||
value: `"a"`,
|
||||
domain: [["many2one_field", "ilike", `"a"`]],
|
||||
facetContent: `AAA contains ""a""`,
|
||||
},
|
||||
{
|
||||
value: `'a'`,
|
||||
domain: [["many2one_field", "ilike", `'a'`]],
|
||||
facetContent: `AAA contains "'a'"`,
|
||||
},
|
||||
{
|
||||
value: `'`,
|
||||
domain: [["many2one_field", "ilike", `'`]],
|
||||
facetContent: `AAA contains "'"`,
|
||||
},
|
||||
{
|
||||
value: `"`,
|
||||
domain: [["many2one_field", "ilike", `"`]],
|
||||
facetContent: `AAA contains """`,
|
||||
},
|
||||
{
|
||||
value: `\\`,
|
||||
domain: [["many2one_field", "ilike", `\\`]],
|
||||
facetContent: `AAA contains "\\"`,
|
||||
},
|
||||
];
|
||||
|
||||
for (const step of steps) {
|
||||
await toggleFilterMenu(target);
|
||||
await toggleAddCustomFilter(target);
|
||||
await editConditionValue(target, 0, step.value);
|
||||
await applyFilter(target);
|
||||
|
||||
assert.deepEqual(getFacetTexts(target), [step.facetContent]);
|
||||
assert.deepEqual(getDomain(controlPanel), step.domain);
|
||||
|
||||
await removeFacet(target);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test("custom filter date with equal operator", async function (assert) {
|
||||
patchTimeZone(-240);
|
||||
patchDate(2017, 1, 22, 12, 30, 0);
|
||||
|
||||
const controlPanel = await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
searchViewId: false,
|
||||
searchMenuTypes: ["filter"],
|
||||
});
|
||||
|
||||
await toggleFilterMenu(target);
|
||||
await toggleAddCustomFilter(target);
|
||||
|
||||
await editConditionField(target, 0, "date_field");
|
||||
await editConditionOperator(target, 0, "=");
|
||||
await editConditionValue(target, 0, "01/01/2017");
|
||||
await applyFilter(target);
|
||||
|
||||
assert.deepEqual(getFacetTexts(target), ['A date is equal to "01/01/2017"']);
|
||||
assert.deepEqual(getDomain(controlPanel), [["date_field", "=", "2017-01-01"]]);
|
||||
});
|
||||
|
||||
QUnit.test("custom filter datetime with equal operator", async function (assert) {
|
||||
assert.expect(5);
|
||||
|
||||
patchTimeZone(-240);
|
||||
patchDate(2017, 1, 22, 12, 30, 0);
|
||||
|
||||
const controlPanel = await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
searchViewId: false,
|
||||
searchMenuTypes: ["filter"],
|
||||
});
|
||||
|
||||
await toggleFilterMenu(target);
|
||||
await toggleAddCustomFilter(target);
|
||||
|
||||
await editConditionField(target, 0, "date_time_field");
|
||||
|
||||
assert.strictEqual(
|
||||
target.querySelector(".o_generator_menu_field").value,
|
||||
"date_time_field"
|
||||
);
|
||||
assert.strictEqual(target.querySelector(".o_generator_menu_operator").value, "between");
|
||||
assert.deepEqual(
|
||||
[...target.querySelectorAll(".o_generator_menu_value input[type=text]")].map(
|
||||
(v) => v.value
|
||||
),
|
||||
["02/22/2017 00:00:00", "02/22/2017 23:59:59"]
|
||||
);
|
||||
|
||||
await editConditionOperator(target, 0, "=");
|
||||
await editConditionValue(target, 0, "02/22/2017 11:00:00"); // in TZ
|
||||
await applyFilter(target);
|
||||
|
||||
assert.deepEqual(
|
||||
getFacetTexts(target),
|
||||
['DateTime is equal to "02/22/2017 11:00:00"'],
|
||||
"description should be in localized format"
|
||||
);
|
||||
assert.deepEqual(
|
||||
getDomain(controlPanel),
|
||||
[["date_time_field", "=", "2017-02-22 15:00:00"]],
|
||||
"domain should be in UTC format"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test("custom filter datetime between operator", async function (assert) {
|
||||
assert.expect(5);
|
||||
|
||||
patchTimeZone(-240);
|
||||
patchDate(2017, 1, 22, 12, 30, 0);
|
||||
|
||||
const controlPanel = await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
searchViewId: false,
|
||||
searchMenuTypes: ["filter"],
|
||||
});
|
||||
|
||||
await toggleFilterMenu(target);
|
||||
await toggleAddCustomFilter(target);
|
||||
await editConditionField(target, 0, "date_time_field");
|
||||
|
||||
assert.strictEqual(
|
||||
target.querySelector(".o_generator_menu_field").value,
|
||||
"date_time_field"
|
||||
);
|
||||
assert.strictEqual(target.querySelector(".o_generator_menu_operator").value, "between");
|
||||
assert.deepEqual(
|
||||
[...target.querySelectorAll(".o_generator_menu_value input[type=text]")].map(
|
||||
(v) => v.value
|
||||
),
|
||||
["02/22/2017 00:00:00", "02/22/2017 23:59:59"]
|
||||
);
|
||||
|
||||
await editConditionValue(target, 0, "02/22/2017 11:00:00", 0); // in TZ
|
||||
await editConditionValue(target, 0, "02-22-2017 17:00:00", 1); // in TZ
|
||||
await applyFilter(target);
|
||||
|
||||
assert.deepEqual(
|
||||
getFacetTexts(target),
|
||||
['DateTime is between "02/22/2017 11:00:00 and 02/22/2017 17:00:00"'],
|
||||
"description should be in localized format"
|
||||
);
|
||||
assert.deepEqual(
|
||||
getDomain(controlPanel),
|
||||
[
|
||||
"&",
|
||||
["date_time_field", ">=", "2017-02-22 15:00:00"],
|
||||
["date_time_field", "<=", "2017-02-22 21:00:00"],
|
||||
],
|
||||
"domain should be in UTC format"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test("input value parsing", async function (assert) {
|
||||
assert.expect(7);
|
||||
|
||||
await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
searchViewId: false,
|
||||
searchMenuTypes: ["filter"],
|
||||
});
|
||||
|
||||
await toggleFilterMenu(target);
|
||||
await toggleAddCustomFilter(target);
|
||||
await addCondition(target);
|
||||
|
||||
await editConditionField(target, 0, "float_field");
|
||||
await editConditionField(target, 1, "id");
|
||||
|
||||
const [floatInput, idInput] = target.querySelectorAll(".o_generator_menu_value .o_input");
|
||||
|
||||
// Default values
|
||||
await editConditionValue(target, 0, "0.0");
|
||||
assert.strictEqual(floatInput.value, "0.0");
|
||||
|
||||
await editConditionValue(target, 1, "0");
|
||||
assert.strictEqual(idInput.value, "0");
|
||||
|
||||
// Float parsing
|
||||
await editConditionValue(target, 0, "4.2");
|
||||
assert.strictEqual(floatInput.value, "4.2");
|
||||
|
||||
await editConditionValue(target, 0, "DefinitelyValidFloat");
|
||||
// "DefinitelyValidFloat" cannot be entered in a input type number so that the input value is reset to 0
|
||||
assert.strictEqual(floatInput.value, "4.2");
|
||||
|
||||
// Number parsing
|
||||
await editConditionValue(target, 1, "4");
|
||||
assert.strictEqual(idInput.value, "4");
|
||||
|
||||
await editConditionValue(target, 1, "4.2");
|
||||
assert.strictEqual(idInput.value, "4");
|
||||
|
||||
await editConditionValue(target, 1, "DefinitelyValidID");
|
||||
// "DefinitelyValidID" cannot be entered in a input type number so that the input value is reset to 0
|
||||
assert.strictEqual(idInput.value, "0");
|
||||
});
|
||||
|
||||
QUnit.test("input value parsing with language", async function (assert) {
|
||||
assert.expect(5);
|
||||
|
||||
await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
searchViewId: false,
|
||||
searchMenuTypes: ["filter"],
|
||||
});
|
||||
|
||||
// Needs to be done after services have been started
|
||||
patchWithCleanup(localization, {
|
||||
decimalPoint: ",",
|
||||
thousandsSep: "",
|
||||
grouping: [3, 0],
|
||||
});
|
||||
|
||||
await toggleFilterMenu(target);
|
||||
await toggleAddCustomFilter(target);
|
||||
|
||||
await editConditionField(target, 0, "float_field");
|
||||
|
||||
const [floatInput] = target.querySelectorAll(".o_generator_menu_value .o_input");
|
||||
|
||||
// Default values
|
||||
assert.strictEqual(floatInput.value, "0,0");
|
||||
|
||||
// Float parsing
|
||||
await editConditionValue(target, 0, "4,");
|
||||
assert.strictEqual(floatInput.value, "4,0");
|
||||
|
||||
await editConditionValue(target, 0, "4,2");
|
||||
assert.strictEqual(floatInput.value, "4,2");
|
||||
|
||||
await editConditionValue(target, 0, "4,2,");
|
||||
assert.strictEqual(floatInput.value, "42,0"); // because of the en localization fallback in parsers.
|
||||
|
||||
await editConditionValue(target, 0, "DefinitelyValidFloat");
|
||||
|
||||
// The input here is a string, resulting in a parsing error instead of 0
|
||||
assert.strictEqual(floatInput.value, "42,0");
|
||||
});
|
||||
|
||||
QUnit.test("add custom filter with multiple values", async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const controlPanel = await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
searchViewId: false,
|
||||
searchMenuTypes: ["filter"],
|
||||
});
|
||||
|
||||
await toggleFilterMenu(target);
|
||||
await toggleAddCustomFilter(target);
|
||||
|
||||
for (let i = 0; i < 4; i++) {
|
||||
await addCondition(target);
|
||||
}
|
||||
|
||||
await editConditionField(target, 0, "date_field");
|
||||
await editConditionValue(target, 0, "01/09/1997");
|
||||
|
||||
await editConditionField(target, 1, "boolean_field");
|
||||
await editConditionOperator(target, 1, "!=");
|
||||
|
||||
await editConditionField(target, 2, "char_field");
|
||||
await editConditionValue(target, 2, "I will be deleted anyway");
|
||||
|
||||
await editConditionField(target, 3, "float_field");
|
||||
await editConditionValue(target, 3, 7.2);
|
||||
|
||||
await editConditionField(target, 4, "id");
|
||||
await editConditionValue(target, 4, 9);
|
||||
|
||||
const thirdcondition = target.querySelectorAll(".o_filter_condition")[2];
|
||||
|
||||
await click(thirdcondition, ".o_generator_menu_delete");
|
||||
await applyFilter(target);
|
||||
|
||||
assert.deepEqual(getFacetTexts(target), [
|
||||
[
|
||||
'A date is equal to "01/09/1997"',
|
||||
"Boolean Field is No",
|
||||
'Floaty McFloatface is equal to "7.2"',
|
||||
'ID is "9"',
|
||||
].join("or"),
|
||||
]);
|
||||
assert.deepEqual(getDomain(controlPanel), [
|
||||
"|",
|
||||
["date_field", "=", "1997-01-09"],
|
||||
"|",
|
||||
["boolean_field", "!=", true],
|
||||
"|",
|
||||
["float_field", "=", 7.2],
|
||||
["id", "=", 9],
|
||||
]);
|
||||
});
|
||||
|
||||
QUnit.test("delete button is visible", async function (assert) {
|
||||
await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
searchViewId: false,
|
||||
searchMenuTypes: ["filter"],
|
||||
});
|
||||
|
||||
await toggleFilterMenu(target);
|
||||
await toggleAddCustomFilter(target);
|
||||
|
||||
assert.containsNone(
|
||||
target,
|
||||
".o_generator_menu_delete",
|
||||
"There is no delete button by default"
|
||||
);
|
||||
|
||||
await addCondition(target);
|
||||
assert.containsN(
|
||||
target,
|
||||
".o_generator_menu_delete",
|
||||
2,
|
||||
"A delete button has been added to each condition"
|
||||
);
|
||||
assert.containsN(
|
||||
target,
|
||||
"i.o_generator_menu_delete.fa-trash-o",
|
||||
2,
|
||||
"The delete button is shown as a trash icon"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,19 +1,18 @@
|
|||
/** @odoo-module **/
|
||||
|
||||
import { browser } from "@web/core/browser/browser";
|
||||
import { ControlPanel } from "@web/search/control_panel/control_panel";
|
||||
import { getFixture, patchWithCleanup } from "../helpers/utils";
|
||||
import { getFixture, getNodesTextContent, patchWithCleanup } from "../helpers/utils";
|
||||
import {
|
||||
applyGroup,
|
||||
getFacetTexts,
|
||||
isItemSelected,
|
||||
isOptionSelected,
|
||||
makeWithSearch,
|
||||
selectGroup,
|
||||
setupControlPanelServiceRegistry,
|
||||
toggleAddCustomGroup,
|
||||
toggleGroupByMenu,
|
||||
toggleMenuItem,
|
||||
toggleSearchBarMenu,
|
||||
} from "./helpers";
|
||||
import { SearchBar } from "@web/search/search_bar/search_bar";
|
||||
|
||||
let target;
|
||||
let serverData;
|
||||
|
|
@ -47,31 +46,26 @@ QUnit.module("Search", (hooks) => {
|
|||
QUnit.module("CustomGroupByItem");
|
||||
|
||||
QUnit.test("simple rendering", async function (assert) {
|
||||
assert.expect(5);
|
||||
assert.expect(2);
|
||||
|
||||
await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
Component: SearchBar,
|
||||
searchMenuTypes: ["groupBy"],
|
||||
searchViewId: false,
|
||||
});
|
||||
|
||||
await toggleGroupByMenu(target);
|
||||
|
||||
const customGroupByItem = target.querySelector(".o_add_custom_group_menu");
|
||||
assert.strictEqual(customGroupByItem.innerText.trim(), "Add Custom Group");
|
||||
|
||||
assert.containsOnce(customGroupByItem, "button.dropdown-toggle");
|
||||
assert.containsNone(customGroupByItem, ".dropdown-menu");
|
||||
|
||||
await toggleAddCustomGroup(target);
|
||||
|
||||
assert.containsOnce(customGroupByItem, ".dropdown-menu");
|
||||
await toggleSearchBarMenu(target);
|
||||
|
||||
const groupByMenu = target.querySelector(".o_group_by_menu");
|
||||
assert.strictEqual(
|
||||
groupByMenu.querySelector(".o_group_by_menu option[disabled]").innerText.trim(),
|
||||
"Add Custom Group"
|
||||
);
|
||||
assert.deepEqual(
|
||||
[...target.querySelectorAll(".o_add_custom_group_menu select option")].map(
|
||||
(el) => el.innerText
|
||||
getNodesTextContent(
|
||||
target.querySelectorAll(".o_add_custom_group_menu option:not([disabled])")
|
||||
),
|
||||
["Birthday", "Date", "Foo"]
|
||||
);
|
||||
|
|
@ -85,7 +79,7 @@ QUnit.module("Search", (hooks) => {
|
|||
await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
Component: SearchBar,
|
||||
searchMenuTypes: ["groupBy"],
|
||||
searchViewId: false,
|
||||
searchViewFields: {
|
||||
|
|
@ -94,17 +88,11 @@ QUnit.module("Search", (hooks) => {
|
|||
},
|
||||
});
|
||||
|
||||
await toggleGroupByMenu(target);
|
||||
await toggleAddCustomGroup(target);
|
||||
|
||||
assert.deepEqual(
|
||||
[
|
||||
...target.querySelectorAll(
|
||||
".o_add_custom_group_menu .dropdown-menu select option"
|
||||
),
|
||||
].map((el) => el.innerText),
|
||||
["Foo"]
|
||||
);
|
||||
await toggleSearchBarMenu(target);
|
||||
const optionDescriptions = [
|
||||
...target.querySelectorAll(".o_add_custom_group_menu option:not([disabled])"),
|
||||
].map((option) => option.innerText.trim());
|
||||
assert.deepEqual(optionDescriptions, ["Foo"]);
|
||||
}
|
||||
);
|
||||
|
||||
|
|
@ -116,7 +104,7 @@ QUnit.module("Search", (hooks) => {
|
|||
await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
Component: SearchBar,
|
||||
searchMenuTypes: ["groupBy"],
|
||||
searchViewId: false,
|
||||
searchViewFields: {
|
||||
|
|
@ -130,17 +118,11 @@ QUnit.module("Search", (hooks) => {
|
|||
},
|
||||
});
|
||||
|
||||
await toggleGroupByMenu(target);
|
||||
await toggleAddCustomGroup(target);
|
||||
|
||||
assert.deepEqual(
|
||||
[
|
||||
...target.querySelectorAll(
|
||||
".o_add_custom_group_menu .dropdown-menu select option"
|
||||
),
|
||||
].map((el) => el.innerText),
|
||||
["Char A", "M2M Stored"]
|
||||
);
|
||||
await toggleSearchBarMenu(target);
|
||||
const optionDescriptions = [
|
||||
...target.querySelectorAll(".o_add_custom_group_menu option:not([disabled])"),
|
||||
].map((option) => option.innerText.trim());
|
||||
assert.deepEqual(optionDescriptions, ["Char A", "M2M Stored"]);
|
||||
}
|
||||
);
|
||||
|
||||
|
|
@ -152,7 +134,7 @@ QUnit.module("Search", (hooks) => {
|
|||
const controlPanel = await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
Component: SearchBar,
|
||||
searchMenuTypes: ["groupBy"],
|
||||
searchViewId: false,
|
||||
searchViewFields: {
|
||||
|
|
@ -160,13 +142,12 @@ QUnit.module("Search", (hooks) => {
|
|||
id: { sortable: true, string: "ID", type: "integer" },
|
||||
},
|
||||
});
|
||||
await toggleGroupByMenu(target);
|
||||
await toggleSearchBarMenu(target);
|
||||
|
||||
assert.deepEqual(controlPanel.env.searchModel.groupBy, []);
|
||||
assert.containsNone(target, ".o_menu_item");
|
||||
assert.containsOnce(target, ".o_add_custom_group_menu"); //Add Custom Group
|
||||
|
||||
await toggleAddCustomGroup(target);
|
||||
await applyGroup(target);
|
||||
await selectGroup(target, "date_field");
|
||||
|
||||
assert.deepEqual(controlPanel.env.searchModel.groupBy, ["date_field:month"]);
|
||||
assert.deepEqual(getFacetTexts(target), ["Date: Month"]);
|
||||
|
|
@ -179,48 +160,44 @@ QUnit.module("Search", (hooks) => {
|
|||
);
|
||||
|
||||
QUnit.test("click on add custom group toggle group selector", async function (assert) {
|
||||
assert.expect(4);
|
||||
assert.expect(3);
|
||||
|
||||
await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
Component: SearchBar,
|
||||
searchMenuTypes: ["groupBy"],
|
||||
searchViewFields: {
|
||||
date: { sortable: true, name: "date", string: "Super Date", type: "date" },
|
||||
},
|
||||
});
|
||||
|
||||
await toggleGroupByMenu(target);
|
||||
await toggleSearchBarMenu(target);
|
||||
|
||||
const addCustomGroupMenu = target.querySelector(".o_add_custom_group_menu");
|
||||
|
||||
assert.strictEqual(addCustomGroupMenu.innerText.trim(), "Add Custom Group");
|
||||
|
||||
await toggleAddCustomGroup(target);
|
||||
|
||||
// Single select node with a single option
|
||||
assert.containsOnce(target, ".o_add_custom_group_menu .dropdown-menu select");
|
||||
assert.strictEqual(
|
||||
target
|
||||
.querySelector(".o_add_custom_group_menu .dropdown-menu select option")
|
||||
.innerText.trim(),
|
||||
"Super Date"
|
||||
addCustomGroupMenu.querySelector("option[disabled]").innerText.trim(),
|
||||
"Add Custom Group"
|
||||
);
|
||||
|
||||
// Button apply
|
||||
assert.containsOnce(target, ".o_add_custom_group_menu .dropdown-menu .btn");
|
||||
// Single select node with a single option
|
||||
assert.containsOnce(target, ".o_add_custom_group_menu option:not([disabled])");
|
||||
assert.deepEqual(
|
||||
target.querySelector(".o_add_custom_group_menu option:not([disabled])").textContent,
|
||||
"Super Date"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
"select a field name in Add Custom Group menu properly trigger the corresponding field",
|
||||
async function (assert) {
|
||||
assert.expect(4);
|
||||
assert.expect(3);
|
||||
|
||||
await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
Component: SearchBar,
|
||||
searchMenuTypes: ["groupBy"],
|
||||
searchViewFields: {
|
||||
candle_light: {
|
||||
|
|
@ -231,13 +208,11 @@ QUnit.module("Search", (hooks) => {
|
|||
},
|
||||
});
|
||||
|
||||
await toggleGroupByMenu(target);
|
||||
await toggleAddCustomGroup(target);
|
||||
await applyGroup(target);
|
||||
await toggleSearchBarMenu(target);
|
||||
await selectGroup(target, "candle_light");
|
||||
|
||||
assert.containsOnce(target, ".o_group_by_menu .o_menu_item");
|
||||
assert.containsOnce(target, ".o_add_custom_group_menu .dropdown-toggle");
|
||||
assert.containsOnce(target, ".o_add_custom_group_menu .dropdown-menu");
|
||||
assert.containsN(target, ".o_group_by_menu .o_menu_item", 2);
|
||||
assert.containsOnce(target, ".o_add_custom_group_menu");
|
||||
assert.deepEqual(getFacetTexts(target), ["Candlelight"]);
|
||||
}
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,391 +0,0 @@
|
|||
/** @odoo-module **/
|
||||
|
||||
import { click, getFixture, patchDate } from "@web/../tests/helpers/utils";
|
||||
import { createWebClient, doAction } from "@web/../tests/webclient/helpers";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { ControlPanel } from "@web/search/control_panel/control_panel";
|
||||
import { FavoriteMenu } from "@web/search/favorite_menu/favorite_menu";
|
||||
import { SearchBar } from "@web/search/search_bar/search_bar";
|
||||
import {
|
||||
deleteFavorite,
|
||||
getFacetTexts,
|
||||
isItemSelected,
|
||||
makeWithSearch,
|
||||
setupControlPanelFavoriteMenuRegistry,
|
||||
setupControlPanelServiceRegistry,
|
||||
toggleComparisonMenu,
|
||||
toggleFavoriteMenu,
|
||||
toggleMenuItem,
|
||||
} from "@web/../tests/search/helpers";
|
||||
|
||||
import { Component, onWillUpdateProps, xml } from "@odoo/owl";
|
||||
const viewRegistry = registry.category("views");
|
||||
const favoriteMenuRegistry = registry.category("favoriteMenu");
|
||||
|
||||
function getDomain(comp) {
|
||||
return comp.env.searchModel.domain;
|
||||
}
|
||||
|
||||
let serverData;
|
||||
let target;
|
||||
QUnit.module("Search", (hooks) => {
|
||||
hooks.beforeEach(async () => {
|
||||
target = getFixture();
|
||||
serverData = {
|
||||
models: {
|
||||
foo: {
|
||||
fields: {
|
||||
bar: { string: "Bar", type: "many2one", relation: "partner" },
|
||||
birthday: { string: "Birthday", type: "date", store: true, sortable: true },
|
||||
date_field: { string: "Date", type: "date", store: true, sortable: true },
|
||||
float_field: { string: "Float", type: "float", group_operator: "sum" },
|
||||
foo: { string: "Foo", type: "char", store: true, sortable: true },
|
||||
},
|
||||
records: {},
|
||||
},
|
||||
},
|
||||
views: {
|
||||
"foo,false,search": `<search/>`,
|
||||
},
|
||||
};
|
||||
setupControlPanelFavoriteMenuRegistry();
|
||||
setupControlPanelServiceRegistry();
|
||||
});
|
||||
|
||||
QUnit.module("FavoriteMenu");
|
||||
|
||||
QUnit.test(
|
||||
"simple rendering with no favorite (without ability to save)",
|
||||
async function (assert) {
|
||||
assert.expect(4);
|
||||
|
||||
favoriteMenuRegistry.remove("custom-favorite-item");
|
||||
|
||||
await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
searchMenuTypes: ["favorite"],
|
||||
searchViewId: false,
|
||||
config: {
|
||||
getDisplayName: () => "Action Name",
|
||||
},
|
||||
});
|
||||
|
||||
assert.containsOnce(target, "div.o_favorite_menu > button i.fa.fa-star");
|
||||
assert.strictEqual(
|
||||
target
|
||||
.querySelector("div.o_favorite_menu > button span")
|
||||
.innerText.trim()
|
||||
.toUpperCase(),
|
||||
"FAVORITES"
|
||||
);
|
||||
|
||||
await toggleFavoriteMenu(target);
|
||||
assert.containsOnce(
|
||||
target,
|
||||
"div.o_favorite_menu > .dropdown-menu",
|
||||
"the menu should be opened"
|
||||
);
|
||||
assert.containsNone(target, ".dropdown-menu *", "the menu should be empty");
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test("simple rendering with no favorite", async function (assert) {
|
||||
assert.expect(5);
|
||||
|
||||
await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
searchMenuTypes: ["favorite"],
|
||||
searchViewId: false,
|
||||
config: {
|
||||
getDisplayName: () => "Action Name",
|
||||
},
|
||||
});
|
||||
|
||||
assert.containsOnce(target, "div.o_favorite_menu > button i.fa.fa-star");
|
||||
assert.strictEqual(
|
||||
target
|
||||
.querySelector("div.o_favorite_menu > button span")
|
||||
.innerText.trim()
|
||||
.toUpperCase(),
|
||||
"FAVORITES"
|
||||
);
|
||||
|
||||
await toggleFavoriteMenu(target);
|
||||
assert.containsOnce(
|
||||
target,
|
||||
"div.o_favorite_menu > .dropdown-menu",
|
||||
"the menu should be opened"
|
||||
);
|
||||
assert.containsNone(target, ".dropdown-menu .dropdown-divider");
|
||||
assert.containsOnce(target, ".dropdown-menu .o_add_favorite");
|
||||
});
|
||||
|
||||
QUnit.test("delete an active favorite", async function (assert) {
|
||||
assert.expect(14);
|
||||
|
||||
class ToyController extends Component {
|
||||
setup() {
|
||||
assert.deepEqual(this.props.domain, [["foo", "=", "qsdf"]]);
|
||||
onWillUpdateProps((nextProps) => {
|
||||
assert.deepEqual(nextProps.domain, []);
|
||||
});
|
||||
}
|
||||
}
|
||||
ToyController.components = { FavoriteMenu, SearchBar };
|
||||
ToyController.template = xml`
|
||||
<div>
|
||||
<SearchBar/>
|
||||
<FavoriteMenu/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
viewRegistry.add("toy", {
|
||||
type: "toy",
|
||||
display_name: "Toy",
|
||||
Controller: ToyController,
|
||||
});
|
||||
|
||||
serverData.views["foo,false,toy"] = `<toy />`;
|
||||
serverData.models.foo.filters = [
|
||||
{
|
||||
context: "{}",
|
||||
domain: "[['foo', '=', 'qsdf']]",
|
||||
id: 7,
|
||||
is_default: true,
|
||||
name: "My favorite",
|
||||
sort: "[]",
|
||||
user_id: [2, "Mitchell Admin"],
|
||||
},
|
||||
];
|
||||
|
||||
const webClient = await createWebClient({
|
||||
serverData,
|
||||
mockRPC: async (_, args) => {
|
||||
if (args.model === "ir.filters" && args.method === "unlink") {
|
||||
assert.step("deleteFavorite");
|
||||
return { result: true }; // mocked unlink result
|
||||
}
|
||||
},
|
||||
});
|
||||
webClient.env.bus.on("CLEAR-CACHES", webClient, () => assert.step("CLEAR-CACHES"));
|
||||
await doAction(webClient, {
|
||||
name: "Action",
|
||||
res_model: "foo",
|
||||
type: "ir.actions.act_window",
|
||||
views: [[false, "toy"]],
|
||||
});
|
||||
|
||||
await toggleFavoriteMenu(target);
|
||||
const favorite = target.querySelector(".o_favorite_menu .dropdown-item");
|
||||
assert.equal(favorite.innerText, "My favorite");
|
||||
assert.deepEqual(favorite.getAttribute("role"), "menuitemcheckbox");
|
||||
assert.deepEqual(favorite.ariaChecked, "true");
|
||||
|
||||
assert.deepEqual(getFacetTexts(target), ["My favorite"]);
|
||||
assert.hasClass(target.querySelector(".o_favorite_menu .o_menu_item"), "selected");
|
||||
|
||||
await deleteFavorite(target, 0);
|
||||
|
||||
assert.verifySteps([]);
|
||||
|
||||
await click(document.querySelector("div.o_dialog footer button"));
|
||||
|
||||
assert.deepEqual(getFacetTexts(target), []);
|
||||
assert.containsNone(target, ".o_favorite_menu .o_menu_item");
|
||||
assert.containsOnce(target, ".o_favorite_menu .o_add_favorite");
|
||||
assert.verifySteps(["deleteFavorite", "CLEAR-CACHES"]);
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
"default favorite is not activated if activateFavorite is set to false",
|
||||
async function (assert) {
|
||||
assert.expect(3);
|
||||
|
||||
const controlPanel = await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
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 toggleFavoriteMenu(target);
|
||||
|
||||
assert.notOk(isItemSelected(target, "My favorite"));
|
||||
assert.deepEqual(getDomain(controlPanel), []);
|
||||
assert.deepEqual(getFacetTexts(target), []);
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test(
|
||||
'toggle favorite correctly clears filter, groupbys, comparison and field "options"',
|
||||
async function (assert) {
|
||||
patchDate(2019, 6, 31, 13, 43, 0);
|
||||
|
||||
const controlPanel = await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
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="this_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",
|
||||
},
|
||||
});
|
||||
|
||||
let domain = controlPanel.env.searchModel.domain;
|
||||
let groupBy = controlPanel.env.searchModel.groupBy;
|
||||
let comparison = controlPanel.env.searchModel.getFullComparison();
|
||||
|
||||
assert.deepEqual(domain, [
|
||||
"&",
|
||||
["foo", "ilike", "a"],
|
||||
"&",
|
||||
["date_field", ">=", "2019-01-01"],
|
||||
["date_field", "<=", "2019-12-31"],
|
||||
]);
|
||||
assert.deepEqual(groupBy, ["date_field:month"]);
|
||||
assert.deepEqual(comparison, null);
|
||||
|
||||
assert.deepEqual(getFacetTexts(target), [
|
||||
"Foo\na",
|
||||
"Date Field Filter: 2019",
|
||||
"Date Field Groupby: Month",
|
||||
]);
|
||||
|
||||
// activate a comparison
|
||||
await toggleComparisonMenu(target);
|
||||
await toggleMenuItem(target, "Date Field Filter: Previous Period");
|
||||
|
||||
domain = controlPanel.env.searchModel.domain;
|
||||
groupBy = controlPanel.env.searchModel.groupBy;
|
||||
comparison = controlPanel.env.searchModel.getFullComparison();
|
||||
|
||||
assert.deepEqual(domain, [["foo", "ilike", "a"]]);
|
||||
assert.deepEqual(groupBy, ["date_field:month"]);
|
||||
assert.deepEqual(comparison, {
|
||||
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
|
||||
await toggleFavoriteMenu(target);
|
||||
const favorite = target.querySelector(".o_favorite_menu .dropdown-item");
|
||||
assert.equal(favorite.innerText, "My favorite");
|
||||
assert.deepEqual(favorite.getAttribute("role"), "menuitemcheckbox");
|
||||
assert.deepEqual(favorite.ariaChecked, "false");
|
||||
await toggleMenuItem(target, 0);
|
||||
assert.deepEqual(favorite.ariaChecked, "true");
|
||||
|
||||
domain = controlPanel.env.searchModel.domain;
|
||||
groupBy = controlPanel.env.searchModel.groupBy;
|
||||
comparison = controlPanel.env.searchModel.getFullComparison();
|
||||
|
||||
assert.deepEqual(domain, ["!", ["foo", "=", "qsdf"]]);
|
||||
assert.deepEqual(groupBy, ["foo"]);
|
||||
assert.deepEqual(comparison, {
|
||||
"favorite comparison content": "bla bla...",
|
||||
});
|
||||
|
||||
assert.deepEqual(getFacetTexts(target), ["My favorite"]);
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.skip("modal loads saved search filters", async function (assert) {
|
||||
/** @todo I don't know yet how to convert this test */
|
||||
// assert.expect(1);
|
||||
// const data = {
|
||||
// partner: {
|
||||
// fields: {
|
||||
// bar: { string: "Bar", type: "many2one", relation: "partner" },
|
||||
// },
|
||||
// // 10 records so that the Search button shows
|
||||
// records: Array.apply(null, Array(10)).map(function (_, i) {
|
||||
// return { id: i, display_name: "Record " + i, bar: 1 };
|
||||
// }),
|
||||
// },
|
||||
// };
|
||||
// const form = await createView({
|
||||
// arch: `
|
||||
// <form string="Partners">
|
||||
// <sheet>
|
||||
// <group>
|
||||
// <field name="bar"/>
|
||||
// </group>
|
||||
// </sheet>
|
||||
// </form>`,
|
||||
// data,
|
||||
// model: "partner",
|
||||
// res_id: 1,
|
||||
// View: FormView,
|
||||
// interceptsPropagate: {
|
||||
// load_views: function (ev) {
|
||||
// assert.ok(
|
||||
// ev.data.options.load_filters,
|
||||
// "opening dialog should load the filters"
|
||||
// );
|
||||
// },
|
||||
// },
|
||||
// });
|
||||
// await testUtils.form.clickEdit(form);
|
||||
// await testUtils.fields.many2one.clickOpenDropdown("bar");
|
||||
// await testUtils.fields.many2one.clickItem("bar", "Search");
|
||||
// form.destroy();
|
||||
});
|
||||
});
|
||||
|
|
@ -1,587 +0,0 @@
|
|||
/** @odoo-module **/
|
||||
|
||||
import { getFixture, patchDate, patchWithCleanup } from "@web/../tests/helpers/utils";
|
||||
import { browser } from "@web/core/browser/browser";
|
||||
import { ControlPanel } from "@web/search/control_panel/control_panel";
|
||||
import {
|
||||
getFacetTexts,
|
||||
isItemSelected,
|
||||
isOptionSelected,
|
||||
makeWithSearch,
|
||||
setupControlPanelServiceRegistry,
|
||||
toggleFilterMenu,
|
||||
toggleMenuItem,
|
||||
toggleMenuItemOption,
|
||||
} from "./helpers";
|
||||
|
||||
function getDomain(controlPanel) {
|
||||
return controlPanel.env.searchModel.domain;
|
||||
}
|
||||
|
||||
function getContext(controlPanel) {
|
||||
return controlPanel.env.searchModel.context;
|
||||
}
|
||||
|
||||
let target;
|
||||
let serverData;
|
||||
QUnit.module("Search", (hooks) => {
|
||||
hooks.beforeEach(async () => {
|
||||
serverData = {
|
||||
models: {
|
||||
foo: {
|
||||
fields: {
|
||||
date_field: {
|
||||
string: "Date",
|
||||
type: "date",
|
||||
store: true,
|
||||
sortable: true,
|
||||
searchable: true,
|
||||
},
|
||||
foo: { string: "Foo", type: "char", store: true, sortable: true },
|
||||
},
|
||||
records: {},
|
||||
},
|
||||
},
|
||||
views: {
|
||||
"foo,false,search": `<search/>`,
|
||||
},
|
||||
};
|
||||
setupControlPanelServiceRegistry();
|
||||
patchWithCleanup(browser, {
|
||||
setTimeout: (fn) => fn(),
|
||||
clearTimeout: () => {},
|
||||
});
|
||||
target = getFixture();
|
||||
});
|
||||
|
||||
QUnit.module("FilterMenu");
|
||||
|
||||
QUnit.test("simple rendering with no filter", async function (assert) {
|
||||
assert.expect(3);
|
||||
|
||||
await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
searchMenuTypes: ["filter"],
|
||||
});
|
||||
|
||||
await toggleFilterMenu(target);
|
||||
assert.containsNone(target, ".o_menu_item");
|
||||
assert.containsNone(target, ".dropdown-divider");
|
||||
assert.containsOnce(target, ".o_add_custom_filter_menu");
|
||||
});
|
||||
|
||||
QUnit.test("simple rendering with a single filter", async function (assert) {
|
||||
await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
searchViewId: false,
|
||||
searchMenuTypes: ["filter"],
|
||||
searchViewArch: `
|
||||
<search>
|
||||
<filter string="Foo" name="foo" domain="[]"/>
|
||||
</search>
|
||||
`,
|
||||
});
|
||||
|
||||
await toggleFilterMenu(target);
|
||||
assert.containsOnce(target, ".o_menu_item");
|
||||
assert.containsOnce(target, ".o_menu_item[role=menuitemcheckbox]");
|
||||
assert.deepEqual(target.querySelector(".o_menu_item").ariaChecked, "false");
|
||||
assert.containsOnce(target, ".dropdown-divider");
|
||||
assert.containsOnce(target, ".o_add_custom_filter_menu");
|
||||
});
|
||||
|
||||
QUnit.test('toggle a "simple" filter in filter menu works', async function (assert) {
|
||||
const controlPanel = await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
searchViewId: false,
|
||||
searchMenuTypes: ["filter"],
|
||||
searchViewArch: `
|
||||
<search>
|
||||
<filter string="Foo" name="foo" domain="[('foo', '=', 'qsdf')]"/>
|
||||
</search>
|
||||
`,
|
||||
});
|
||||
|
||||
await toggleFilterMenu(target);
|
||||
assert.deepEqual(getFacetTexts(target), []);
|
||||
assert.notOk(isItemSelected(target, "Foo"));
|
||||
assert.deepEqual(getDomain(controlPanel), []);
|
||||
assert.containsOnce(target, ".o_menu_item[role=menuitemcheckbox]");
|
||||
assert.deepEqual(target.querySelector(".o_menu_item").ariaChecked, "false");
|
||||
|
||||
await toggleMenuItem(target, "Foo");
|
||||
assert.deepEqual(target.querySelector(".o_menu_item").ariaChecked, "true");
|
||||
|
||||
assert.deepEqual(getFacetTexts(target), ["Foo"]);
|
||||
assert.containsOnce(
|
||||
target.querySelector(".o_searchview .o_searchview_facet"),
|
||||
"span.fa.fa-filter.o_searchview_facet_label"
|
||||
);
|
||||
assert.ok(isItemSelected(target, "Foo"));
|
||||
assert.deepEqual(getDomain(controlPanel), [["foo", "=", "qsdf"]]);
|
||||
|
||||
await toggleMenuItem(target, "Foo");
|
||||
|
||||
assert.deepEqual(getFacetTexts(target), []);
|
||||
assert.notOk(isItemSelected(target, "Foo"));
|
||||
assert.deepEqual(getDomain(controlPanel), []);
|
||||
});
|
||||
|
||||
QUnit.test("filter by a date field using period works", async function (assert) {
|
||||
assert.expect(57);
|
||||
|
||||
patchDate(2017, 2, 22, 1, 0, 0);
|
||||
|
||||
const controlPanel = await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
searchViewId: false,
|
||||
searchMenuTypes: ["filter"],
|
||||
searchViewArch: `
|
||||
<search>
|
||||
<filter string="Date" name="date_field" date="date_field"/>
|
||||
</search>
|
||||
`,
|
||||
context: { search_default_date_field: 1 },
|
||||
});
|
||||
|
||||
await toggleFilterMenu(target);
|
||||
await toggleMenuItem(target, "Date");
|
||||
|
||||
const optionEls = target.querySelectorAll(".dropdown .o_item_option");
|
||||
|
||||
// default filter should be activated with the global default period 'this_month'
|
||||
assert.deepEqual(getDomain(controlPanel), [
|
||||
"&",
|
||||
["date_field", ">=", "2017-03-01"],
|
||||
["date_field", "<=", "2017-03-31"],
|
||||
]);
|
||||
assert.ok(isItemSelected(target, "Date"));
|
||||
assert.ok(isOptionSelected(target, "Date", "March"));
|
||||
|
||||
// check option descriptions
|
||||
const optionDescriptions = [...optionEls].map((e) => e.innerText.trim());
|
||||
const expectedDescriptions = [
|
||||
"March",
|
||||
"February",
|
||||
"January",
|
||||
"Q4",
|
||||
"Q3",
|
||||
"Q2",
|
||||
"Q1",
|
||||
"2017",
|
||||
"2016",
|
||||
"2015",
|
||||
];
|
||||
assert.deepEqual(optionDescriptions, expectedDescriptions);
|
||||
|
||||
// check generated domains
|
||||
const steps = [
|
||||
{
|
||||
toggledOption: "March",
|
||||
resultingFacet: "Date: 2017",
|
||||
selectedoptions: ["2017"],
|
||||
domain: [
|
||||
"&",
|
||||
["date_field", ">=", "2017-01-01"],
|
||||
["date_field", "<=", "2017-12-31"],
|
||||
],
|
||||
},
|
||||
{
|
||||
toggledOption: "February",
|
||||
resultingFacet: "Date: February 2017",
|
||||
selectedoptions: ["February", "2017"],
|
||||
domain: [
|
||||
"&",
|
||||
["date_field", ">=", "2017-02-01"],
|
||||
["date_field", "<=", "2017-02-28"],
|
||||
],
|
||||
},
|
||||
{
|
||||
toggledOption: "February",
|
||||
resultingFacet: "Date: 2017",
|
||||
selectedoptions: ["2017"],
|
||||
domain: [
|
||||
"&",
|
||||
["date_field", ">=", "2017-01-01"],
|
||||
["date_field", "<=", "2017-12-31"],
|
||||
],
|
||||
},
|
||||
{
|
||||
toggledOption: "January",
|
||||
resultingFacet: "Date: January 2017",
|
||||
selectedoptions: ["January", "2017"],
|
||||
domain: [
|
||||
"&",
|
||||
["date_field", ">=", "2017-01-01"],
|
||||
["date_field", "<=", "2017-01-31"],
|
||||
],
|
||||
},
|
||||
{
|
||||
toggledOption: "Q4",
|
||||
resultingFacet: "Date: January 2017/Q4 2017",
|
||||
selectedoptions: ["January", "Q4", "2017"],
|
||||
domain: [
|
||||
"|",
|
||||
"&",
|
||||
["date_field", ">=", "2017-01-01"],
|
||||
["date_field", "<=", "2017-01-31"],
|
||||
"&",
|
||||
["date_field", ">=", "2017-10-01"],
|
||||
["date_field", "<=", "2017-12-31"],
|
||||
],
|
||||
},
|
||||
{
|
||||
toggledOption: "January",
|
||||
resultingFacet: "Date: Q4 2017",
|
||||
selectedoptions: ["Q4", "2017"],
|
||||
domain: [
|
||||
"&",
|
||||
["date_field", ">=", "2017-10-01"],
|
||||
["date_field", "<=", "2017-12-31"],
|
||||
],
|
||||
},
|
||||
{
|
||||
toggledOption: "Q4",
|
||||
resultingFacet: "Date: 2017",
|
||||
selectedoptions: ["2017"],
|
||||
domain: [
|
||||
"&",
|
||||
["date_field", ">=", "2017-01-01"],
|
||||
["date_field", "<=", "2017-12-31"],
|
||||
],
|
||||
},
|
||||
{
|
||||
toggledOption: "Q1",
|
||||
resultingFacet: "Date: Q1 2017",
|
||||
selectedoptions: ["Q1", "2017"],
|
||||
domain: [
|
||||
"&",
|
||||
["date_field", ">=", "2017-01-01"],
|
||||
["date_field", "<=", "2017-03-31"],
|
||||
],
|
||||
},
|
||||
{
|
||||
toggledOption: "Q1",
|
||||
resultingFacet: "Date: 2017",
|
||||
selectedoptions: ["2017"],
|
||||
domain: [
|
||||
"&",
|
||||
["date_field", ">=", "2017-01-01"],
|
||||
["date_field", "<=", "2017-12-31"],
|
||||
],
|
||||
},
|
||||
{
|
||||
toggledOption: "2017",
|
||||
selectedoptions: [],
|
||||
domain: [],
|
||||
},
|
||||
{
|
||||
toggledOption: "2017",
|
||||
resultingFacet: "Date: 2017",
|
||||
selectedoptions: ["2017"],
|
||||
domain: [
|
||||
"&",
|
||||
["date_field", ">=", "2017-01-01"],
|
||||
["date_field", "<=", "2017-12-31"],
|
||||
],
|
||||
},
|
||||
{
|
||||
toggledOption: "2016",
|
||||
resultingFacet: "Date: 2016/2017",
|
||||
selectedoptions: ["2017", "2016"],
|
||||
domain: [
|
||||
"|",
|
||||
"&",
|
||||
["date_field", ">=", "2016-01-01"],
|
||||
["date_field", "<=", "2016-12-31"],
|
||||
"&",
|
||||
["date_field", ">=", "2017-01-01"],
|
||||
["date_field", "<=", "2017-12-31"],
|
||||
],
|
||||
},
|
||||
{
|
||||
toggledOption: "2015",
|
||||
resultingFacet: "Date: 2015/2016/2017",
|
||||
selectedoptions: ["2017", "2016", "2015"],
|
||||
domain: [
|
||||
"|",
|
||||
"&",
|
||||
["date_field", ">=", "2015-01-01"],
|
||||
["date_field", "<=", "2015-12-31"],
|
||||
"|",
|
||||
"&",
|
||||
["date_field", ">=", "2016-01-01"],
|
||||
["date_field", "<=", "2016-12-31"],
|
||||
"&",
|
||||
["date_field", ">=", "2017-01-01"],
|
||||
["date_field", "<=", "2017-12-31"],
|
||||
],
|
||||
},
|
||||
{
|
||||
toggledOption: "March",
|
||||
resultingFacet: "Date: March 2015/March 2016/March 2017",
|
||||
selectedoptions: ["March", "2017", "2016", "2015"],
|
||||
domain: [
|
||||
"|",
|
||||
"&",
|
||||
["date_field", ">=", "2015-03-01"],
|
||||
["date_field", "<=", "2015-03-31"],
|
||||
"|",
|
||||
"&",
|
||||
["date_field", ">=", "2016-03-01"],
|
||||
["date_field", "<=", "2016-03-31"],
|
||||
"&",
|
||||
["date_field", ">=", "2017-03-01"],
|
||||
["date_field", "<=", "2017-03-31"],
|
||||
],
|
||||
},
|
||||
];
|
||||
for (const s of steps) {
|
||||
await toggleMenuItemOption(target, "Date", s.toggledOption);
|
||||
assert.deepEqual(getDomain(controlPanel), s.domain);
|
||||
if (s.resultingFacet) {
|
||||
assert.deepEqual(getFacetTexts(target), [s.resultingFacet]);
|
||||
} else {
|
||||
assert.deepEqual(getFacetTexts(target), []);
|
||||
}
|
||||
s.selectedoptions.forEach((option) => {
|
||||
assert.ok(
|
||||
isOptionSelected(target, "Date", option),
|
||||
`at step ${steps.indexOf(s) + 1}, ${option} should be selected`
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
"filter by a date field using period works even in January",
|
||||
async function (assert) {
|
||||
assert.expect(5);
|
||||
|
||||
patchDate(2017, 0, 7, 3, 0, 0);
|
||||
|
||||
const controlPanel = await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
searchViewId: false,
|
||||
searchMenuTypes: ["filter"],
|
||||
searchViewArch: `
|
||||
<search>
|
||||
<filter string="Date" name="some_filter" date="date_field" default_period="last_month"/>
|
||||
</search>
|
||||
`,
|
||||
context: { search_default_some_filter: 1 },
|
||||
});
|
||||
|
||||
assert.deepEqual(getDomain(controlPanel), [
|
||||
"&",
|
||||
["date_field", ">=", "2016-12-01"],
|
||||
["date_field", "<=", "2016-12-31"],
|
||||
]);
|
||||
|
||||
assert.deepEqual(getFacetTexts(target), ["Date: December 2016"]);
|
||||
|
||||
await toggleFilterMenu(target);
|
||||
await toggleMenuItem(target, "Date");
|
||||
|
||||
assert.ok(isItemSelected(target, "Date"));
|
||||
assert.ok(isOptionSelected(target, "Date", "December"));
|
||||
assert.ok(isOptionSelected(target, "Date", "2016"));
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test("`context` key in <filter> is used", async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
const controlPanel = await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
searchViewId: false,
|
||||
searchMenuTypes: ["filter"],
|
||||
searchViewArch: `
|
||||
<search>
|
||||
<filter string="Filter" name="some_filter" domain="[]" context="{'coucou_1': 1}"/>
|
||||
</search>
|
||||
`,
|
||||
context: { search_default_some_filter: 1 },
|
||||
});
|
||||
|
||||
assert.deepEqual(getContext(controlPanel), {
|
||||
coucou_1: 1,
|
||||
lang: "en",
|
||||
tz: "taht",
|
||||
uid: 7,
|
||||
});
|
||||
});
|
||||
|
||||
QUnit.test("Filter with JSON-parsable domain works", async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
const xml_domain = "[["foo","=","Gently Weeps"]]";
|
||||
|
||||
const controlPanel = await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
searchViewId: false,
|
||||
searchMenuTypes: ["filter"],
|
||||
searchViewArch: `
|
||||
<search>
|
||||
<filter string="Foo" name="gently_weeps" domain="${xml_domain}"/>
|
||||
</search>
|
||||
`,
|
||||
context: { search_default_gently_weeps: 1 },
|
||||
});
|
||||
|
||||
assert.deepEqual(
|
||||
getDomain(controlPanel),
|
||||
[["foo", "=", "Gently Weeps"]],
|
||||
"A JSON parsable xml domain should be handled just like any other"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test("filter with date attribute set as search_default", async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
patchDate(2019, 6, 31, 13, 43, 0);
|
||||
|
||||
await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
searchViewId: false,
|
||||
searchMenuTypes: ["filter"],
|
||||
searchViewArch: `
|
||||
<search>
|
||||
<filter string="Date" name="date_field" date="date_field" default_period="last_month"/>
|
||||
</search>
|
||||
`,
|
||||
context: { search_default_date_field: true },
|
||||
});
|
||||
|
||||
assert.deepEqual(getFacetTexts(target), ["Date: June 2019"]);
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
"filter with multiple values in default_period date attribute set as search_default",
|
||||
async function (assert) {
|
||||
assert.expect(3);
|
||||
|
||||
patchDate(2019, 6, 31, 13, 43, 0);
|
||||
|
||||
await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
searchViewId: false,
|
||||
searchMenuTypes: ["filter"],
|
||||
searchViewArch: `
|
||||
<search>
|
||||
<filter string="Date" name="date_field" date="date_field" default_period="this_year,last_year"/>
|
||||
</search>
|
||||
`,
|
||||
context: { search_default_date_field: true },
|
||||
});
|
||||
|
||||
await toggleFilterMenu(target);
|
||||
await toggleMenuItem(target, "Date");
|
||||
|
||||
assert.ok(isItemSelected(target, "Date"));
|
||||
assert.ok(isOptionSelected(target, "Date", "2019"));
|
||||
assert.ok(isOptionSelected(target, "Date", "2018"));
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test("filter domains are correcly combined by OR and AND", async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const controlPanel = await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
searchViewId: false,
|
||||
searchMenuTypes: ["filter"],
|
||||
searchViewArch: `
|
||||
<search>
|
||||
<filter string="Filter Group 1" name="f_1_g1" domain="[['foo', '=', 'f1_g1']]"/>
|
||||
<separator/>
|
||||
<filter string="Filter 1 Group 2" name="f1_g2" domain="[['foo', '=', 'f1_g2']]"/>
|
||||
<filter string="Filter 2 GROUP 2" name="f2_g2" domain="[['foo', '=', 'f2_g2']]"/>
|
||||
</search>
|
||||
`,
|
||||
context: {
|
||||
search_default_f_1_g1: true,
|
||||
search_default_f1_g2: true,
|
||||
search_default_f2_g2: true,
|
||||
},
|
||||
});
|
||||
|
||||
assert.deepEqual(getDomain(controlPanel), [
|
||||
"&",
|
||||
["foo", "=", "f1_g1"],
|
||||
"|",
|
||||
["foo", "=", "f1_g2"],
|
||||
["foo", "=", "f2_g2"],
|
||||
]);
|
||||
|
||||
assert.deepEqual(getFacetTexts(target), [
|
||||
"Filter Group 1",
|
||||
"Filter 1 Group 2orFilter 2 GROUP 2",
|
||||
]);
|
||||
});
|
||||
|
||||
QUnit.test("arch order of groups of filters preserved", async function (assert) {
|
||||
assert.expect(12);
|
||||
|
||||
await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
searchViewId: false,
|
||||
searchMenuTypes: ["filter"],
|
||||
searchViewArch: `
|
||||
<search>
|
||||
<filter string="1" name="coolName1" date="date_field"/>
|
||||
<separator/>
|
||||
<filter string="2" name="coolName2" date="date_field"/>
|
||||
<separator/>
|
||||
<filter string="3" name="coolName3" domain="[]"/>
|
||||
<separator/>
|
||||
<filter string="4" name="coolName4" domain="[]"/>
|
||||
<separator/>
|
||||
<filter string="5" name="coolName5" domain="[]"/>
|
||||
<separator/>
|
||||
<filter string="6" name="coolName6" domain="[]"/>
|
||||
<separator/>
|
||||
<filter string="7" name="coolName7" domain="[]"/>
|
||||
<separator/>
|
||||
<filter string="8" name="coolName8" domain="[]"/>
|
||||
<separator/>
|
||||
<filter string="9" name="coolName9" domain="[]"/>
|
||||
<separator/>
|
||||
<filter string="10" name="coolName10" domain="[]"/>
|
||||
<separator/>
|
||||
<filter string="11" name="coolName11" domain="[]"/>
|
||||
</search>
|
||||
`,
|
||||
});
|
||||
|
||||
await toggleFilterMenu(target);
|
||||
assert.containsN(target, ".o_filter_menu .o_menu_item", 11);
|
||||
|
||||
const menuItemEls = target.querySelectorAll(".o_filter_menu .o_menu_item");
|
||||
[...menuItemEls].forEach((e, index) => {
|
||||
assert.strictEqual(e.innerText.trim(), String(index + 1));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,534 +0,0 @@
|
|||
/** @odoo-module **/
|
||||
|
||||
import { browser } from "@web/core/browser/browser";
|
||||
import { ControlPanel } from "@web/search/control_panel/control_panel";
|
||||
import { getFixture, patchWithCleanup } from "../helpers/utils";
|
||||
import {
|
||||
getFacetTexts,
|
||||
isItemSelected,
|
||||
isOptionSelected,
|
||||
makeWithSearch,
|
||||
removeFacet,
|
||||
setupControlPanelServiceRegistry,
|
||||
toggleGroupByMenu,
|
||||
toggleMenuItem,
|
||||
toggleMenuItemOption,
|
||||
} from "./helpers";
|
||||
|
||||
let target;
|
||||
let serverData;
|
||||
QUnit.module("Search", (hooks) => {
|
||||
hooks.beforeEach(async () => {
|
||||
serverData = {
|
||||
models: {
|
||||
foo: {
|
||||
fields: {
|
||||
bar: { string: "Bar", type: "many2one", relation: "partner" },
|
||||
birthday: { string: "Birthday", type: "date", store: true, sortable: true },
|
||||
date_field: { string: "Date", type: "date", store: true, sortable: true },
|
||||
float_field: { string: "Float", type: "float", group_operator: "sum" },
|
||||
foo: { string: "Foo", type: "char", store: true, sortable: true },
|
||||
},
|
||||
records: {},
|
||||
},
|
||||
},
|
||||
views: {
|
||||
"foo,false,search": `<search/>`,
|
||||
},
|
||||
};
|
||||
setupControlPanelServiceRegistry();
|
||||
patchWithCleanup(browser, {
|
||||
setTimeout: (fn) => fn(),
|
||||
clearTimeout: () => {},
|
||||
});
|
||||
target = getFixture();
|
||||
});
|
||||
|
||||
QUnit.module("GroupByMenu");
|
||||
|
||||
QUnit.test(
|
||||
"simple rendering with neither groupbys nor groupable fields",
|
||||
async function (assert) {
|
||||
assert.expect(3);
|
||||
|
||||
await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
searchMenuTypes: ["groupBy"],
|
||||
searchViewId: false,
|
||||
searchViewFields: {},
|
||||
});
|
||||
|
||||
await toggleGroupByMenu(target);
|
||||
|
||||
assert.containsNone(target, ".o_menu_item");
|
||||
assert.containsNone(target, ".dropdown-divider");
|
||||
assert.containsNone(target, ".o_add_custom_group_menu");
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test("simple rendering with no groupby", async function (assert) {
|
||||
assert.expect(3);
|
||||
|
||||
await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
searchMenuTypes: ["groupBy"],
|
||||
searchViewId: false,
|
||||
});
|
||||
|
||||
await toggleGroupByMenu(target);
|
||||
|
||||
assert.containsNone(target, ".o_menu_item");
|
||||
assert.containsNone(target, ".dropdown-divider");
|
||||
assert.containsOnce(target, ".o_add_custom_group_menu");
|
||||
});
|
||||
|
||||
QUnit.test("simple rendering with a single groupby", async function (assert) {
|
||||
await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
searchMenuTypes: ["groupBy"],
|
||||
searchViewId: false,
|
||||
searchViewArch: `
|
||||
<search>
|
||||
<filter string="Foo" name="group_by_foo" context="{'group_by': 'foo'}"/>
|
||||
</search>
|
||||
`,
|
||||
});
|
||||
|
||||
await toggleGroupByMenu(target);
|
||||
|
||||
assert.containsOnce(target, ".o_menu_item");
|
||||
const menuItem = target.querySelector(".o_menu_item");
|
||||
assert.strictEqual(menuItem.innerText.trim(), "Foo");
|
||||
assert.strictEqual(menuItem.getAttribute("role"), "menuitemcheckbox");
|
||||
assert.strictEqual(menuItem.ariaChecked, "false");
|
||||
assert.containsOnce(target, ".dropdown-divider");
|
||||
assert.containsOnce(target, ".o_add_custom_group_menu");
|
||||
});
|
||||
|
||||
QUnit.test('toggle a "simple" groupby in groupby menu works', async function (assert) {
|
||||
const controlPanel = await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
searchMenuTypes: ["groupBy"],
|
||||
searchViewId: false,
|
||||
searchViewArch: `
|
||||
<search>
|
||||
<filter string="Foo" name="group_by_foo" context="{'group_by': 'foo'}"/>
|
||||
</search>
|
||||
`,
|
||||
});
|
||||
|
||||
await toggleGroupByMenu(target);
|
||||
|
||||
assert.deepEqual(controlPanel.env.searchModel.groupBy, []);
|
||||
assert.deepEqual(getFacetTexts(target), []);
|
||||
assert.notOk(isItemSelected(target, "Foo"));
|
||||
const menuItem = target.querySelector(".o_menu_item");
|
||||
assert.strictEqual(menuItem.innerText.trim(), "Foo");
|
||||
assert.strictEqual(menuItem.getAttribute("role"), "menuitemcheckbox");
|
||||
assert.strictEqual(menuItem.ariaChecked, "false");
|
||||
await toggleMenuItem(target, "Foo");
|
||||
assert.strictEqual(menuItem.ariaChecked, "true");
|
||||
|
||||
assert.deepEqual(controlPanel.env.searchModel.groupBy, ["foo"]);
|
||||
assert.deepEqual(getFacetTexts(target), ["Foo"]);
|
||||
assert.containsOnce(
|
||||
target.querySelector(".o_searchview .o_searchview_facet"),
|
||||
"span.oi.oi-group.o_searchview_facet_label"
|
||||
);
|
||||
assert.ok(isItemSelected(target, "Foo"));
|
||||
|
||||
await toggleMenuItem(target, "Foo");
|
||||
|
||||
assert.deepEqual(controlPanel.env.searchModel.groupBy, []);
|
||||
assert.deepEqual(getFacetTexts(target), []);
|
||||
assert.notOk(isItemSelected(target, "Foo"));
|
||||
});
|
||||
|
||||
QUnit.test('toggle a "simple" groupby quickly does not crash', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
searchMenuTypes: ["groupBy"],
|
||||
searchViewId: false,
|
||||
searchViewArch: `
|
||||
<search>
|
||||
<filter string="Foo" name="group_by_foo" context="{'group_by': 'foo'}"/>
|
||||
</search>
|
||||
`,
|
||||
});
|
||||
|
||||
await toggleGroupByMenu(target);
|
||||
|
||||
toggleMenuItem(target, "Foo");
|
||||
toggleMenuItem(target, "Foo");
|
||||
|
||||
assert.ok(true);
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
'remove a "Group By" facet properly unchecks groupbys in groupby menu',
|
||||
async function (assert) {
|
||||
assert.expect(6);
|
||||
|
||||
const controlPanel = await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
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 toggleGroupByMenu(target);
|
||||
|
||||
assert.deepEqual(getFacetTexts(target), ["Foo"]);
|
||||
assert.deepEqual(controlPanel.env.searchModel.groupBy, ["foo"]);
|
||||
assert.ok(isItemSelected(target, "Foo"));
|
||||
|
||||
await removeFacet(target, "Foo");
|
||||
|
||||
assert.deepEqual(getFacetTexts(target), []);
|
||||
assert.deepEqual(controlPanel.env.searchModel.groupBy, []);
|
||||
|
||||
await toggleGroupByMenu(target);
|
||||
|
||||
assert.notOk(isItemSelected(target, "Foo"));
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test("group by a date field using interval works", async function (assert) {
|
||||
assert.expect(21);
|
||||
|
||||
const controlPanel = await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
searchMenuTypes: ["groupBy"],
|
||||
searchViewId: false,
|
||||
searchViewArch: `
|
||||
<search>
|
||||
<filter string="Date" name="date" context="{'group_by': 'date_field:week'}"/>
|
||||
</search>
|
||||
`,
|
||||
context: { search_default_date: 1 },
|
||||
});
|
||||
|
||||
await toggleGroupByMenu(target);
|
||||
|
||||
assert.deepEqual(controlPanel.env.searchModel.groupBy, ["date_field:week"]);
|
||||
|
||||
await toggleMenuItem(target, "Date");
|
||||
|
||||
assert.ok(isOptionSelected(target, "Date", "Week"));
|
||||
|
||||
assert.deepEqual(
|
||||
[...target.querySelectorAll(".o_item_option")].map((el) => el.innerText),
|
||||
["Year", "Quarter", "Month", "Week", "Day"]
|
||||
);
|
||||
|
||||
const steps = [
|
||||
{
|
||||
description: "Year",
|
||||
facetTexts: ["Date: Year>Date: Week"],
|
||||
selectedoptions: ["Year", "Week"],
|
||||
groupBy: ["date_field:year", "date_field:week"],
|
||||
},
|
||||
{
|
||||
description: "Month",
|
||||
facetTexts: ["Date: Year>Date: Month>Date: Week"],
|
||||
selectedoptions: ["Year", "Month", "Week"],
|
||||
groupBy: ["date_field:year", "date_field:month", "date_field:week"],
|
||||
},
|
||||
{
|
||||
description: "Week",
|
||||
facetTexts: ["Date: Year>Date: Month"],
|
||||
selectedoptions: ["Year", "Month"],
|
||||
groupBy: ["date_field:year", "date_field:month"],
|
||||
},
|
||||
{
|
||||
description: "Month",
|
||||
facetTexts: ["Date: Year"],
|
||||
selectedoptions: ["Year"],
|
||||
groupBy: ["date_field:year"],
|
||||
},
|
||||
{ description: "Year", facetTexts: [], selectedoptions: [], groupBy: [] },
|
||||
];
|
||||
for (const s of steps) {
|
||||
await toggleMenuItemOption(target, "Date", s.description);
|
||||
|
||||
assert.deepEqual(controlPanel.env.searchModel.groupBy, s.groupBy);
|
||||
assert.deepEqual(getFacetTexts(target), s.facetTexts);
|
||||
s.selectedoptions.forEach((description) => {
|
||||
assert.ok(isOptionSelected(target, "Date", description));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
QUnit.test("interval options are correctly grouped and ordered", async function (assert) {
|
||||
assert.expect(8);
|
||||
|
||||
await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
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 },
|
||||
});
|
||||
|
||||
assert.deepEqual(getFacetTexts(target), ["Bar"]);
|
||||
|
||||
// open menu 'Group By'
|
||||
await toggleGroupByMenu(target);
|
||||
|
||||
// Open the groupby 'Date'
|
||||
await toggleMenuItem(target, "Date");
|
||||
// select option 'week'
|
||||
await toggleMenuItemOption(target, "Date", "Week");
|
||||
|
||||
assert.deepEqual(getFacetTexts(target), ["Bar>Date: Week"]);
|
||||
|
||||
// select option 'day'
|
||||
await toggleMenuItemOption(target, "Date", "Day");
|
||||
|
||||
assert.deepEqual(getFacetTexts(target), ["Bar>Date: Week>Date: Day"]);
|
||||
|
||||
// select option 'year'
|
||||
await toggleMenuItemOption(target, "Date", "Year");
|
||||
|
||||
assert.deepEqual(getFacetTexts(target), ["Bar>Date: Year>Date: Week>Date: Day"]);
|
||||
|
||||
// select 'Foo'
|
||||
await toggleMenuItem(target, "Foo");
|
||||
|
||||
assert.deepEqual(getFacetTexts(target), ["Bar>Date: Year>Date: Week>Date: Day>Foo"]);
|
||||
|
||||
// select option 'quarter'
|
||||
await toggleMenuItem(target, "Date");
|
||||
await toggleMenuItemOption(target, "Date", "Quarter");
|
||||
|
||||
assert.deepEqual(getFacetTexts(target), [
|
||||
"Bar>Date: Year>Date: Quarter>Date: Week>Date: Day>Foo",
|
||||
]);
|
||||
|
||||
// unselect 'Bar'
|
||||
await toggleMenuItem(target, "Bar");
|
||||
|
||||
assert.deepEqual(getFacetTexts(target), [
|
||||
"Date: Year>Date: Quarter>Date: Week>Date: Day>Foo",
|
||||
]);
|
||||
|
||||
// unselect option 'week'
|
||||
await toggleMenuItem(target, "Date");
|
||||
await toggleMenuItemOption(target, "Date", "Week");
|
||||
|
||||
assert.deepEqual(getFacetTexts(target), ["Date: Year>Date: Quarter>Date: Day>Foo"]);
|
||||
});
|
||||
|
||||
QUnit.test("default groupbys can be ordered", async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const controlPanel = await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
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
|
||||
assert.deepEqual(controlPanel.env.searchModel.groupBy, [
|
||||
"date_field:week",
|
||||
"birthday:month",
|
||||
]);
|
||||
assert.deepEqual(getFacetTexts(target), ["Date: Week>Birthday: Month"]);
|
||||
});
|
||||
|
||||
QUnit.test("a separator in groupbys does not cause problems", async function (assert) {
|
||||
assert.expect(23);
|
||||
|
||||
await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
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 toggleGroupByMenu(target);
|
||||
await toggleMenuItem(target, "Date");
|
||||
await toggleMenuItemOption(target, "Date", "Day");
|
||||
|
||||
assert.ok(isItemSelected(target, "Date"));
|
||||
assert.notOk(isItemSelected(target, "Bar"));
|
||||
assert.ok(isOptionSelected(target, "Date", "Day"), "selected");
|
||||
assert.deepEqual(getFacetTexts(target), ["Date: Day"]);
|
||||
|
||||
await toggleMenuItem(target, "Bar");
|
||||
await toggleMenuItem(target, "Date");
|
||||
|
||||
assert.ok(isItemSelected(target, "Date"));
|
||||
assert.ok(isItemSelected(target, "Bar"));
|
||||
assert.ok(isOptionSelected(target, "Date", "Day"), "selected");
|
||||
assert.deepEqual(getFacetTexts(target), ["Date: Day>Bar"]);
|
||||
|
||||
await toggleMenuItemOption(target, "Date", "Quarter");
|
||||
|
||||
assert.ok(isItemSelected(target, "Date"));
|
||||
assert.ok(isItemSelected(target, "Bar"));
|
||||
assert.ok(isOptionSelected(target, "Date", "Quarter"), "selected");
|
||||
assert.ok(isOptionSelected(target, "Date", "Day"), "selected");
|
||||
assert.deepEqual(getFacetTexts(target), ["Date: Quarter>Date: Day>Bar"]);
|
||||
|
||||
await toggleMenuItem(target, "Bar");
|
||||
await toggleMenuItem(target, "Date");
|
||||
|
||||
assert.ok(isItemSelected(target, "Date"));
|
||||
assert.notOk(isItemSelected(target, "Bar"));
|
||||
assert.ok(isOptionSelected(target, "Date", "Quarter"), "selected");
|
||||
assert.ok(isOptionSelected(target, "Date", "Day"), "selected");
|
||||
assert.deepEqual(getFacetTexts(target), ["Date: Quarter>Date: Day"]);
|
||||
|
||||
await removeFacet(target);
|
||||
|
||||
assert.deepEqual(getFacetTexts(target), []);
|
||||
|
||||
await toggleGroupByMenu(target);
|
||||
await toggleMenuItem(target, "Date");
|
||||
|
||||
assert.notOk(isItemSelected(target, "Date"));
|
||||
assert.notOk(isItemSelected(target, "Bar"));
|
||||
assert.notOk(isOptionSelected(target, "Date", "Quarter"), "selected");
|
||||
assert.notOk(isOptionSelected(target, "Date", "Day"), "selected");
|
||||
});
|
||||
|
||||
QUnit.test("falsy search default groupbys are not activated", async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const controlPanel = await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
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 },
|
||||
});
|
||||
|
||||
assert.deepEqual(controlPanel.env.searchModel.groupBy, []);
|
||||
assert.deepEqual(getFacetTexts(target), []);
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
"Custom group by menu is displayed when hideCustomGroupBy is not set",
|
||||
async function (assert) {
|
||||
await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
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 toggleGroupByMenu(target);
|
||||
|
||||
assert.containsOnce(target, ".o_add_custom_group_menu");
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test(
|
||||
"Custom group by menu is displayed when hideCustomGroupBy is false",
|
||||
async function (assert) {
|
||||
await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
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 toggleGroupByMenu(target);
|
||||
|
||||
assert.containsOnce(target, ".o_add_custom_group_menu");
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test(
|
||||
"Custom group by menu is displayed when hideCustomGroupBy is true",
|
||||
async function (assert) {
|
||||
await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
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 toggleGroupByMenu(target);
|
||||
|
||||
assert.containsNone(target, ".o_add_custom_group_menu");
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
@ -34,12 +34,12 @@ QUnit.module("GroupBy Class", {}, () => {
|
|||
assert.expect(3);
|
||||
try {
|
||||
getGroupBy(":day");
|
||||
} catch (_e) {
|
||||
} catch {
|
||||
assert.step("Error 1");
|
||||
}
|
||||
try {
|
||||
getGroupBy("diay_name:yar");
|
||||
} catch (_e) {
|
||||
} catch {
|
||||
assert.step("Error 2");
|
||||
}
|
||||
assert.verifySteps(["Error 1", "Error 2"]);
|
||||
|
|
@ -56,22 +56,22 @@ QUnit.module("GroupBy Class", {}, () => {
|
|||
assert.expect(5);
|
||||
try {
|
||||
getGroupBy("", fields);
|
||||
} catch (_e) {
|
||||
} catch {
|
||||
assert.step("Error 1");
|
||||
}
|
||||
try {
|
||||
getGroupBy("display_name:day", fields);
|
||||
} catch (_e) {
|
||||
} catch {
|
||||
assert.step("Error 2");
|
||||
}
|
||||
try {
|
||||
getGroupBy("diay_name:year", fields);
|
||||
} catch (_e) {
|
||||
} catch {
|
||||
assert.step("Error 3");
|
||||
}
|
||||
try {
|
||||
getGroupBy("diay_name:yar", fields);
|
||||
} catch (_e) {
|
||||
} catch {
|
||||
assert.step("Error 4");
|
||||
}
|
||||
assert.verifySteps(["Error 1", "Error 2", "Error 3", "Error 4"]);
|
||||
|
|
@ -91,7 +91,7 @@ QUnit.module("GroupBy Class", {}, () => {
|
|||
assert.expect(2);
|
||||
try {
|
||||
getGroupBy("date_field:yar", fields);
|
||||
} catch (_e) {
|
||||
} catch {
|
||||
assert.step("Error");
|
||||
}
|
||||
assert.verifySteps(["Error"]);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
/** @odoo-module **/
|
||||
|
||||
import { Component, xml } from "@odoo/owl";
|
||||
import { makeTestEnv } from "@web/../tests/helpers/mock_env";
|
||||
import {
|
||||
click,
|
||||
|
|
@ -10,29 +11,38 @@ import {
|
|||
triggerEvent,
|
||||
triggerEvents,
|
||||
} from "@web/../tests/helpers/utils";
|
||||
import { commandService } from "@web/core/commands/command_service";
|
||||
import { dialogService } from "@web/core/dialog/dialog_service";
|
||||
import { fieldService } from "@web/core/field_service";
|
||||
import { hotkeyService } from "@web/core/hotkeys/hotkey_service";
|
||||
import { notificationService } from "@web/core/notifications/notification_service";
|
||||
import { ormService } from "@web/core/orm_service";
|
||||
import { popoverService } from "@web/core/popover/popover_service";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { CustomFavoriteItem } from "@web/search/favorite_menu/custom_favorite_item";
|
||||
import { CustomFavoriteItem } from "@web/search/custom_favorite_item/custom_favorite_item";
|
||||
import { WithSearch } from "@web/search/with_search/with_search";
|
||||
import { getDefaultConfig } from "@web/views/view";
|
||||
import { viewService } from "@web/views/view_service";
|
||||
import { actionService } from "@web/webclient/actions/action_service";
|
||||
import { dialogService } from "@web/core/dialog/dialog_service";
|
||||
import { MainComponentsContainer } from "@web/core/main_components_container";
|
||||
import { nameService } from "@web/core/name_service";
|
||||
import { datetimePickerService } from "@web/core/datetime/datetimepicker_service";
|
||||
|
||||
import { Component, xml } from "@odoo/owl";
|
||||
const serviceRegistry = registry.category("services");
|
||||
const favoriteMenuRegistry = registry.category("favoriteMenu");
|
||||
|
||||
export function setupControlPanelServiceRegistry() {
|
||||
serviceRegistry.add("action", actionService);
|
||||
serviceRegistry.add("dialog", dialogService);
|
||||
serviceRegistry.add("field", fieldService);
|
||||
serviceRegistry.add("hotkey", hotkeyService);
|
||||
serviceRegistry.add("name", nameService);
|
||||
serviceRegistry.add("notification", notificationService);
|
||||
serviceRegistry.add("orm", ormService);
|
||||
serviceRegistry.add("popover", popoverService);
|
||||
serviceRegistry.add("view", viewService);
|
||||
serviceRegistry.add("dialog", dialogService);
|
||||
serviceRegistry.add("command", commandService);
|
||||
serviceRegistry.add("datetime_picker", datetimePickerService);
|
||||
}
|
||||
|
||||
export function setupControlPanelFavoriteMenuRegistry() {
|
||||
|
|
@ -63,22 +73,24 @@ export async function makeWithSearch(params) {
|
|||
class Parent extends Component {
|
||||
setup() {
|
||||
this.withSearchProps = props;
|
||||
this.componentProps = componentProps;
|
||||
}
|
||||
getDisplay(display) {
|
||||
return Object.assign({}, display, componentProps.display);
|
||||
|
||||
getProps(search) {
|
||||
const props = Object.assign({}, componentProps, {
|
||||
context: search.context,
|
||||
domain: search.domain,
|
||||
groupBy: search.groupBy,
|
||||
orderBy: search.orderBy,
|
||||
comparison: search.comparison,
|
||||
display: Object.assign({}, search.display, componentProps.display),
|
||||
});
|
||||
return filterPropsForComponent(params.Component, props);
|
||||
}
|
||||
}
|
||||
|
||||
Parent.template = xml`
|
||||
<WithSearch t-props="withSearchProps" t-slot-scope="search">
|
||||
<Component
|
||||
t-props="componentProps"
|
||||
context="search.context"
|
||||
domain="search.domain"
|
||||
groupBy="search.groupBy"
|
||||
orderBy="search.orderBy"
|
||||
comparison="search.comparison"
|
||||
display="getDisplay(search.display)"/>
|
||||
<Component t-props="getProps(search)"/>
|
||||
</WithSearch>
|
||||
<MainComponentsContainer />
|
||||
`;
|
||||
|
|
@ -94,6 +106,40 @@ export async function makeWithSearch(params) {
|
|||
return component;
|
||||
}
|
||||
|
||||
/** This function is aim to be used only in the tests.
|
||||
* It will filter the props that are needed by the Component.
|
||||
* This is to avoid errors of props validation. This occurs for example, on ControlPanel tests.
|
||||
* In production, View use WithSearch for the Controllers, and the Layout send only the props that
|
||||
* need to the ControlPanel.
|
||||
*
|
||||
* @param {Component} Component
|
||||
* @param {Object} props
|
||||
* @returns {Object} filtered props
|
||||
*/
|
||||
function filterPropsForComponent(Component, props) {
|
||||
// This if, can be removed once all the Components have the props defined
|
||||
if (Component.props) {
|
||||
let componentKeys = null;
|
||||
if (Component.props instanceof Array) {
|
||||
componentKeys = Component.props.map((x) => x.replace("?", ""));
|
||||
} else {
|
||||
componentKeys = Object.keys(Component.props);
|
||||
}
|
||||
if (componentKeys.includes("*")) {
|
||||
return props;
|
||||
} else {
|
||||
return Object.keys(props)
|
||||
.filter((k) => componentKeys.includes(k))
|
||||
.reduce((o, k) => {
|
||||
o[k] = props[k];
|
||||
return o;
|
||||
}, {});
|
||||
}
|
||||
} else {
|
||||
return props;
|
||||
}
|
||||
}
|
||||
|
||||
function getUniqueChild(node) {
|
||||
return Object.values(node.children)[0];
|
||||
}
|
||||
|
|
@ -102,7 +148,7 @@ function getNode(target) {
|
|||
return target instanceof Component ? target.el : target;
|
||||
}
|
||||
|
||||
function findItem(target, selector, finder = 0) {
|
||||
export function findItem(target, selector, finder = 0) {
|
||||
const el = getNode(target);
|
||||
const elems = [...el.querySelectorAll(selector)];
|
||||
if (Number.isInteger(finder)) {
|
||||
|
|
@ -153,117 +199,57 @@ export function getMenuItemTexts(target) {
|
|||
return [...el.querySelectorAll(`.dropdown-menu .o_menu_item`)].map((e) => e.innerText.trim());
|
||||
}
|
||||
|
||||
export function getButtons(el) {
|
||||
return [...el.querySelector(`div.o_cp_bottom div.o_cp_buttons`).children];
|
||||
export function getVisibleButtons(el) {
|
||||
return [
|
||||
...$(el).find(
|
||||
[
|
||||
"div.o_control_panel_breadcrumbs button:visible", // button in the breadcrumbs
|
||||
"div.o_control_panel_actions button:visible", // buttons for list selection
|
||||
].join(",")
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
/** Filter menu */
|
||||
|
||||
export async function toggleFilterMenu(el) {
|
||||
await click(findItem(el, `.o_filter_menu button.dropdown-toggle`));
|
||||
}
|
||||
|
||||
export async function toggleAddCustomFilter(el) {
|
||||
await mouseEnter(findItem(el, `.o_add_custom_filter_menu .dropdown-toggle`));
|
||||
}
|
||||
|
||||
export async function editConditionField(el, index, fieldName) {
|
||||
const condition = findItem(el, `.o_filter_condition`, index);
|
||||
const select = findItem(condition, "select", 0);
|
||||
select.value = fieldName;
|
||||
await triggerEvent(select, null, "change");
|
||||
}
|
||||
|
||||
export async function editConditionOperator(el, index, operator) {
|
||||
const condition = findItem(el, `.o_filter_condition`, index);
|
||||
const select = findItem(condition, "select", 1);
|
||||
select.value = operator;
|
||||
await triggerEvent(select, null, "change");
|
||||
}
|
||||
|
||||
export async function editConditionValue(el, index, value, valueIndex = 0) {
|
||||
const condition = findItem(el, `.o_filter_condition`, index);
|
||||
const target = findItem(
|
||||
condition,
|
||||
".o_generator_menu_value input:not([type=hidden]),.o_generator_menu_value select",
|
||||
valueIndex
|
||||
);
|
||||
target.value = value;
|
||||
await triggerEvent(target, null, "change");
|
||||
}
|
||||
|
||||
export async function applyFilter(el) {
|
||||
await click(findItem(el, `.o_add_custom_filter_menu .dropdown-menu button.o_apply_filter`));
|
||||
}
|
||||
|
||||
export async function addCondition(el) {
|
||||
await click(findItem(el, `.o_add_custom_filter_menu .dropdown-menu button.o_add_condition`));
|
||||
}
|
||||
|
||||
export async function removeCondition(el, index) {
|
||||
const condition = findItem(el, `.o_filter_condition`, index);
|
||||
await click(findItem(condition, ".o_generator_menu_delete"));
|
||||
export async function openAddCustomFilterDialog(el) {
|
||||
await click(findItem(el, `.o_filter_menu .o_menu_item.o_add_custom_filter`));
|
||||
}
|
||||
|
||||
/** Group by menu */
|
||||
|
||||
export async function toggleGroupByMenu(el) {
|
||||
await click(findItem(el, `.o_group_by_menu .dropdown-toggle`));
|
||||
}
|
||||
|
||||
export async function toggleAddCustomGroup(el) {
|
||||
await mouseEnter(findItem(el, `.o_add_custom_group_menu .dropdown-toggle`));
|
||||
}
|
||||
|
||||
export async function selectGroup(el, fieldName) {
|
||||
const select = findItem(el, `.o_add_custom_group_menu .dropdown-menu select`);
|
||||
select.value = fieldName;
|
||||
await triggerEvent(select, null, "change");
|
||||
}
|
||||
|
||||
export async function applyGroup(el) {
|
||||
await click(findItem(el, `.o_add_custom_group_menu .dropdown-menu .btn`));
|
||||
el.querySelector(".o_add_custom_group_menu").value = fieldName;
|
||||
await triggerEvent(el, ".o_add_custom_group_menu", "change");
|
||||
}
|
||||
|
||||
export async function groupByMenu(el, fieldName) {
|
||||
await toggleGroupByMenu(el);
|
||||
await toggleAddCustomGroup(el);
|
||||
await toggleSearchBarMenu(el);
|
||||
await selectGroup(el, fieldName);
|
||||
await applyGroup(el);
|
||||
}
|
||||
|
||||
/** Favorite menu */
|
||||
|
||||
export async function toggleFavoriteMenu(el) {
|
||||
await click(findItem(el, `.o_favorite_menu .dropdown-toggle`));
|
||||
}
|
||||
|
||||
export async function deleteFavorite(el, favoriteFinder) {
|
||||
const favorite = findItem(el, `.o_favorite_menu .o_menu_item`, favoriteFinder);
|
||||
await click(findItem(favorite, "i.fa-trash-o"));
|
||||
}
|
||||
|
||||
export async function toggleSaveFavorite(el) {
|
||||
await mouseEnter(findItem(el, `.o_favorite_menu .o_add_favorite .dropdown-toggle`));
|
||||
await click(findItem(el, `.o_favorite_menu .o_add_favorite`));
|
||||
}
|
||||
|
||||
export async function editFavoriteName(el, name) {
|
||||
const input = findItem(
|
||||
el,
|
||||
`.o_favorite_menu .o_add_favorite .dropdown-menu input[type="text"]`
|
||||
`.o_favorite_menu .o_add_favorite + .o_accordion_values input[type="text"]`
|
||||
);
|
||||
input.value = name;
|
||||
await triggerEvents(input, null, ["input", "change"]);
|
||||
}
|
||||
|
||||
export async function saveFavorite(el) {
|
||||
await click(findItem(el, `.o_favorite_menu .o_add_favorite .dropdown-menu button`));
|
||||
}
|
||||
|
||||
/** Comparison menu */
|
||||
|
||||
export async function toggleComparisonMenu(el) {
|
||||
await click(findItem(el, `.o_comparison_menu button.dropdown-toggle`));
|
||||
await click(findItem(el, `.o_favorite_menu .o_add_favorite + .o_accordion_values button`));
|
||||
}
|
||||
|
||||
/** Search bar */
|
||||
|
|
@ -277,7 +263,7 @@ export function getFacetTexts(target) {
|
|||
|
||||
export async function removeFacet(el, facetFinder = 0) {
|
||||
const facet = findItem(el, `div.o_searchview_facet`, facetFinder);
|
||||
await click(facet.querySelector("i.o_facet_remove"));
|
||||
await click(facet.querySelector(".o_facet_remove"));
|
||||
}
|
||||
|
||||
export async function editSearch(el, value) {
|
||||
|
|
@ -330,7 +316,11 @@ export async function editPager(el, value) {
|
|||
* @param {string} [menuFinder="Action"]
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export async function toggleActionMenu(el, menuFinder = "Action") {
|
||||
const dropdown = findItem(el, `.o_cp_action_menus button`, menuFinder);
|
||||
await click(dropdown);
|
||||
export async function toggleActionMenu(el) {
|
||||
await click(el.querySelector(".o_cp_action_menus .dropdown-toggle"));
|
||||
}
|
||||
|
||||
/** SearchBarMenu */
|
||||
export async function toggleSearchBarMenu(el) {
|
||||
await click(findItem(el, `.o_searchview_dropdown_toggler`));
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -49,6 +49,12 @@ QUnit.module("Search", (hooks) => {
|
|||
date_field: { string: "Date", type: "date", store: true, sortable: true },
|
||||
float_field: { string: "Float", type: "float" },
|
||||
bar: { string: "Bar", type: "many2one", relation: "partner" },
|
||||
properties: {
|
||||
string: "Properties",
|
||||
type: "properties",
|
||||
definition_record: "bar",
|
||||
definition_record_field: "child_properties",
|
||||
},
|
||||
},
|
||||
records: [],
|
||||
},
|
||||
|
|
@ -959,6 +965,44 @@ QUnit.module("Search", (hooks) => {
|
|||
assert.deepEqual(model.domain, [["date_deadline", "<", "2021-09-17"]]);
|
||||
});
|
||||
|
||||
QUnit.test("field tags with invisible attribute", async function (assert) {
|
||||
const model = await makeSearchModel({
|
||||
serverData,
|
||||
searchViewArch: `
|
||||
<search>
|
||||
<field name="foo" invisible="context.get('abc')"/>
|
||||
<field name="bar" invisible="context.get('def')"/>
|
||||
<field name="float_field" invisible="1"/>
|
||||
</search>
|
||||
`,
|
||||
context: { abc: true },
|
||||
});
|
||||
assert.deepEqual(
|
||||
model.getSearchItems((f) => f.type === "field").map((item) => item.fieldName),
|
||||
["bar"]
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test("filter tags with invisible attribute", async function (assert) {
|
||||
const model = await makeSearchModel({
|
||||
serverData,
|
||||
searchViewArch: `
|
||||
<search>
|
||||
<filter name="filter1" string="Invisible ABC" domain="[]" invisible="context.get('abc')"/>
|
||||
<filter name="filter2" string="Invisible DEF" domain="[]" invisible="context.get('def')"/>
|
||||
<filter name="filter3" string="Always invisible" domain="[]" invisible="1"/>
|
||||
</search>
|
||||
`,
|
||||
context: { abc: true },
|
||||
});
|
||||
assert.deepEqual(
|
||||
model
|
||||
.getSearchItems((item) => ["filter", "dateFilter"].includes(item.type))
|
||||
.map((item) => item.name),
|
||||
["filter2"]
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test("no search items created for search panel sections", async function (assert) {
|
||||
const model = await makeSearchModel({
|
||||
serverData,
|
||||
|
|
@ -977,4 +1021,31 @@ QUnit.module("Search", (hooks) => {
|
|||
assert.strictEqual(sections.length, 2);
|
||||
assert.deepEqual(sanitizeSearchItems(model), []);
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
"a field of type 'properties' should not be accepted as a search_default",
|
||||
async function (assert) {
|
||||
const searchViewArch = `
|
||||
<search>
|
||||
<field name="properties"/>
|
||||
</search>
|
||||
`;
|
||||
|
||||
const model = await makeSearchModel({
|
||||
serverData,
|
||||
searchViewArch,
|
||||
context: {
|
||||
search_default_properties: true,
|
||||
},
|
||||
});
|
||||
assert.deepEqual(sanitizeSearchItems(model), [
|
||||
{
|
||||
description: "Properties",
|
||||
fieldName: "properties",
|
||||
fieldType: "properties",
|
||||
type: "field",
|
||||
},
|
||||
]);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -12,16 +12,20 @@ import {
|
|||
makeWithSearch,
|
||||
setupControlPanelServiceRegistry,
|
||||
switchView,
|
||||
toggleFilterMenu,
|
||||
toggleSearchBarMenu,
|
||||
toggleMenuItem,
|
||||
} from "@web/../tests/search/helpers";
|
||||
import { createWebClient, doAction } from "@web/../tests/webclient/helpers";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { FilterMenu } from "@web/search/filter_menu/filter_menu";
|
||||
import { GroupByMenu } from "@web/search/group_by_menu/group_by_menu";
|
||||
import { SearchBarMenu } from "@web/search/search_bar_menu/search_bar_menu";
|
||||
import { SearchPanel } from "@web/search/search_panel/search_panel";
|
||||
|
||||
import { Component, xml } from "@odoo/owl";
|
||||
import {
|
||||
Component,
|
||||
xml,
|
||||
onWillStart as onWillStartOWL,
|
||||
onWillUpdateProps as onWillUpdatePropsOWL,
|
||||
} from "@odoo/owl";
|
||||
|
||||
const serviceRegistry = registry.category("services");
|
||||
|
||||
|
|
@ -107,13 +111,13 @@ function makeTestComponent({ onWillStart, onWillUpdateProps } = {}) {
|
|||
let domain;
|
||||
class TestComponent extends Component {
|
||||
setup() {
|
||||
owl.onWillStart(async () => {
|
||||
onWillStartOWL(async () => {
|
||||
if (onWillStart) {
|
||||
await onWillStart();
|
||||
}
|
||||
domain = this.props.domain;
|
||||
});
|
||||
owl.onWillUpdateProps(async (nextProps) => {
|
||||
onWillUpdatePropsOWL(async (nextProps) => {
|
||||
if (onWillUpdateProps) {
|
||||
await onWillUpdateProps();
|
||||
}
|
||||
|
|
@ -122,12 +126,11 @@ function makeTestComponent({ onWillStart, onWillUpdateProps } = {}) {
|
|||
}
|
||||
}
|
||||
|
||||
TestComponent.components = { FilterMenu, GroupByMenu, SearchPanel };
|
||||
TestComponent.components = { SearchBarMenu, SearchPanel };
|
||||
TestComponent.template = xml`
|
||||
<div class="o_test_component">
|
||||
<SearchPanel t-if="env.searchModel.display.searchPanel" />
|
||||
<FilterMenu />
|
||||
<GroupByMenu />
|
||||
<SearchBarMenu />
|
||||
</div>`;
|
||||
|
||||
return { TestComponent, getDomain: () => domain };
|
||||
|
|
@ -947,9 +950,9 @@ QUnit.module("Search", (hooks) => {
|
|||
|
||||
// unfold agrolait
|
||||
function getAgrolaitElement() {
|
||||
return [
|
||||
...target.querySelectorAll(".o_search_panel_category_value > header"),
|
||||
].find((el) => el.innerText.includes("agrolait"));
|
||||
return [...target.querySelectorAll(".o_search_panel_category_value > header")].find(
|
||||
(el) => el.innerText.includes("agrolait")
|
||||
);
|
||||
}
|
||||
|
||||
await click(getAgrolaitElement());
|
||||
|
|
@ -960,7 +963,7 @@ QUnit.module("Search", (hooks) => {
|
|||
);
|
||||
assert.containsN(target, ".o_search_panel_category_value", 5);
|
||||
|
||||
await toggleFilterMenu(target);
|
||||
await toggleSearchBarMenu(target);
|
||||
await toggleMenuItem(target, "True Domain");
|
||||
|
||||
assert.hasClass(
|
||||
|
|
@ -987,11 +990,6 @@ QUnit.module("Search", (hooks) => {
|
|||
});
|
||||
await makeWithSearch({
|
||||
serverData,
|
||||
async mockRPC(route) {
|
||||
if (route === "/web/dataset/search_read") {
|
||||
await promise;
|
||||
}
|
||||
},
|
||||
Component: TestComponent,
|
||||
resModel: "partner",
|
||||
searchViewId: false,
|
||||
|
|
@ -1083,7 +1081,7 @@ QUnit.module("Search", (hooks) => {
|
|||
// Case 2: search domain changed so we wait for the search panel once again
|
||||
promise = makeDeferred();
|
||||
|
||||
await toggleFilterMenu(target);
|
||||
await toggleSearchBarMenu(target);
|
||||
await toggleMenuItem(target, 0);
|
||||
|
||||
assert.verifySteps([]);
|
||||
|
|
@ -1285,7 +1283,7 @@ QUnit.module("Search", (hooks) => {
|
|||
// trigger a reload and delay the get_filter
|
||||
promise = makeDeferred();
|
||||
|
||||
await toggleFilterMenu(target);
|
||||
await toggleSearchBarMenu(target);
|
||||
await toggleMenuItem(target, 0);
|
||||
|
||||
assert.deepEqual(getDomain(), []);
|
||||
|
|
@ -1434,7 +1432,7 @@ QUnit.module("Search", (hooks) => {
|
|||
assert.verifySteps(["search_panel_select_range", "search_panel_select_multi_range"]);
|
||||
|
||||
// reload with another domain, so the filters should be reloaded
|
||||
await toggleFilterMenu(target);
|
||||
await toggleSearchBarMenu(target);
|
||||
await toggleMenuItem(target, 0);
|
||||
|
||||
assert.verifySteps(["search_panel_select_multi_range"]);
|
||||
|
|
@ -1476,7 +1474,7 @@ QUnit.module("Search", (hooks) => {
|
|||
assert.verifySteps(["search_panel_select_range", "search_panel_select_multi_range"]);
|
||||
|
||||
// reload with another domain, so the filters should be reloaded
|
||||
await toggleFilterMenu(target);
|
||||
await toggleSearchBarMenu(target);
|
||||
await toggleMenuItem(target, 0);
|
||||
|
||||
assert.verifySteps(["search_panel_select_multi_range"]);
|
||||
|
|
@ -1533,7 +1531,7 @@ QUnit.module("Search", (hooks) => {
|
|||
]);
|
||||
|
||||
// reload with another domain, so the categories 'state' and 'company_id' should be reloaded
|
||||
await toggleFilterMenu(target);
|
||||
await toggleSearchBarMenu(target);
|
||||
await toggleMenuItem(target, 0);
|
||||
|
||||
assert.verifySteps(["search_panel_select_range", "state"]);
|
||||
|
|
@ -1593,7 +1591,7 @@ QUnit.module("Search", (hooks) => {
|
|||
assert.deepEqual(getCategoriesContent(target), ["All", "ABC", "DEF", "GHI"]);
|
||||
|
||||
// reload with another domain, so the category 'state' should be reloaded
|
||||
await toggleFilterMenu(target);
|
||||
await toggleSearchBarMenu(target);
|
||||
await toggleMenuItem(target, 0);
|
||||
|
||||
assert.verifySteps([]);
|
||||
|
|
@ -3350,7 +3348,7 @@ QUnit.module("Search", (hooks) => {
|
|||
assert.verifySteps(["search_panel_select_range"]);
|
||||
|
||||
// select DEF in filter menu
|
||||
await toggleFilterMenu(target);
|
||||
await toggleSearchBarMenu(target);
|
||||
await toggleMenuItem(target, "DEF");
|
||||
|
||||
assert.verifySteps(["search_panel_select_range"]);
|
||||
|
|
@ -3401,7 +3399,7 @@ QUnit.module("Search", (hooks) => {
|
|||
assert.deepEqual(getCategoriesContent(target), ["All", "ABC", "DEF", "GHI"]);
|
||||
|
||||
// select DEF in filter menu --> the external domain changes --> the values should be updated
|
||||
await toggleFilterMenu(target);
|
||||
await toggleSearchBarMenu(target);
|
||||
await toggleMenuItem(target, "DEF");
|
||||
|
||||
assert.verifySteps(["search_panel_select_range"]);
|
||||
|
|
@ -3462,4 +3460,74 @@ QUnit.module("Search", (hooks) => {
|
|||
|
||||
assert.verifySteps(["special_key", "special_key"]);
|
||||
});
|
||||
|
||||
QUnit.test("Display message when no filter availible", async (assert) => {
|
||||
serverData.models.partner.records = [];
|
||||
serverData.models.company.records = [];
|
||||
serverData.models.category.records = [];
|
||||
|
||||
const { TestComponent } = makeTestComponent();
|
||||
await makeWithSearch({
|
||||
serverData,
|
||||
Component: TestComponent,
|
||||
resModel: "partner",
|
||||
searchViewId: false,
|
||||
});
|
||||
|
||||
assert.containsOnce(
|
||||
target,
|
||||
".o_search_panel_empty_state",
|
||||
"Search panel has the empty state container"
|
||||
);
|
||||
assert.containsN(
|
||||
target,
|
||||
".o_search_panel_empty_state button",
|
||||
1,
|
||||
"Empty state has the All button"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
"Don't display empty state message when some filters are availible",
|
||||
async (assert) => {
|
||||
const { TestComponent } = makeTestComponent();
|
||||
await makeWithSearch({
|
||||
serverData,
|
||||
Component: TestComponent,
|
||||
resModel: "partner",
|
||||
searchViewId: false,
|
||||
});
|
||||
|
||||
assert.containsNone(
|
||||
target,
|
||||
".o_search_panel_empty_state",
|
||||
"Search panel does not have the empty state container"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test("search panel with sample data", async (assert) => {
|
||||
serverData.models.partner.records = [];
|
||||
|
||||
serverData.views["partner,false,kanban"] = /* xml */ `
|
||||
<kanban sample="1">
|
||||
<templates>
|
||||
<div t-name="kanban-box" class="oe_kanban_global_click">
|
||||
<field name="foo"/>
|
||||
</div>
|
||||
</templates>
|
||||
</kanban>`;
|
||||
serverData.views["partner,false,list"] = /* xml */ `
|
||||
<tree sample="1">
|
||||
<field name="foo"/>
|
||||
</tree>`;
|
||||
|
||||
const webclient = await createWebClient({ serverData });
|
||||
await doAction(webclient, 1);
|
||||
|
||||
assert.deepEqual(getComputedStyle(getFilter(target, 0, "input")).pointerEvents, 'auto');
|
||||
|
||||
await switchView(target, "list");
|
||||
assert.deepEqual(getComputedStyle(getFilter(target, 0, "input")).pointerEvents, 'auto');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,25 +1,14 @@
|
|||
/** @odoo-module **/
|
||||
|
||||
import { constructDateDomain } from "@web/search/utils/dates";
|
||||
import { defaultLocalization } from "@web/../tests/helpers/mock_services";
|
||||
import { patchDate, patchTimeZone, patchWithCleanup } from "@web/../tests/helpers/utils";
|
||||
import { Domain } from "@web/core/domain";
|
||||
import { localization } from "@web/core/l10n/localization";
|
||||
import { patch, unpatch } from "@web/core/utils/patch";
|
||||
import { patchDate } from "@web/../tests/helpers/utils";
|
||||
import { registerCleanup } from "@web/../tests/helpers/cleanup";
|
||||
import { translatedTerms } from "@web/core/l10n/translation";
|
||||
import { constructDateDomain } from "@web/search/utils/dates";
|
||||
|
||||
const { DateTime } = luxon;
|
||||
|
||||
function patchTimeZone(offset) {
|
||||
const fixedZone = new luxon.FixedOffsetZone.instance(offset);
|
||||
const originalZone = luxon.Settings.defaultZone;
|
||||
luxon.Settings.defaultZone = fixedZone.name;
|
||||
registerCleanup(() => {
|
||||
luxon.Settings.defaultZone = originalZone;
|
||||
});
|
||||
}
|
||||
|
||||
QUnit.module("Search", () => {
|
||||
QUnit.module("SearchUtils", {
|
||||
beforeEach() {
|
||||
|
|
@ -497,7 +486,7 @@ QUnit.module("Search", () => {
|
|||
QUnit.test("Quarter option: custom translation", async function (assert) {
|
||||
patchDate(2020, 5, 1, 13, 0, 0);
|
||||
const referenceMoment = DateTime.local().setLocale("en");
|
||||
patch(translatedTerms, "add_translations", { Q2: "Deuxième trimestre de l'an de grâce" });
|
||||
patchWithCleanup(translatedTerms, { Q2: "Deuxième trimestre de l'an de grâce" });
|
||||
assert.deepEqual(
|
||||
constructDateDomain(referenceMoment, "date_field", "date", [
|
||||
"second_quarter",
|
||||
|
|
@ -511,17 +500,12 @@ QUnit.module("Search", () => {
|
|||
},
|
||||
"Quarter term should be translated"
|
||||
);
|
||||
unpatch(translatedTerms, "add_translations");
|
||||
});
|
||||
|
||||
QUnit.test("Quarter option: right to left", async function (assert) {
|
||||
patchDate(2020, 5, 1, 13, 0, 0);
|
||||
const referenceMoment = DateTime.local().setLocale("en");
|
||||
patch(
|
||||
localization,
|
||||
"rtl_localization",
|
||||
Object.assign({}, defaultLocalization, { direction: "rtl" })
|
||||
);
|
||||
patchWithCleanup(localization, { ...defaultLocalization, direction: "rtl" });
|
||||
assert.deepEqual(
|
||||
constructDateDomain(referenceMoment, "date_field", "date", [
|
||||
"second_quarter",
|
||||
|
|
@ -535,18 +519,13 @@ QUnit.module("Search", () => {
|
|||
},
|
||||
"Notation should be right to left"
|
||||
);
|
||||
unpatch(localization, "rtl_localization");
|
||||
});
|
||||
|
||||
QUnit.test("Quarter option: custom translation and right to left", async function (assert) {
|
||||
patchDate(2020, 5, 1, 13, 0, 0);
|
||||
const referenceMoment = DateTime.local().setLocale("en");
|
||||
patch(
|
||||
localization,
|
||||
"rtl_localization",
|
||||
Object.assign({}, defaultLocalization, { direction: "rtl" })
|
||||
);
|
||||
patch(translatedTerms, "add_translations", { Q2: "2e Trimestre" });
|
||||
patchWithCleanup(localization, { ...defaultLocalization, direction: "rtl" });
|
||||
patchWithCleanup(translatedTerms, { Q2: "2e Trimestre" });
|
||||
assert.deepEqual(
|
||||
constructDateDomain(referenceMoment, "date_field", "date", [
|
||||
"second_quarter",
|
||||
|
|
@ -560,34 +539,5 @@ QUnit.module("Search", () => {
|
|||
},
|
||||
"Quarter term should be translated and notation should be right to left"
|
||||
);
|
||||
unpatch(localization, "rtl_localization");
|
||||
unpatch(translatedTerms, "add_translations");
|
||||
});
|
||||
|
||||
QUnit.skip(
|
||||
"Moment.js localization does not affect formatted domain dates",
|
||||
async function (assert) {
|
||||
patchDate(2020, 5, 1, 13, 0, 0);
|
||||
const initialLocale = moment.locale();
|
||||
moment.defineLocale("addoneForTest", {
|
||||
postformat: function (string) {
|
||||
return string.replace(/\d/g, (match) => (1 + parseInt(match)) % 10);
|
||||
},
|
||||
});
|
||||
const referenceMoment = moment().locale("addoneForTest");
|
||||
assert.deepEqual(
|
||||
constructDateDomain(referenceMoment, "date_field", "date", [
|
||||
"this_month",
|
||||
"this_year",
|
||||
]),
|
||||
{
|
||||
domain: `["&", ["date_field", ">=", "2020-06-01"], ["date_field", "<=", "2020-06-30"]]`,
|
||||
description: "June 3131",
|
||||
},
|
||||
"Numbers in domain should not use addoneForTest locale"
|
||||
);
|
||||
moment.locale(initialLocale);
|
||||
moment.updateLocale("addoneForTest", null);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,20 +2,18 @@
|
|||
|
||||
import { makeTestEnv } from "@web/../tests/helpers/mock_env";
|
||||
import { getFixture, nextTick } from "@web/../tests/helpers/utils";
|
||||
import { FilterMenu } from "@web/search/filter_menu/filter_menu";
|
||||
import { GroupByMenu } from "@web/search/group_by_menu/group_by_menu";
|
||||
import { SearchBarMenu } from "@web/search/search_bar_menu/search_bar_menu";
|
||||
import { WithSearch } from "@web/search/with_search/with_search";
|
||||
import { mount } from "../helpers/utils";
|
||||
import {
|
||||
getMenuItemTexts,
|
||||
makeWithSearch,
|
||||
setupControlPanelServiceRegistry,
|
||||
toggleFilterMenu,
|
||||
toggleGroupByMenu,
|
||||
toggleSearchBarMenu,
|
||||
toggleMenuItem,
|
||||
} from "./helpers";
|
||||
|
||||
import { Component, onWillUpdateProps, onWillStart, useState, xml } from "@odoo/owl";
|
||||
import { Component, onWillUpdateProps, onWillStart, useState, xml, useSubEnv } from "@odoo/owl";
|
||||
|
||||
let target;
|
||||
let serverData;
|
||||
|
|
@ -235,11 +233,10 @@ QUnit.module("Search", (hooks) => {
|
|||
assert.expect(3);
|
||||
|
||||
class TestComponent extends Component {}
|
||||
TestComponent.components = { FilterMenu, GroupByMenu };
|
||||
TestComponent.components = { SearchBarMenu };
|
||||
TestComponent.template = xml`
|
||||
<div class="o_test_component">
|
||||
<FilterMenu/>
|
||||
<GroupByMenu/>
|
||||
<SearchBarMenu/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
|
@ -254,10 +251,10 @@ QUnit.module("Search", (hooks) => {
|
|||
Component: TestComponent,
|
||||
searchViewId: 1,
|
||||
});
|
||||
await toggleFilterMenu(target);
|
||||
await toggleSearchBarMenu(target);
|
||||
await assert.ok(getMenuItemTexts(target), ["True Domain"]);
|
||||
|
||||
await toggleGroupByMenu(target);
|
||||
await toggleSearchBarMenu(target);
|
||||
await assert.ok(getMenuItemTexts(target), ["Name"]);
|
||||
}
|
||||
);
|
||||
|
|
@ -269,18 +266,18 @@ QUnit.module("Search", (hooks) => {
|
|||
|
||||
class TestComponent extends Component {
|
||||
setup() {
|
||||
owl.onWillStart(() => {
|
||||
onWillStart(() => {
|
||||
assert.deepEqual(this.props.domain, []);
|
||||
});
|
||||
owl.onWillUpdateProps((nextProps) => {
|
||||
onWillUpdateProps((nextProps) => {
|
||||
assert.deepEqual(nextProps.domain, [[1, "=", 1]]);
|
||||
});
|
||||
}
|
||||
}
|
||||
TestComponent.components = { FilterMenu };
|
||||
TestComponent.components = { SearchBarMenu };
|
||||
TestComponent.template = xml`
|
||||
<div class="o_test_component">
|
||||
<FilterMenu/>
|
||||
<SearchBarMenu/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
|
@ -290,7 +287,7 @@ QUnit.module("Search", (hooks) => {
|
|||
Component: TestComponent,
|
||||
searchViewId: 1,
|
||||
});
|
||||
await toggleFilterMenu(target);
|
||||
await toggleSearchBarMenu(target);
|
||||
await toggleMenuItem(target, "True domain");
|
||||
}
|
||||
);
|
||||
|
|
@ -315,7 +312,7 @@ QUnit.module("Search", (hooks) => {
|
|||
|
||||
class Parent extends Component {
|
||||
setup() {
|
||||
owl.useSubEnv({ config: {} });
|
||||
useSubEnv({ config: {} });
|
||||
this.searchState = useState({
|
||||
resModel: "animal",
|
||||
domain: [["type", "=", "carnivorous"]],
|
||||
|
|
@ -363,7 +360,7 @@ QUnit.module("Search", (hooks) => {
|
|||
|
||||
class Parent extends Component {
|
||||
setup() {
|
||||
owl.useSubEnv({ config: {} });
|
||||
useSubEnv({ config: {} });
|
||||
this.searchState = useState({
|
||||
resModel: "animal",
|
||||
domain: [["type", "=", "carnivorous"]],
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue