diff --git a/addons/web/static/src/views/relational_model.js b/addons/web/static/src/views/relational_model.js index 071a80abaf0..4bdfbc4c224 100644 --- a/addons/web/static/src/views/relational_model.js +++ b/addons/web/static/src/views/relational_model.js @@ -3503,6 +3503,17 @@ export class RelationalModel extends Model { ) { rootParams.groupBy = [this.defaultGroupBy]; } + + if (rootParams.context) { + // *_view_ref and search_default_* context keys are not destined to us, rather + // they were tyîcally "consumed" by the View. + // The default_* context keys are, on the contrary, destined for us. + rootParams.context = Object.fromEntries( + Object.entries(rootParams.context).filter( + ([key]) => !key.endsWith("_view_ref") && !key.startsWith("search_default_") + ) + ); + } rootParams.rawContext = { make: () => { return makeContext([rootParams.context], {}); diff --git a/addons/web/static/tests/views/list_view_tests.js b/addons/web/static/tests/views/list_view_tests.js index 8240d64beae..930a7c11d25 100644 --- a/addons/web/static/tests/views/list_view_tests.js +++ b/addons/web/static/tests/views/list_view_tests.js @@ -12,6 +12,7 @@ import { uiService } from "@web/core/ui/ui_service"; import { getNextTabableElement } from "@web/core/utils/ui"; import { session } from "@web/session"; import { FloatField } from "@web/views/fields/float/float_field"; +import { AutoComplete } from "@web/core/autocomplete/autocomplete"; import { Many2XAutocomplete } from "@web/views/fields/relational_utils"; import { textField } from "@web/views/fields/text/text_field"; import { ListController } from "@web/views/list/list_controller"; @@ -18837,4 +18838,78 @@ QUnit.module("Views", (hooks) => { "onchange result should be applied" ); }); + + QUnit.test("context keys not passed down the stack and not to fields", async (assert) => { + patchWithCleanup(AutoComplete, { + timeout: 0, + }); + serverData.actions = { + 1: { + id: 1, + name: "Foo", + res_model: "foo", + type: "ir.actions.act_window", + views: [[false, "list"]], + context: { + tree_view_ref: "foo_view_ref", + search_default_bar: true, + }, + }, + }; + serverData.views = { + "foo,foo_view_ref,list": ` + + + `, + "foo,false,search": "", + "bar,false,list": ``, + "bar,false,search": "", + }; + + const barRecs = []; + for (let i = 1; i < 50; i++) { + barRecs.push({ + id: i, + display_name: `Value ${i}`, + }); + } + serverData.models.bar.records = barRecs; + + const mockRPC = (route, args) => { + if (args.method) { + assert.step( + `${args.model}: ${args.method}: ${JSON.stringify(args.kwargs.context)}` + ); + } + }; + const wc = await createWebClient({ serverData, mockRPC }); + await doAction(wc, 1); + assert.verifySteps([ + `foo: get_views: {"lang":"en","uid":7,"tz":"taht","tree_view_ref":"foo_view_ref","search_default_bar":true}`, + `foo: web_search_read: {"lang":"en","uid":7,"tz":"taht","bin_size":true}`, + `bar: read: {"lang":"en","uid":7,"tz":"taht","bin_size":true}`, + ]); + + await click(target.querySelectorAll(".o_data_row .o_data_cell")[1]); + + const input = target.querySelector(".o_selected_row .o_field_many2many_tags input"); + await triggerEvent(input, null, "focus"); + await click(input); + await nextTick(); + assert.verifySteps([`bar: name_search: {"lang":"en","uid":7,"tz":"taht"}`]); + + const items = Array.from( + target.querySelectorAll(".o_selected_row .o_field_many2many_tags .dropdown-item") + ); + await click(items.find((el) => el.textContent.trim() === "Search More...")); + assert.verifySteps([ + `bar: get_views: {"lang":"en","uid":7,"tz":"taht"}`, + `bar: web_search_read: {"lang":"en","uid":7,"tz":"taht","bin_size":true}`, + ]); + assert.containsOnce(target, ".modal"); + assert.strictEqual( + target.querySelector(".modal .modal-header .modal-title").textContent, + "Search: M2M field" + ); + }); });