/** @odoo-module **/ import { ControlPanel } from "@web/search/control_panel/control_panel"; import { click, getFixture, makeDeferred, nextTick, patchTimeZone, patchWithCleanup, triggerEvent, } from "../helpers/utils"; import { editSearch, getFacetTexts, makeWithSearch, removeFacet, setupControlPanelServiceRegistry, validateSearch, } from "./helpers"; function getDomain(controlPanel) { return controlPanel.env.searchModel.domain; } import { onWillUpdateProps } from "@odoo/owl"; 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" }, }, 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": ``, "partner,false,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: ControlPanel, 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: ControlPanel, 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: ControlPanel, 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: ControlPanel, 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+/, " ")), ["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+/, " ")), ["Birth DateTime\n07/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: ControlPanel, searchMenuTypes: [], searchViewId: false, searchViewArch: ` `, }); 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: ControlPanel, 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; patchWithCleanup(ControlPanel.prototype, { setup() { this._super(); onWillUpdateProps(() => { updateCount++; }); }, }); const controlPanel = await makeWithSearch({ serverData, resModel: "partner", Component: ControlPanel, 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+/, "")), ["BarFirst 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+/, "")), ["BarFirst recordorSecond 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; patchWithCleanup(ControlPanel.prototype, { setup() { this._super(); onWillUpdateProps(() => { updateCount++; }); }, }); await makeWithSearch({ serverData, resModel: "partner", Component: ControlPanel, 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: ControlPanel, searchMenuTypes: [], searchViewId: false, searchViewArch: ` `, }); 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: ControlPanel, 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: ControlPanel, 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: ControlPanel, searchMenuTypes: [], searchViewId: false, searchViewArch: ` `, }); 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: ControlPanel, 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: ControlPanel, searchMenuTypes: [], searchViewId: false, searchViewArch: ` `, }); 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: ControlPanel, searchMenuTypes: [], searchViewId: false, searchViewArch: ` `, }); 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: ControlPanel, searchMenuTypes: [], searchViewId: false, searchViewArch: ` `, }); 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: ControlPanel, searchMenuTypes: [], searchViewId: false, searchViewArch: ` `, }); 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: ControlPanel, searchMenuTypes: [], searchViewId: false, searchViewArch: ` `, }); 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: ControlPanel, searchMenuTypes: [], searchViewId: false, searchViewArch: ` `, }); 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: ControlPanel, searchMenuTypes: [], searchViewId: false, searchViewArch: ` `, }); 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: ControlPanel, searchMenuTypes: [], searchViewId: false, searchViewArch: /*xml*/ ` `, }); 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: ControlPanel, 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 === "name_get") { await def; } }; makeWithSearch({ serverData, mockRPC, resModel: "partner", Component: ControlPanel, searchMenuTypes: [], searchViewId: false, context: { search_default_company: 1 }, }); await nextTick(); assert.containsNone(target, ".o_control_panel"); def.resolve(); await nextTick(); assert.containsOnce(target, ".o_control_panel"); assert.strictEqual(getFacetTexts(target)[0].replace("\n", ""), "CompanyFirst record"); }); });