import { after, expect, test } from "@odoo/hoot";
import { press, queryAllTexts } from "@odoo/hoot-dom";
import { Component, xml } from "@odoo/owl";
import {
contains,
defineModels,
editFavoriteName,
editSearch,
fields,
getFacetTexts,
mockService,
models,
mountWithSearch,
onRpc,
saveFavorite,
toggleSaveFavorite,
toggleSearchBarMenu,
validateSearch,
} from "@web/../tests/web_test_helpers";
import { SearchBar } from "@web/search/search_bar/search_bar";
import { SearchBarMenu } from "@web/search/search_bar_menu/search_bar_menu";
import { useSetupAction } from "@web/search/action_hook";
class Foo extends models.Model {
bar = fields.Many2one({ relation: "partner" });
birthday = fields.Date();
date_field = fields.Date();
float_field = fields.Float();
foo = fields.Char();
_views = {
search: ``,
};
}
class Partner extends models.Model {}
defineModels([Foo, Partner]);
test("simple rendering", async () => {
await mountWithSearch(
SearchBar,
{
resModel: "foo",
searchMenuTypes: ["favorite"],
searchViewId: false,
},
{
getDisplayName: () => "Action Name",
}
);
await toggleSearchBarMenu();
await toggleSaveFavorite();
expect(`.o_add_favorite + .o_accordion_values input[type="text"]`).toHaveValue("Action Name");
expect(`.o_add_favorite + .o_accordion_values input[type="checkbox"]`).toHaveCount(2);
expect(queryAllTexts(`.o_add_favorite + .o_accordion_values .form-check label`)).toEqual([
"Default filter",
"Shared",
]);
});
test("favorites use by default and share are exclusive", async () => {
await mountWithSearch(SearchBar, {
resModel: "foo",
searchMenuTypes: ["favorite"],
searchViewId: false,
});
await toggleSearchBarMenu();
await toggleSaveFavorite();
expect(`input[type="checkbox"]`).toHaveCount(2);
expect(`input[type="checkbox"]:checked`).toHaveCount(0);
await contains(`input[type="checkbox"]:eq(0)`).check();
expect(`input[type="checkbox"]:eq(0)`).toBeChecked();
expect(`input[type="checkbox"]:eq(1)`).not.toBeChecked();
await contains(`input[type="checkbox"]:eq(1)`).check();
expect(`input[type="checkbox"]:eq(0)`).not.toBeChecked();
expect(`input[type="checkbox"]:eq(1)`).toBeChecked();
await contains(`input[type="checkbox"]:eq(0)`).check();
expect(`input[type="checkbox"]:eq(0)`).toBeChecked();
expect(`input[type="checkbox"]:eq(1)`).not.toBeChecked();
await contains(`input[type="checkbox"]:eq(0)`).uncheck();
expect(`input[type="checkbox"]:eq(0)`).not.toBeChecked();
expect(`input[type="checkbox"]:eq(1)`).not.toBeChecked();
});
test("save filter", async () => {
class TestComponent extends Component {
static components = { SearchBarMenu };
static template = xml`
`;
static props = ["*"];
setup() {
useSetupAction({
getContext: () => {
return { someKey: "foo" };
},
});
}
}
onRpc("create_or_replace", ({ args, route }) => {
expect.step(route);
const irFilter = args[0];
expect(irFilter.context).toEqual({ group_by: [], someKey: "foo" });
return 7; // fake serverSideId
});
const component = await mountWithSearch(TestComponent, {
resModel: "foo",
context: { someOtherKey: "bar" }, // should not end up in filter's context
searchViewId: false,
});
const clearCacheListener = () => expect.step("CLEAR-CACHES");
component.env.bus.addEventListener("CLEAR-CACHES", clearCacheListener);
after(() => component.env.bus.removeEventListener("CLEAR-CACHES", clearCacheListener));
expect.verifySteps([]);
await toggleSearchBarMenu();
await toggleSaveFavorite();
await editFavoriteName("aaa");
await saveFavorite();
expect.verifySteps(["/web/dataset/call_kw/ir.filters/create_or_replace", "CLEAR-CACHES"]);
});
test("dynamic filters are saved dynamic", async () => {
onRpc("create_or_replace", ({ args, route }) => {
expect.step(route);
const irFilter = args[0];
expect(irFilter.domain).toBe(
`[("date_field", ">=", (context_today() + relativedelta()).strftime("%Y-%m-%d"))]`
);
return 7; // fake serverSideId
});
await mountWithSearch(SearchBar, {
resModel: "foo",
context: { search_default_filter: 1 },
searchMenuTypes: ["filter", "favorite"],
searchViewId: false,
searchViewArch: `
`,
});
expect(getFacetTexts()).toEqual(["Filter"]);
await toggleSearchBarMenu();
await toggleSaveFavorite();
await editFavoriteName("My favorite");
await saveFavorite();
expect(getFacetTexts()).toEqual(["My favorite"]);
expect.verifySteps(["/web/dataset/call_kw/ir.filters/create_or_replace"]);
});
test("save filters created via autocompletion works", async () => {
onRpc("create_or_replace", ({ args, route }) => {
expect.step(route);
const irFilter = args[0];
expect(irFilter.domain).toBe(`[("foo", "ilike", "a")]`);
return 7; // fake serverSideId
});
await mountWithSearch(SearchBar, {
resModel: "foo",
searchMenuTypes: ["favorite"],
searchViewId: false,
searchViewArch: ``,
});
expect(getFacetTexts()).toEqual([]);
await editSearch("a");
await validateSearch();
expect(getFacetTexts()).toEqual(["Foo\na"]);
await toggleSearchBarMenu();
await toggleSaveFavorite();
await editFavoriteName("My favorite");
await saveFavorite();
expect(getFacetTexts()).toEqual(["My favorite"]);
expect.verifySteps(["/web/dataset/call_kw/ir.filters/create_or_replace"]);
});
test("favorites have unique descriptions (the submenus of the favorite menu are correctly updated)", async () => {
mockService("notification", {
add(message, options) {
expect.step("notification");
expect(message).toBe("A filter with same name already exists.");
expect(options).toEqual({ type: "danger" });
},
});
onRpc("create_or_replace", ({ args, route }) => {
expect.step(route);
expect(args[0]).toEqual({
action_id: false,
context: { group_by: [] },
domain: `[]`,
is_default: false,
model_id: "foo",
name: "My favorite 2",
sort: `[]`,
embedded_action_id: false,
embedded_parent_res_id: false,
user_id: 7,
});
return 2; // fake serverSideId
});
await mountWithSearch(SearchBar, {
resModel: "foo",
searchMenuTypes: ["favorite"],
searchViewId: false,
irFilters: [
{
context: "{}",
domain: "[]",
id: 1,
is_default: false,
name: "My favorite",
sort: "[]",
user_id: [2, "Mitchell Admin"],
},
],
});
await toggleSearchBarMenu();
await toggleSaveFavorite();
// first try: should fail
await editFavoriteName("My favorite");
await saveFavorite();
expect.verifySteps(["notification"]);
// second try: should succeed
await editFavoriteName("My favorite 2");
await saveFavorite();
expect.verifySteps(["/web/dataset/call_kw/ir.filters/create_or_replace"]);
// third try: should fail
await editFavoriteName("My favorite 2");
await saveFavorite();
expect.verifySteps(["notification"]);
});
test("undefined name for filter shows notification and not error", async () => {
mockService("notification", {
add(message, options) {
expect.step("notification");
expect(message).toBe("A name for your favorite filter is required.");
expect(options).toEqual({ type: "danger" });
},
});
onRpc("create_or_replace", () => 7); // fake serverSideId
await mountWithSearch(SearchBarMenu, {
resModel: "foo",
searchViewId: false,
});
await toggleSearchBarMenu();
await toggleSaveFavorite();
await saveFavorite();
expect.verifySteps(["notification"]);
});
test("add favorite with enter which already exists", async () => {
mockService("notification", {
add(message, options) {
expect.step("notification");
expect(message).toBe("A name for your favorite filter is required.");
expect(options).toEqual({ type: "danger" });
},
});
await mountWithSearch(SearchBarMenu, {
resModel: "foo",
searchViewId: false,
irFilters: [
{
context: "{}",
domain: "[]",
id: 1,
is_default: false,
name: "My favorite",
sort: "[]",
user_id: [2, "Mitchell Admin"],
},
],
});
await toggleSearchBarMenu();
await toggleSaveFavorite();
await editFavoriteName("My favorite");
await press("Enter");
expect.verifySteps(["notification"]);
});