From 6ea05e844abbb6f6fc05d4c63e2a264360dfa348 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Lef=C3=A8vre=20=28lul=29?= Date: Thu, 6 Jul 2023 10:24:55 +0000 Subject: [PATCH] [IMP] web: evaluate domain with user context Currently, the domain editor dialog doesn't handle domains like [('foo', '=', uid)] if you don't provide the (user) context in the component props. Since this kind of domain is expected to work everywhere, the component now uses the user context by default to evaluate the domain. There's no need to provide the context everytime the component is used. closes odoo/odoo#127565 Signed-off-by: Mathieu Duckerts-Antoine (dam) --- .../domain_selector_dialog.js | 4 +- .../core/domain_selector_dialog_tests.js | 139 ++++++++++++++++++ 2 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 addons/web/static/tests/core/domain_selector_dialog_tests.js diff --git a/addons/web/static/src/core/domain_selector_dialog/domain_selector_dialog.js b/addons/web/static/src/core/domain_selector_dialog/domain_selector_dialog.js index af4777cb503..bfef855dab1 100644 --- a/addons/web/static/src/core/domain_selector_dialog/domain_selector_dialog.js +++ b/addons/web/static/src/core/domain_selector_dialog/domain_selector_dialog.js @@ -11,6 +11,7 @@ export class DomainSelectorDialog extends Component { setup() { this.notification = useService("notification"); this.orm = useService("orm"); + this.user = useService("user"); this.state = useState({ domain: this.props.domain }); } @@ -51,7 +52,8 @@ export class DomainSelectorDialog extends Component { async onConfirm() { try { let domain = new Domain(this.state.domain); - domain = domain.toList(this.props.context); + const evalContext = { ...this.user.context, ...this.props.context }; + domain = domain.toList(evalContext); await this.orm.silent.searchCount(this.props.resModel, domain, { limit: 1 }); } catch { this.notification.add(this.env._t("Domain is invalid. Please correct it"), { diff --git a/addons/web/static/tests/core/domain_selector_dialog_tests.js b/addons/web/static/tests/core/domain_selector_dialog_tests.js new file mode 100644 index 00000000000..3b84dd74738 --- /dev/null +++ b/addons/web/static/tests/core/domain_selector_dialog_tests.js @@ -0,0 +1,139 @@ +/** @odoo-module **/ + +import { Component, xml } from "@odoo/owl"; +import { DomainSelectorDialog } from "@web/core/domain_selector_dialog/domain_selector_dialog"; +import { click, getFixture, mount } from "../helpers/utils"; +import { makeDialogTestEnv } from "../helpers/mock_env"; +import { registry } from "@web/core/registry"; +import { notificationService } from "@web/core/notifications/notification_service"; +import { ormService } from "@web/core/orm_service"; +import { uiService } from "@web/core/ui/ui_service"; +import { MainComponentsContainer } from "@web/core/main_components_container"; +import { makeFakeLocalizationService } from "../helpers/mock_services"; +import { hotkeyService } from "@web/core/hotkeys/hotkey_service"; +import { fieldService } from "@web/core/field_service"; +import { popoverService } from "@web/core/popover/popover_service"; + +/** + * @typedef {Record} Props + */ + +/** + * @param {Partial & { mockRPC: Function }} [params] + */ +async function makeDomainSelectorDialog(params = {}) { + const props = { ...params }; + const mockRPC = props.mockRPC; + delete props.mockRPC; + + class Parent extends Component { + static components = { DomainSelectorDialog }; + static template = xml``; + setup() { + this.domainSelectorProps = { + resModel: "partner", + readonly: false, + domain: "[]", + close: () => {}, + onConfirm: () => {}, + ...props, + }; + } + } + + const env = await makeDialogTestEnv({ serverData, mockRPC }); + await mount(MainComponentsContainer, fixture, { env }); + return mount(Parent, fixture, { env, props }); +} + +/** @type {Element} */ +let fixture; +let serverData; + +QUnit.module("Components", (hooks) => { + hooks.beforeEach(async () => { + serverData = { + models: { + partner: { + fields: { + foo: { string: "Foo", type: "char", searchable: true }, + bar: { string: "Bar", type: "boolean", searchable: true }, + product_id: { + string: "Product", + type: "many2one", + relation: "product", + searchable: true, + }, + datetime: { string: "Date Time", type: "datetime", searchable: true }, + int: { string: "Integer", type: "integer", searchable: true }, + json_field: { string: "Json Field", type: "json", searchable: true }, + }, + records: [ + { id: 1, foo: "yop", bar: true, product_id: 37 }, + { id: 2, foo: "blip", bar: true, product_id: false }, + { id: 4, foo: "abc", bar: false, product_id: 41 }, + ], + onchanges: {}, + }, + product: { + fields: { + name: { string: "Product Name", type: "char", searchable: true }, + }, + records: [ + { id: 37, display_name: "xphone" }, + { id: 41, display_name: "xpad" }, + ], + }, + }, + }; + + registry.category("services").add("notification", notificationService); + registry.category("services").add("orm", ormService); + registry.category("services").add("ui", uiService); + registry.category("services").add("hotkey", hotkeyService); + registry.category("services").add("localization", makeFakeLocalizationService()); + registry.category("services").add("popover", popoverService); + registry.category("services").add("field", fieldService); + + fixture = getFixture(); + }); + + QUnit.module("DomainSelector"); + + QUnit.test("a domain with a user context dynamic part is valid", async (assert) => { + await makeDomainSelectorDialog({ + domain: "[('foo', '=', uid)]", + onConfirm(domain) { + assert.strictEqual(domain, "[('foo', '=', uid)]"); + assert.step("confirmed"); + }, + mockRPC(route, args) { + if (args.method === "search_count") { + const domain = args.args[0]; + assert.deepEqual(domain, [["foo", "=", 7]]); + assert.step("rpc validation"); + } + }, + }); + const confirmButton = fixture.querySelector(".o_dialog footer button"); + await click(confirmButton); + assert.verifySteps(["rpc validation", "confirmed"]); + }); + + QUnit.test("can override user context", async (assert) => { + await makeDomainSelectorDialog({ + domain: "[('foo', '=', uid)]", + context: { uid: 99 }, + mockRPC(route, args) { + if (args.method === "search_count") { + const domain = args.args[0]; + assert.deepEqual(domain, [["foo", "=", 99]]); + assert.step("rpc validation"); + } + }, + }); + const confirmButton = fixture.querySelector(".o_dialog footer button"); + await click(confirmButton); + assert.verifySteps(["rpc validation"]); + }); +});