Odoo18-Base/addons/web/static/tests/search/search_bar_tests.js
2025-03-10 10:52:11 +07:00

1764 lines
66 KiB
JavaScript

/** @odoo-module **/
import * as dsHelpers from "@web/../tests/core/domain_selector_tests";
import {
click,
getFixture,
getNodesTextContent,
makeDeferred,
nextTick,
patchDate,
patchTimeZone,
triggerEvent,
} from "../helpers/utils";
import {
editSearch,
getFacetTexts,
makeWithSearch,
removeFacet,
setupControlPanelServiceRegistry,
toggleMenuItem,
toggleSearchBarMenu,
validateSearch,
} from "./helpers";
function getContext(controlPanel) {
return controlPanel.env.searchModel.context;
}
function getDomain(controlPanel) {
return controlPanel.env.searchModel.domain;
}
import { Component, onWillUpdateProps, xml } from "@odoo/owl";
import { SearchBar } from "@web/search/search_bar/search_bar";
let target;
let serverData;
QUnit.module("Search", (hooks) => {
hooks.beforeEach(async () => {
serverData = {
models: {
partner: {
fields: {
bar: { string: "Bar", type: "many2one", relation: "partner" },
birthday: { string: "Birthday", type: "date" },
birth_datetime: { string: "Birth DateTime", type: "datetime" },
foo: { string: "Foo", type: "char" },
bool: { string: "Bool", type: "boolean" },
company: { string: "Company", type: "many2one", relation: "partner" },
properties: {
string: "Properties",
type: "properties",
definition_record: "bar",
definition_record_field: "child_properties",
},
child_properties: { type: "properties_definition" },
},
records: [
{
id: 1,
display_name: "First record",
foo: "yop",
bar: 2,
bool: true,
birthday: "1983-07-15",
birth_datetime: "1983-07-15 01:00:00",
},
{
id: 2,
display_name: "Second record",
foo: "blip",
bar: 1,
bool: false,
birthday: "1982-06-04",
birth_datetime: "1982-06-04 02:00:00",
company: 1,
},
{
id: 3,
display_name: "Third record",
foo: "gnap",
bar: 1,
bool: false,
birthday: "1985-09-13",
birth_datetime: "1985-09-13 03:00:00",
company: 5,
},
{
id: 4,
display_name: "Fourth record",
foo: "plop",
bar: 2,
bool: true,
birthday: "1983-05-05",
birth_datetime: "1983-05-05 04:00:00",
},
{
id: 5,
display_name: "Fifth record",
foo: "zoup",
bar: 2,
bool: true,
birthday: "1800-01-01",
birth_datetime: "1800-01-01 05:00:00",
},
],
},
},
views: {
"partner,false,list": `<tree><field name="foo"/></tree>`,
"partner,false,search": `
<search>
<field name="foo"/>
<field name="birthday"/>
<field name="birth_datetime"/>
<field name="bar" context="{'bar': self}"/>
<field name="company" domain="[('bool', '=', True)]"/>
<filter string="Birthday" name="date_filter" date="birthday"/>
<filter string="Birthday" name="date_group_by" context="{'group_by': 'birthday:day'}"/>
</search>
`,
},
actions: {
1: {
id: 1,
name: "Partners Action",
res_model: "partner",
search_view_id: [false, "search"],
type: "ir.actions.act_window",
views: [[false, "list"]],
},
},
};
setupControlPanelServiceRegistry();
target = getFixture();
});
QUnit.module("SearchBar");
QUnit.test("basic rendering", async function (assert) {
assert.expect(1);
await makeWithSearch({
serverData,
resModel: "partner",
Component: SearchBar,
searchMenuTypes: [],
searchViewId: false,
});
assert.strictEqual(
document.activeElement,
target.querySelector(".o_searchview input"),
"searchview input should be focused"
);
});
QUnit.test("navigation with facets", async function (assert) {
assert.expect(4);
await makeWithSearch({
serverData,
resModel: "partner",
Component: SearchBar,
searchMenuTypes: ["groupBy"],
searchViewId: false,
context: { search_default_date_group_by: 1 },
});
assert.containsOnce(
target,
".o_searchview .o_searchview_facet",
"there should be one facet"
);
assert.strictEqual(document.activeElement, target.querySelector(".o_searchview input"));
// press left to focus the facet
await triggerEvent(document.activeElement, null, "keydown", { key: "ArrowLeft" });
assert.strictEqual(
document.activeElement,
target.querySelector(".o_searchview .o_searchview_facet")
);
// press right to focus the input
await triggerEvent(document.activeElement, null, "keydown", { key: "ArrowRight" });
assert.strictEqual(document.activeElement, target.querySelector(".o_searchview input"));
});
QUnit.test("navigation with facets (2)", async function (assert) {
await makeWithSearch({
serverData,
resModel: "partner",
Component: SearchBar,
searchMenuTypes: ["groupBy"],
searchViewId: false,
context: {
search_default_date_group_by: 1,
search_default_foo: 1,
},
});
assert.containsN(target, ".o_searchview .o_searchview_facet", 2);
assert.strictEqual(document.activeElement, target.querySelector(".o_searchview input"));
// press left to focus the rightmost facet
await triggerEvent(document.activeElement, null, "keydown", { key: "ArrowLeft" });
assert.strictEqual(
document.activeElement,
target.querySelector(".o_searchview .o_searchview_facet:nth-child(2)")
);
// press left to focus the leftmost facet
await triggerEvent(document.activeElement, null, "keydown", { key: "ArrowLeft" });
assert.strictEqual(
document.activeElement,
target.querySelector(".o_searchview .o_searchview_facet:nth-child(1)")
);
// press left to focus the input
await triggerEvent(document.activeElement, null, "keydown", { key: "ArrowLeft" });
assert.strictEqual(document.activeElement, target.querySelector(".o_searchview input"));
// press left to focus the leftmost facet
await triggerEvent(document.activeElement, null, "keydown", { key: "ArrowRight" });
assert.strictEqual(
document.activeElement,
target.querySelector(".o_searchview .o_searchview_facet:nth-child(1)")
);
});
QUnit.test("search date and datetime fields. Support of timezones", async function (assert) {
assert.expect(4);
patchTimeZone(360);
const controlPanel = await makeWithSearch({
serverData,
resModel: "partner",
Component: SearchBar,
searchMenuTypes: [],
searchViewId: false,
});
// Date case
await editSearch(target, "07/15/1983");
let searchInput = target.querySelector(".o_searchview input");
await triggerEvent(searchInput, null, "keydown", { key: "ArrowDown" });
await triggerEvent(searchInput, null, "keydown", { key: "Enter" }); // select
assert.deepEqual(
getFacetTexts(target).map((str) => str.replace(/\s+/g, " ")),
["Birthday 07/15/1983"],
"The format of the date in the facet should be in locale"
);
assert.deepEqual(getDomain(controlPanel), [["birthday", "=", "1983-07-15"]]);
// Close Facet
await click(target.querySelector(".o_searchview_facet .o_facet_remove"));
// DateTime case
await editSearch(target, "07/15/1983 00:00:00");
searchInput = target.querySelector(".o_searchview input");
await triggerEvent(searchInput, null, "keydown", { key: "ArrowDown" });
await triggerEvent(searchInput, null, "keydown", { key: "Enter" }); // select
assert.deepEqual(
getFacetTexts(target).map((str) => str.replace(/\s+/g, " ")),
["Birth DateTime 07/15/1983 00:00:00"],
"The format of the datetime in the facet should be in locale"
);
assert.deepEqual(getDomain(controlPanel), [["birth_datetime", "=", "1983-07-14 18:00:00"]]);
});
QUnit.test("autocomplete menu clickout interactions", async function (assert) {
assert.expect(10);
await makeWithSearch({
serverData,
resModel: "partner",
Component: SearchBar,
searchMenuTypes: [],
searchViewId: false,
searchViewArch: `
<search>
<field name="bar"/>
<field name="birthday"/>
<field name="birth_datetime"/>
<field name="foo"/>
<field name="bool"/>
</search>
`,
});
const input = target.querySelector(".o_searchview input");
// Create an input outside of the search panel to simulate another input outside of the search panel
const outsideInput = document.createElement('input');
getFixture().appendChild(outsideInput);
assert.containsNone(target, ".o_searchview_autocomplete");
await editSearch(target, "Hello there");
assert.strictEqual(input.value, "Hello there", "input value should be updated");
assert.containsOnce(target, ".o_searchview_autocomplete");
await triggerEvent(input, null, "keydown", { key: "Escape" });
assert.strictEqual(input.value, "", "input value should be empty");
assert.containsNone(target, ".o_searchview_autocomplete");
await editSearch(target, "General Kenobi");
assert.strictEqual(input.value, "General Kenobi", "input value should be updated");
assert.containsOnce(target, ".o_searchview_autocomplete");
outsideInput.focus();
await click(outsideInput);
assert.strictEqual(input.value, "", "input value should be empty");
assert.containsNone(target, ".o_searchview_autocomplete");
assert.strictEqual(document.activeElement, outsideInput);
});
QUnit.test("select an autocomplete field", async function (assert) {
assert.expect(3);
const controlPanel = await makeWithSearch({
serverData,
resModel: "partner",
Component: SearchBar,
searchMenuTypes: [],
searchViewId: false,
});
await editSearch(target, "a");
assert.containsN(
target,
".o_searchview_autocomplete li",
3,
"there should be 3 result for 'a' in search bar autocomplete"
);
const searchInput = target.querySelector(".o_searchview input");
await triggerEvent(searchInput, null, "keydown", { key: "Enter" });
assert.strictEqual(
target.querySelector(".o_searchview_input_container .o_facet_values").innerText.trim(),
"a",
"There should be a field facet with label 'a'"
);
assert.deepEqual(getDomain(controlPanel), [["foo", "ilike", "a"]]);
});
QUnit.test("select an autocomplete field with `context` key", async function (assert) {
assert.expect(8);
let updateCount = 0;
class TestComponent extends Component {
setup() {
onWillUpdateProps(() => {
updateCount++;
});
}
}
TestComponent.template = xml`<SearchBar/>`;
TestComponent.components = { SearchBar };
const controlPanel = await makeWithSearch({
serverData,
resModel: "partner",
Component: TestComponent,
searchMenuTypes: [],
searchViewId: false,
});
// 'r' key to filter on bar "First Record"
await editSearch(target, "record");
const searchInput = target.querySelector(".o_searchview input");
await triggerEvent(searchInput, null, "keydown", { key: "ArrowDown" });
await triggerEvent(searchInput, null, "keydown", { key: "ArrowRight" });
await triggerEvent(searchInput, null, "keydown", { key: "ArrowDown" });
await triggerEvent(searchInput, null, "keydown", { key: "Enter" });
assert.deepEqual(
getFacetTexts(target).map((str) => str.replace(/\s+/g, " ")),
["Bar First record"]
);
assert.strictEqual(updateCount, 1);
assert.deepEqual(getDomain(controlPanel), [["bar", "=", 1]]);
assert.deepEqual(controlPanel.env.searchModel.context.bar, [1]);
// 'r' key to filter on bar "Second Record"
await editSearch(target, "record");
await triggerEvent(searchInput, null, "keydown", { key: "ArrowDown" });
await triggerEvent(searchInput, null, "keydown", { key: "ArrowRight" });
await triggerEvent(searchInput, null, "keydown", { key: "ArrowDown" });
await triggerEvent(searchInput, null, "keydown", { key: "ArrowDown" });
await triggerEvent(searchInput, null, "keydown", { key: "Enter" });
assert.deepEqual(
getFacetTexts(target).map((str) => str.replace(/\s+/g, " ")),
["Bar First record or Second record"]
);
assert.strictEqual(updateCount, 2);
assert.deepEqual(getDomain(controlPanel), ["|", ["bar", "=", 1], ["bar", "=", 2]]);
assert.deepEqual(controlPanel.env.searchModel.context.bar, [1, 2]);
});
QUnit.test("no search text triggers a reload", async function (assert) {
assert.expect(2);
let updateCount = 0;
class TestComponent extends Component {
setup() {
onWillUpdateProps(() => {
updateCount++;
});
}
}
TestComponent.template = xml`<SearchBar/>`;
TestComponent.components = { SearchBar };
await makeWithSearch({
serverData,
resModel: "partner",
Component: TestComponent,
searchMenuTypes: [],
searchViewId: false,
});
const searchInput = target.querySelector(".o_searchview input");
await triggerEvent(searchInput, null, "keydown", { key: "Enter" });
assert.containsNone(target, ".o_searchview_facet_label");
assert.strictEqual(updateCount, 1, "should have been updated once");
});
QUnit.test("selecting (no result) triggers a search bar rendering", async function (assert) {
assert.expect(3);
await makeWithSearch({
serverData,
resModel: "partner",
Component: SearchBar,
searchMenuTypes: [],
searchViewId: false,
searchViewArch: `
<search>
<field name="bar"/>
</search>
`,
});
await editSearch(target, "hello there");
// 'a' key to filter nothing on bar
const searchInput = target.querySelector(".o_searchview input");
await triggerEvent(searchInput, null, "keydown", { key: "ArrowDown" });
await triggerEvent(searchInput, null, "keydown", { key: "ArrowRight" });
await triggerEvent(searchInput, null, "keydown", { key: "ArrowDown" });
assert.strictEqual(
target.querySelector(".o_searchview_autocomplete .focus").innerText.trim(),
"(no result)",
"there should be no result for 'a' in bar"
);
await triggerEvent(searchInput, null, "keydown", { key: "Enter" });
assert.containsNone(target, ".o_searchview_facet_label");
assert.strictEqual(
target.querySelector(".o_searchview input").value,
"",
"the search input should be re-rendered"
);
});
QUnit.test(
"update suggested filters in autocomplete menu with Japanese IME",
async function (assert) {
assert.expect(4);
// The goal here is to simulate as many events happening during an IME
// assisted composition session as possible. Some of these events are
// not handled but are triggered to ensure they do not interfere.
const TEST = "TEST";
const テスト = "テスト";
await makeWithSearch({
serverData,
resModel: "partner",
Component: SearchBar,
searchMenuTypes: [],
searchViewId: false,
});
const searchInput = target.querySelector(".o_searchview input");
// Simulate typing "TEST" on search view.
for (let i = 0; i < TEST.length; i++) {
const key = TEST[i].toUpperCase();
await triggerEvent(searchInput, null, "keydown", {
key,
isComposing: true,
});
if (i === 0) {
// Composition is initiated after the first keydown
await triggerEvent(searchInput, null, "compositionstart");
}
await triggerEvent(searchInput, null, "keypress", {
key,
isComposing: true,
});
searchInput.value = TEST.slice(0, i + 1);
await triggerEvent(searchInput, null, "keyup", { key, isComposing: true });
await triggerEvent(searchInput, null, "input", {
inputType: "insertCompositionText",
isComposing: true,
});
}
assert.containsOnce(
target,
".o_searchview_autocomplete",
"should display autocomplete dropdown menu on typing something in search view"
);
assert.strictEqual(
target.querySelector(".o_searchview_autocomplete li").innerText.trim(),
"Search Foo for: TEST",
`1st filter suggestion should be based on typed word "TEST"`
);
// Simulate soft-selection of another suggestion from IME through keyboard navigation.
await triggerEvent(searchInput, null, "keydown", {
key: "ArrowDown",
isComposing: true,
});
await triggerEvent(searchInput, null, "keypress", {
key: "ArrowDown",
isComposing: true,
});
searchInput.value = テスト;
await triggerEvent(searchInput, null, "keyup", {
key: "ArrowDown",
isComposing: true,
});
await triggerEvent(searchInput, null, "input", {
inputType: "insertCompositionText",
isComposing: true,
});
assert.strictEqual(
target.querySelector(".o_searchview_autocomplete li").innerText.trim(),
"Search Foo for: テスト",
`1st filter suggestion should be updated with soft-selection typed word "テスト"`
);
// Simulate selection on suggestion item "TEST" from IME.
await triggerEvent(searchInput, null, "keydown", {
key: "Enter",
isComposing: true,
});
await triggerEvent(searchInput, null, "keypress", {
key: "Enter",
isComposing: true,
});
searchInput.value = TEST;
await triggerEvent(searchInput, null, "keyup", {
key: "Enter",
isComposing: true,
});
await triggerEvent(searchInput, null, "input", {
inputType: "insertCompositionText",
isComposing: true,
});
// End of the composition
await triggerEvent(searchInput, null, "compositionend");
assert.strictEqual(
target.querySelector(".o_searchview_autocomplete li").innerText.trim(),
"Search Foo for: TEST",
`1st filter suggestion should finally be updated with click selection on word "TEST" from IME`
);
}
);
QUnit.test("open search view autocomplete on paste value using mouse", async function (assert) {
assert.expect(1);
await makeWithSearch({
serverData,
resModel: "partner",
Component: SearchBar,
searchMenuTypes: [],
searchViewId: false,
});
// Simulate paste text through the mouse.
const searchInput = target.querySelector(".o_searchview input");
searchInput.value = "ABC";
await triggerEvent(searchInput, null, "input", { inputType: "insertFromPaste" });
assert.containsOnce(
target,
".o_searchview_autocomplete",
"should display autocomplete dropdown menu on paste in search view"
);
});
QUnit.test("select autocompleted many2one", async function (assert) {
assert.expect(4);
const controlPanel = await makeWithSearch({
serverData,
resModel: "partner",
Component: SearchBar,
searchMenuTypes: [],
searchViewId: false,
searchViewArch: `
<search>
<field name="foo"/>
<field name="birthday"/>
<field name="birth_datetime"/>
<field name="bar" operator="child_of"/>
</search>
`,
});
assert.deepEqual(getDomain(controlPanel), []);
await editSearch(target, "rec");
await click(target.querySelector(".o_searchview_autocomplete li:last-child"));
assert.deepEqual(getDomain(controlPanel), [["bar", "child_of", "rec"]]);
await removeFacet(target);
assert.deepEqual(getDomain(controlPanel), []);
await editSearch(target, "rec");
await click(target.querySelector(".o_expand"));
await click(target.querySelector(".o_searchview_autocomplete li.o_menu_item.o_indent"));
assert.deepEqual(getDomain(controlPanel), [["bar", "child_of", 1]]);
});
QUnit.test('"null" as autocomplete value', async function (assert) {
assert.expect(3);
const controlPanel = await makeWithSearch({
serverData,
resModel: "partner",
Component: SearchBar,
searchMenuTypes: [],
searchViewId: false,
});
assert.deepEqual(getDomain(controlPanel), []);
await editSearch(target, "null");
assert.strictEqual(
target.querySelector(".o_searchview_autocomplete .focus").innerText,
"Search Foo for: null"
);
await click(target.querySelector(".o_searchview_autocomplete li.focus a"));
assert.deepEqual(getDomain(controlPanel), [["foo", "ilike", "null"]]);
});
QUnit.test("autocompletion with a boolean field", async function (assert) {
assert.expect(8);
const controlPanel = await makeWithSearch({
serverData,
resModel: "partner",
Component: SearchBar,
searchMenuTypes: [],
searchViewId: false,
searchViewArch: `
<search>
<field name="bool"/>
</search>
`,
});
assert.deepEqual(getDomain(controlPanel), []);
await editSearch(target, "y");
assert.containsN(target, ".o_searchview_autocomplete li", 1);
assert.strictEqual(
target.querySelector(".o_searchview_autocomplete li:last-child").innerText,
"Search Bool for: Yes"
);
// select "Yes"
await click(target.querySelector(".o_searchview_autocomplete li:last-child"));
assert.deepEqual(getDomain(controlPanel), [["bool", "=", true]]);
await removeFacet(target);
assert.deepEqual(getDomain(controlPanel), []);
await editSearch(target, "No");
assert.containsN(target, ".o_searchview_autocomplete li", 1);
assert.strictEqual(
target.querySelector(".o_searchview_autocomplete li:last-child").innerText,
"Search Bool for: No"
);
// select "No"
await click(target.querySelector(".o_searchview_autocomplete li:last-child"));
assert.deepEqual(getDomain(controlPanel), [["bool", "=", false]]);
});
QUnit.test("the search value is trimmed to remove unnecessary spaces", async function (assert) {
const controlPanel = await makeWithSearch({
serverData,
resModel: "partner",
Component: SearchBar,
searchMenuTypes: [],
searchViewId: false,
searchViewArch: `
<search>
<field name="foo" filter_domain="[('foo', 'ilike', self)]"/>
</search>
`,
});
await editSearch(target, "bar");
await validateSearch(target);
assert.deepEqual(getDomain(controlPanel), [["foo", "ilike", "bar"]]);
await removeFacet(target);
assert.deepEqual(getDomain(controlPanel), []);
await editSearch(target, " bar ");
await validateSearch(target);
assert.deepEqual(
getDomain(controlPanel),
[["foo", "ilike", "bar"]],
"the value has been trimmed"
);
});
QUnit.test("reference fields are supported in search view", async function (assert) {
assert.expect(4);
const partnerModel = serverData.models.partner;
partnerModel.fields.ref = { type: "reference", string: "Reference" };
partnerModel.records.forEach((record, i) => {
record.ref = `ref${String(i).padStart(3, "0")}`;
});
const controlPanel = await makeWithSearch({
serverData,
resModel: "partner",
Component: SearchBar,
searchMenuTypes: [],
searchViewId: false,
searchViewArch: `
<search>
<field name="ref"/>
</search>
`,
});
assert.deepEqual(getDomain(controlPanel), []);
await editSearch(target, "ref");
await validateSearch(target);
assert.deepEqual(getDomain(controlPanel), [["ref", "ilike", "ref"]]);
await removeFacet(target);
assert.deepEqual(getDomain(controlPanel), []);
await editSearch(target, "ref002");
await validateSearch(target);
assert.deepEqual(getDomain(controlPanel), [["ref", "ilike", "ref002"]]);
});
QUnit.test(
"expand an asynchronous menu and change the selected item with the mouse during expansion",
async function (assert) {
assert.expect(2);
const def = makeDeferred();
const mockRPC = async (route) => {
if (route.includes("/partner/name_search")) {
await def;
}
};
await makeWithSearch({
serverData,
mockRPC,
resModel: "partner",
Component: SearchBar,
searchMenuTypes: [],
searchViewId: false,
searchViewArch: `
<search>
<field name="bar" operator="child_of"/>
</search>
`,
});
await editSearch(target, "rec");
await click(target.querySelector(".o_expand"));
await triggerEvent(
target,
".o_searchview_autocomplete li.o_menu_item:first-child",
"mousemove"
);
assert.containsNone(target, ".o_searchview_autocomplete li.o_menu_item.o_indent");
def.resolve();
await nextTick();
assert.containsN(target, ".o_searchview_autocomplete li.o_menu_item.o_indent", 5);
}
);
QUnit.test(
"expand an asynchronous menu and change the selected item with the arrow during expansion",
async function (assert) {
assert.expect(2);
const def = makeDeferred();
const mockRPC = async (route) => {
if (route.includes("/partner/name_search")) {
await def;
}
};
await makeWithSearch({
serverData,
mockRPC,
resModel: "partner",
Component: SearchBar,
searchMenuTypes: [],
searchViewId: false,
searchViewArch: `
<search>
<field name="bar" operator="child_of"/>
</search>
`,
});
await editSearch(target, "rec");
await click(target.querySelector(".o_expand"));
const searchInput = target.querySelector(".o_searchview input");
await triggerEvent(searchInput, null, "keydown", { key: "ArrowDown" });
assert.containsNone(target, ".o_searchview_autocomplete li.o_menu_item.o_indent");
def.resolve();
await nextTick();
assert.containsN(target, ".o_searchview_autocomplete li.o_menu_item.o_indent", 5);
}
);
QUnit.test("checks that an arrowDown always selects an item", async function (assert) {
assert.expect(1);
await makeWithSearch({
serverData,
resModel: "partner",
Component: SearchBar,
searchMenuTypes: [],
searchViewId: false,
searchViewArch: `
<search>
<field name="bar" operator="child_of"/>
</search>
`,
});
await editSearch(target, "rec");
await click(target.querySelector(".o_expand"));
click(target.querySelector(".o_expand"));
triggerEvent(
target,
".o_searchview_autocomplete li.o_menu_item.o_indent:last-child",
"mousemove"
);
const searchInput = target.querySelector(".o_searchview input");
await triggerEvent(searchInput, null, "keydown", { key: "ArrowDown" });
assert.containsOnce(target, ".focus");
});
QUnit.test("checks that an arrowUp always selects an item", async function (assert) {
assert.expect(1);
await makeWithSearch({
serverData,
resModel: "partner",
Component: SearchBar,
searchMenuTypes: [],
searchViewId: false,
searchViewArch: `
<search>
<field name="bar" operator="child_of"/>
</search>
`,
});
await editSearch(target, "rec");
await click(target.querySelector(".o_expand"));
click(target.querySelector(".o_expand"));
triggerEvent(
target,
".o_searchview_autocomplete li.o_menu_item.o_indent:last-child",
"mousemove"
);
const searchInput = target.querySelector(".o_searchview input");
await triggerEvent(searchInput, null, "keydown", { key: "ArrowUp" });
assert.containsOnce(target, ".focus");
});
QUnit.test("many2one_reference fields are supported in search view", async function (assert) {
serverData.models.partner.fields.res_id = {
string: "Resource ID",
type: "many2one_reference",
};
const controlPanel = await makeWithSearch({
serverData,
resModel: "partner",
Component: SearchBar,
searchMenuTypes: [],
searchViewId: false,
searchViewArch: /*xml*/ `
<search>
<field name="foo" />
<field name="res_id" />
</search>
`,
});
assert.deepEqual(getDomain(controlPanel), []);
await editSearch(target, "12");
assert.deepEqual(
[...target.querySelectorAll(".o_searchview ul li.dropdown-item")].map(
(el) => el.innerText
),
["Search Foo for: 12", "Search Resource ID for: 12"]
);
await triggerEvent(target.querySelector(".o_searchview input"), null, "keydown", {
key: "ArrowDown",
});
await validateSearch(target);
assert.deepEqual(getDomain(controlPanel), [["res_id", "=", 12]]);
await removeFacet(target);
assert.deepEqual(getDomain(controlPanel), []);
await editSearch(target, "1a");
assert.deepEqual(
[...target.querySelectorAll(".o_searchview ul li.dropdown-item")].map(
(el) => el.innerText
),
["Search Foo for: 1a"]
);
await validateSearch(target);
assert.deepEqual(getDomain(controlPanel), [["foo", "ilike", "1a"]]);
});
QUnit.test("check kwargs of a rpc call with a domain", async function (assert) {
assert.expect(3);
const mockRPC = async (route, args) => {
if (route.includes("/partner/name_search")) {
assert.deepEqual(args, {
model: "partner",
method: "name_search",
args: [],
kwargs: {
args: [["bool", "=", true]],
context: { lang: "en", uid: 7, tz: "taht" },
limit: 8,
name: "F",
},
});
}
};
const controlPanel = await makeWithSearch({
serverData,
mockRPC,
resModel: "partner",
Component: SearchBar,
searchMenuTypes: [],
searchViewId: false,
});
await editSearch(target, "F");
assert.containsN(
target,
".o_searchview_autocomplete li",
3,
"there should be 3 result for 'F' in search bar autocomplete"
);
const searchInput = target.querySelector(".o_searchview input");
await triggerEvent(searchInput, null, "keydown", { key: "ArrowDown" });
await triggerEvent(searchInput, null, "keydown", { key: "ArrowDown" });
await triggerEvent(searchInput, null, "keydown", { key: "ArrowRight" });
await triggerEvent(searchInput, null, "keydown", { key: "ArrowDown" });
await triggerEvent(searchInput, null, "keydown", { key: "ArrowDown" });
await triggerEvent(searchInput, null, "keydown", { key: "ArrowDown" });
await triggerEvent(searchInput, null, "keydown", { key: "Enter" });
assert.deepEqual(getDomain(controlPanel), [["company", "=", 5]]);
});
QUnit.test("should wait label promises for one2many search defaults", async function (assert) {
assert.expect(3);
const target = getFixture();
const def = makeDeferred();
const mockRPC = async (_, args) => {
if (args.method === "read") {
await def;
}
};
makeWithSearch({
serverData,
mockRPC,
resModel: "partner",
Component: SearchBar,
searchMenuTypes: [],
searchViewId: false,
context: { search_default_company: 1 },
});
await nextTick();
assert.containsNone(target, ".o_cp_searchview");
def.resolve();
await nextTick();
assert.containsOnce(target, ".o_cp_searchview");
assert.strictEqual(getFacetTexts(target)[0].replace("\n", ""), "CompanyFirst record");
});
QUnit.test("globalContext keys in name_search", async function (assert) {
assert.expect(1);
await makeWithSearch({
serverData,
resModel: "partner",
Component: SearchBar,
searchMenuTypes: [],
searchViewId: false,
searchViewArch: `
<search>
<field name="company"/>
</search>
`,
context: { specialKey: "ABCD" },
mockRPC(_, args) {
if (args.method === "name_search") {
assert.strictEqual(args.kwargs.context.specialKey, "ABCD");
}
},
});
await editSearch(target, "F");
await triggerEvent(target, ".o_searchview input", "keydown", { key: "ArrowRight" });
});
QUnit.test("search a property", async function (assert) {
assert.expect(57);
async function mockRPC(_, { method, model, kwargs }) {
if (
method === "web_search_read" &&
model === "partner" &&
kwargs.specification.display_name &&
kwargs.specification.child_properties
) {
const definition1 = [
{
type: "many2one",
string: "My Partner",
name: "my_partner",
comodel: "partner",
},
{
type: "many2many",
string: "My Partners",
name: "my_partners",
comodel: "partner",
},
{
type: "selection",
string: "My Selection",
name: "my_selection",
selection: [
["a", "A"],
["b", "B"],
["c", "C"],
["aa", "AA"],
],
},
{
type: "tags",
string: "My Tags",
name: "my_tags",
tags: [
["a", "A", 1],
["b", "B", 5],
["c", "C", 3],
["aa", "AA", 2],
],
},
];
const definition2 = [
{
type: "char",
string: "My Text",
name: "my_text",
},
];
return {
records: [
{ id: 1, display_name: "Bar 1", child_properties: definition1 },
{ id: 2, display_name: "Bar 2", child_properties: definition2 },
],
};
} else if (method === "name_search" && model === "partner" && kwargs.name === "Bo") {
return [
[5, "Bob"],
[6, "Bobby"],
];
} else if (method === "name_search" && model === "partner" && kwargs.name === "Ali") {
return [
[9, "Alice"],
[10, "Alicia"],
];
}
}
const controlPanel = await makeWithSearch({
serverData,
resModel: "partner",
Component: SearchBar,
searchMenuTypes: [],
searchViewId: false,
searchViewArch: `
<search>
<field name="properties"/>
</search>
`,
mockRPC,
});
// expand the properties field
await editSearch(target, "a");
await click(target.querySelector(".o_expand"));
let items = target.querySelectorAll(".o_searchview_input_container li");
assert.strictEqual(items.length, 8);
assert.strictEqual(items[0].innerText, "Search Properties");
assert.strictEqual(items[1].innerText, "My Partner (Bar 1)");
assert.strictEqual(items[3].innerText, "My Selection (Bar 1) for: A");
assert.strictEqual(items[4].innerText, "My Selection (Bar 1) for: AA");
assert.strictEqual(items[5].innerText, "My Tags (Bar 1) for: A");
assert.strictEqual(items[6].innerText, "My Tags (Bar 1) for: AA");
assert.strictEqual(items[7].innerText, "My Text (Bar 2) for: a");
// click again on the expand icon to hide the properties
await click(target.querySelector(".o_expand"));
items = target.querySelectorAll(".o_searchview_input_container li");
assert.strictEqual(items.length, 1, "Should have hidden the properties");
// search for a partner, and expand the many2many property
await editSearch(target, "Bo");
await click(target.querySelector(".o_expand"));
await click(target.querySelector("li:nth-child(3) .o_expand"));
items = target.querySelectorAll(".o_searchview_input_container li");
assert.strictEqual(items.length, 6);
assert.strictEqual(items[3].innerText, "Bob");
assert.strictEqual(items[4].innerText, "Bobby");
// fold all the properties (included the search result)
await click(target.querySelector(".o_expand"));
items = target.querySelectorAll(".o_searchview_input_container li");
assert.strictEqual(items.length, 1, "Should have folded the properties");
// unfold all the properties but fold the search result
await click(target.querySelector(".o_expand"));
await click(target.querySelector("li:nth-child(3) .o_expand"));
items = target.querySelectorAll(".o_searchview_input_container li");
assert.strictEqual(items.length, 4, "Should have unfolded the properties");
assert.strictEqual(items[1].innerText, "My Partner (Bar 1)");
assert.strictEqual(items[2].innerText, "My Partners (Bar 1)");
assert.strictEqual(items[3].innerText, "My Text (Bar 2) for: Bo");
// select Bobby
await click(target.querySelector("li:nth-child(3) .o_expand"));
await click(target.querySelector(".o_searchview_input_container li:nth-child(5)"));
assert.deepEqual(getDomain(controlPanel), [
"&",
["bar", "=", 1],
["properties.my_partners", "in", 6],
]);
// expand the selection properties
await click(target.querySelector(".o_cp_searchview"));
await editSearch(target, "a");
await click(target.querySelector(".o_expand"));
items = target.querySelectorAll(".o_searchview_input_container li");
assert.strictEqual(items.length, 8, "Should have unfolded the selection properties");
assert.strictEqual(items[3].innerText, "My Selection (Bar 1) for: A");
assert.strictEqual(items[4].innerText, "My Selection (Bar 1) for: AA");
// select the selection option "AA"
await click(target.querySelector(".o_searchview_input_container li:nth-child(5)"));
let expectedDomain = [
"&",
"&",
["bar", "=", 1],
["properties.my_partners", "in", 6],
"&",
["bar", "=", 1],
["properties.my_selection", "=", "aa"],
];
assert.deepEqual(getDomain(controlPanel), expectedDomain);
// select the selection option "A"
await click(target.querySelector(".o_cp_searchview"));
await editSearch(target, "a");
await click(target.querySelector(".o_expand"));
await click(target.querySelector(".o_searchview_input_container li:nth-child(4)"));
expectedDomain = [
"&",
"&",
["bar", "=", 1],
["properties.my_partners", "in", 6],
"|",
"&",
["bar", "=", 1],
["properties.my_selection", "=", "aa"],
"&",
["bar", "=", 1],
["properties.my_selection", "=", "a"],
];
assert.deepEqual(getDomain(controlPanel), expectedDomain);
// reset the search
await click(target.querySelector(".o_facet_remove"));
await click(target.querySelector(".o_facet_remove"));
// search a many2one value
await click(target.querySelector(".o_cp_searchview"));
await editSearch(target, "Ali");
await click(target.querySelector(".o_expand"));
await click(target.querySelector("li:nth-child(2) .o_expand"));
items = target.querySelectorAll(".o_searchview_input_container li");
assert.strictEqual(items.length, 6, "Should show the search result");
assert.strictEqual(items[2].innerText, "Alice");
assert.strictEqual(items[3].innerText, "Alicia");
await click(target.querySelector(".o_searchview_input_container li:nth-child(4)"));
expectedDomain = ["&", ["bar", "=", 1], ["properties.my_partner", "=", 10]];
assert.deepEqual(getDomain(controlPanel), expectedDomain);
// search a tag value
await click(target.querySelector(".o_cp_searchview"));
await editSearch(target, "A");
await click(target.querySelector(".o_expand"));
items = target.querySelectorAll(".o_searchview_input_container li");
assert.strictEqual(items.length, 8, "Should show the search result");
assert.strictEqual(items[5].innerText, "My Tags (Bar 1) for: A");
assert.strictEqual(items[6].innerText, "My Tags (Bar 1) for: AA");
await click(target.querySelector(".o_searchview_input_container li:nth-child(7)"));
expectedDomain = [
"&",
"&",
["bar", "=", 1],
["properties.my_partner", "=", 10],
"&",
["bar", "=", 1],
["properties.my_tags", "in", "aa"],
];
assert.deepEqual(getDomain(controlPanel), expectedDomain);
// add the tag "B"
await click(target.querySelector(".o_cp_searchview"));
await editSearch(target, "B");
await click(target.querySelector(".o_expand"));
items = target.querySelectorAll(".o_searchview_input_container li");
assert.strictEqual(items.length, 6, "Should show the search result");
assert.strictEqual(items[4].innerText, "My Tags (Bar 1) for: B");
await click(target.querySelector(".o_searchview_input_container li:nth-child(5)"));
expectedDomain = [
"&",
"&",
["bar", "=", 1],
["properties.my_partner", "=", 10],
"|",
"&",
["bar", "=", 1],
["properties.my_tags", "in", "aa"],
"&",
["bar", "=", 1],
["properties.my_tags", "in", "b"],
];
assert.deepEqual(getDomain(controlPanel), expectedDomain);
// try to click on the many2one properties without unfolding
// it should not add the domain, but unfold the item
await editSearch(target, "Bobby");
await click(target.querySelector(".o_expand"));
await click(target.querySelector(".o_searchview_input_container li:nth-child(2)"));
assert.deepEqual(getDomain(controlPanel), expectedDomain);
items = target.querySelectorAll(".o_searchview_input_container li");
assert.strictEqual(items.length, 5, "Should have unfold the many2one");
assert.strictEqual(items[2].innerText, "(no result)", "Should have unfold the many2one");
// test the navigation with keyboard
await editSearch(target, "Bo");
const focusedItem = () => {
return target.querySelector(".o_menu_item.focus");
};
assert.strictEqual(focusedItem().innerText, "Search Properties");
// unfold the properties field
await triggerEvent(document.activeElement, null, "keydown", { key: "ArrowRight" });
assert.strictEqual(focusedItem().innerText, "Search Properties");
assert.ok(focusedItem().querySelector(".fa-caret-down"));
// move on the many2one property
await triggerEvent(document.activeElement, null, "keydown", { key: "ArrowRight" });
assert.strictEqual(focusedItem().innerText, "My Partner (Bar 1)");
assert.ok(focusedItem().querySelector(".fa-caret-right"));
// move on the many2many property
await triggerEvent(document.activeElement, null, "keydown", { key: "ArrowDown" });
assert.strictEqual(focusedItem().innerText, "My Partners (Bar 1)");
assert.ok(focusedItem().querySelector(".fa-caret-right"));
// move on the many2one property again
await triggerEvent(document.activeElement, null, "keydown", { key: "ArrowUp" });
assert.strictEqual(focusedItem().innerText, "My Partner (Bar 1)");
assert.ok(focusedItem().querySelector(".fa-caret-right"));
// unfold the many2one
await triggerEvent(document.activeElement, null, "keydown", { key: "ArrowRight" });
assert.strictEqual(focusedItem().innerText, "My Partner (Bar 1)");
assert.ok(focusedItem().querySelector(".fa-caret-down"));
// select the first many2one
await triggerEvent(document.activeElement, null, "keydown", { key: "ArrowRight" });
assert.strictEqual(focusedItem().innerText, "Bob");
// go up on the parent
await triggerEvent(document.activeElement, null, "keydown", { key: "ArrowLeft" });
assert.strictEqual(focusedItem().innerText, "My Partner (Bar 1)");
assert.ok(focusedItem().querySelector(".fa-caret-down"));
// fold the parent
await triggerEvent(document.activeElement, null, "keydown", { key: "ArrowLeft" });
assert.strictEqual(focusedItem().innerText, "My Partner (Bar 1)");
assert.ok(focusedItem().querySelector(".fa-caret-right"));
// go up on the properties field
await triggerEvent(document.activeElement, null, "keydown", { key: "ArrowLeft" });
assert.strictEqual(focusedItem().innerText, "Search Properties");
assert.ok(focusedItem().querySelector(".fa-caret-down"));
// fold the properties field
await triggerEvent(document.activeElement, null, "keydown", { key: "ArrowLeft" });
assert.strictEqual(focusedItem().innerText, "Search Properties");
assert.ok(focusedItem().querySelector(".fa-caret-right"));
});
QUnit.test("search a property: definition record id in the context", async function (assert) {
assert.expect(3);
async function mockRPC(route, { method, model, args, kwargs }) {
if (
method === "web_search_read" &&
model === "partner" &&
kwargs.specification.display_name &&
kwargs.specification.child_properties
) {
assert.deepEqual(
kwargs.domain,
["&", ["child_properties", "!=", false], ["id", "=", 2]],
"Should search only the active parent properties"
);
const definition2 = [
{
type: "char",
string: "My Text",
name: "my_text",
},
];
return {
records: [{ id: 2, display_name: "Bar 2", child_properties: definition2 }],
};
}
}
await makeWithSearch({
serverData,
resModel: "partner",
Component: SearchBar,
searchMenuTypes: [],
searchViewId: false,
searchViewArch: `
<search>
<field name="properties"/>
</search>
`,
mockRPC,
context: { active_id: 2 },
});
await click(target.querySelector(".o_cp_searchview"));
await editSearch(target, "a");
await click(target.querySelector(".o_expand"));
const items = target.querySelectorAll(".o_searchview_input_container li");
assert.strictEqual(items.length, 2, "Should show the search result");
assert.strictEqual(items[1].innerText, "My Text (Bar 2) for: a");
});
QUnit.test("edit a filter", async function (assert) {
await makeWithSearch({
serverData,
resModel: "partner",
Component: SearchBar,
searchMenuTypes: ["groupBy"], // we need it to have facet (see facets getter in search_model)
searchViewId: false,
searchViewArch: `
<search>
<filter name="filter" string="Filter" domain="[('birthday', '>=', context_today())]"/>
<filter name="bool" string="Bool" domain="[]" context="{'group_by': 'bool'}"/>
</search>
`,
context: {
search_default_filter: true,
search_default_bool: true,
},
mockRPC(route) {
if (route === "/web/domain/validate") {
return true;
}
},
});
assert.deepEqual(getFacetTexts(target), ["Filter", "Bool"]);
assert.containsN(target, ".o_searchview_facet .o_searchview_facet_label", 2);
assert.containsOnce(
target,
".o_searchview_facet.o_facet_with_domain .o_searchview_facet_label"
);
assert.containsNone(target, ".modal");
await click(target, ".o_facet_with_domain .o_searchview_facet_label");
assert.containsOnce(target, ".modal");
assert.strictEqual(target.querySelector(".modal header").innerText, "Modify Condition");
assert.containsOnce(target, ".modal .o_domain_selector");
assert.containsOnce(target, dsHelpers.SELECTORS.condition);
assert.deepEqual(getNodesTextContent(target.querySelectorAll(".modal footer button")), [
"Confirm",
"Discard",
]);
assert.strictEqual(dsHelpers.getCurrentPath(target), "Birthday");
assert.strictEqual(dsHelpers.getCurrentOperator(target), ">=");
assert.strictEqual(dsHelpers.getCurrentValue(target), "context_today()");
assert.notOk(target.querySelector(".modal footer button").disabled);
await dsHelpers.clickOnButtonDeleteNode(target);
assert.containsNone(target, dsHelpers.SELECTORS.condition);
assert.ok(target.querySelector(".modal footer button").disabled);
await click(target, `.modal ${dsHelpers.SELECTORS.addNewRule}`);
assert.containsOnce(target, dsHelpers.SELECTORS.condition);
assert.strictEqual(dsHelpers.getCurrentPath(target), "ID");
assert.strictEqual(dsHelpers.getCurrentOperator(target), "=");
assert.strictEqual(dsHelpers.getCurrentValue(target), "1");
await click(target.querySelector(".modal footer button"));
assert.containsNone(target, ".modal");
assert.deepEqual(getFacetTexts(target), ["Bool", "ID = 1"]);
});
QUnit.test(
"edit a filter with context: context is kept after edition",
async function (assert) {
const controlPanel = await makeWithSearch({
serverData,
resModel: "partner",
Component: SearchBar,
searchViewId: false,
searchViewArch: `
<search>
<filter name="filter" string="Filter" context="{'specialKey': 'abc'}" domain="[('foo', '=', 'abc')]"/>
</search>
`,
context: {
search_default_filter: true,
},
mockRPC(route) {
if (route === "/web/domain/validate") {
return true;
}
},
});
assert.deepEqual(getFacetTexts(target), ["Filter"]);
assert.deepEqual(getContext(controlPanel).specialKey, "abc");
await click(target, ".o_facet_with_domain .o_searchview_facet_label");
await click(target.querySelector(".modal footer button"));
assert.deepEqual(getFacetTexts(target), [`Foo = abc`]);
assert.deepEqual(getContext(controlPanel).specialKey, "abc");
}
);
QUnit.test("edit a favorite", async function (assert) {
const irFilters = [
{
context: "{ 'some_key': 'some_value', 'group_by': ['bool'] }",
domain: "[('foo', 'ilike', 'abc')]",
id: 1,
is_default: true,
name: "My favorite",
sort: "[]",
user_id: [2, "Mitchell Admin"],
},
];
await makeWithSearch({
serverData,
resModel: "partner",
Component: SearchBar,
searchMenuTypes: ["groupBy"], // we need it to have facet (see facets getter in search_model)
searchViewId: false,
searchViewArch: `
<search>
<filter name="company" string="Company" domain="[]" context="{'group_by': 'company'}"/>
</search>
`,
irFilters,
mockRPC(route) {
if (route === "/web/domain/validate") {
return true;
}
},
});
assert.deepEqual(getFacetTexts(target), ["My favorite"]);
assert.containsOnce(
target,
".o_searchview_facet.o_facet_with_domain .o_searchview_facet_label"
);
await toggleSearchBarMenu(target);
await toggleMenuItem(target, "Company");
assert.deepEqual(getFacetTexts(target), ["My favorite", "Company"]);
assert.containsN(target, ".o_searchview_facet .o_searchview_facet_label", 2);
assert.containsOnce(
target,
".o_searchview_facet.o_facet_with_domain .o_searchview_facet_label"
);
await click(target, ".o_facet_with_domain .o_searchview_facet_label");
assert.containsOnce(target, ".modal");
assert.strictEqual(dsHelpers.getCurrentPath(target), "Foo");
assert.strictEqual(dsHelpers.getCurrentOperator(target), "contains");
assert.strictEqual(dsHelpers.getCurrentValue(target), "abc");
await click(target.querySelector(".modal footer button"));
assert.containsNone(target, ".modal");
assert.deepEqual(getFacetTexts(target), ["Bool\n>\nCompany", "Foo contains abc"]);
});
QUnit.test("edit a date filter with comparison active", async function (assert) {
patchDate(2023, 3, 28, 13, 40, 0);
await makeWithSearch({
serverData,
resModel: "partner",
Component: SearchBar,
searchMenuTypes: ["filter", "comparison"],
searchViewId: false,
searchViewArch: `
<search>
<filter name="birthday" string="Birthday" date="birthday"/>
</search>
`,
context: {
search_default_birthday: true,
},
mockRPC(route) {
if (route === "/web/domain/validate") {
return true;
}
},
});
assert.deepEqual(getFacetTexts(target), ["Birthday: April 2023"]);
assert.containsOnce(
target,
".o_searchview_facet.o_facet_with_domain .o_searchview_facet_label"
);
await toggleSearchBarMenu(target);
await toggleMenuItem(target, "Birthday: Previous Period");
assert.deepEqual(getFacetTexts(target), [
"Birthday: April 2023",
"Birthday: Previous Period",
]);
assert.containsOnce(
target,
".o_searchview_facet.o_facet_with_domain .o_searchview_facet_label"
);
await click(target, ".o_facet_with_domain .o_searchview_facet_label");
assert.containsOnce(target, ".modal");
assert.containsOnce(target, dsHelpers.SELECTORS.condition);
assert.strictEqual(dsHelpers.getCurrentPath(target), "Birthday");
assert.strictEqual(dsHelpers.getCurrentOperator(target), "is between");
assert.deepEqual(
[...target.querySelectorAll(`.o_datetime_input`)].map((el) => el.value),
["04/01/2023", "04/30/2023"]
);
await click(target.querySelector(".modal footer button"));
assert.containsNone(target, ".modal");
assert.deepEqual(getFacetTexts(target), [`Birthday is between 04/01/2023 and 04/30/2023`]);
});
QUnit.test("edit a field", async function (assert) {
await makeWithSearch({
serverData,
resModel: "partner",
Component: SearchBar,
searchViewId: false,
searchViewArch: `
<search>
<field name="foo"/>
</search>
`,
context: {
search_default_foo: "abc",
},
mockRPC(route) {
if (route === "/web/domain/validate") {
return true;
}
},
});
assert.deepEqual(getFacetTexts(target), ["Foo\nabc"]);
assert.containsOnce(
target,
".o_searchview_facet.o_facet_with_domain .o_searchview_facet_label"
);
await editSearch(target, "def");
const searchInput = target.querySelector(".o_searchview input");
await triggerEvent(searchInput, null, "keydown", { key: "ArrowDown" });
await triggerEvent(searchInput, null, "keydown", { key: "Enter" }); // select
assert.deepEqual(getFacetTexts(target), ["Foo\nabc\nor\ndef"]);
await click(target, ".o_facet_with_domain .o_searchview_facet_label");
assert.containsN(target, dsHelpers.SELECTORS.condition, 2);
assert.strictEqual(dsHelpers.getCurrentPath(target), "Foo");
assert.strictEqual(dsHelpers.getCurrentOperator(target), "contains");
assert.strictEqual(dsHelpers.getCurrentValue(target), "abc");
assert.strictEqual(dsHelpers.getCurrentPath(target, 1), "Foo");
assert.strictEqual(dsHelpers.getCurrentOperator(target, 1), "contains");
assert.strictEqual(dsHelpers.getCurrentValue(target, 1), "def");
await click(target.querySelector(".modal footer button"));
assert.deepEqual(getFacetTexts(target), [`Foo contains abc or Foo contains def`]);
});
QUnit.test("no rpc for getting display_name for facets if known", async function (assert) {
await makeWithSearch({
serverData,
resModel: "partner",
Component: SearchBar,
searchViewId: false,
searchViewArch: `
<search>
<filter name="filter" string="Filter" domain="[('bar', 'in', [])]"/>
</search>
`,
context: {
search_default_filter: true,
},
mockRPC(route, { method, kwargs }) {
assert.step(method || route);
if (route === "/web/domain/validate") {
return true;
}
if (method === "name_search") {
assert.step(JSON.stringify(kwargs.args /** domain */));
}
},
});
assert.deepEqual(getFacetTexts(target), ["Filter"]);
assert.verifySteps([`get_views`]);
await click(target, ".o_facet_with_domain .o_searchview_facet_label");
assert.verifySteps([`fields_get`]);
await click(target, ".o-autocomplete--input");
assert.verifySteps([`name_search`, `["!",["id","in",[]]]`]);
await click(target.querySelector(".dropdown-menu li"));
await click(target.querySelector(".modal footer button"));
assert.deepEqual(getFacetTexts(target), ["Bar is in ( First record )"]);
assert.verifySteps([`/web/domain/validate`]);
});
QUnit.test(
"single name_search call and no flicker when holding ArrowRight",
async function (assert) {
assert.expect(8);
await makeWithSearch({
serverData,
resModel: "partner",
Component: SearchBar,
searchMenuTypes: [],
searchViewId: false,
mockRPC(route, { method, kwargs }) {
if (method === "name_search") {
assert.step(method);
}
},
});
await editSearch(target, "a");
await triggerEvent(document.activeElement, null, "keydown", { key: "ArrowDown" });
await triggerEvent(document.activeElement, null, "keydown", { key: "ArrowLeft" });
await nextTick();
const input = target.querySelector("input.o_searchview_input");
for (let i = 0; i < 3; i++) {
input.dispatchEvent(
new KeyboardEvent("keydown", { key: "ArrowRight", repeat: i > 0 })
);
assert.containsNone(target, ".o_menu_item.o_indent");
assert.strictEqual(document.activeElement, input);
}
assert.verifySteps(["name_search"]);
}
);
});