From 4e70ca384d7ce1afae1633e582e7fe38562e0d96 Mon Sep 17 00:00:00 2001 From: Mathieu Duckerts-Antoine Date: Wed, 19 Jul 2023 08:04:05 +0000 Subject: [PATCH] [FIX] web: domain selector dialog: validate domain on confirm With ab4f45b, the domain validation done on confirmation in the domain selector dialog was removed. Now that we have a new route /web/domain/validate (see previous commit) that allows us to quickly check the validity of a domain, we can reintroduce the validation. closes odoo/odoo#128913 X-original-commit: fa7788fac3c48e95dcbe78abb66ac5c284d27c56 Related: odoo/enterprise#44291 Signed-off-by: Aaron Bohy (aab) Signed-off-by: Mathieu Duckerts-Antoine (dam) --- .../domain_selector_dialog.js | 19 ++++-- .../core/domain_selector_dialog_tests.js | 24 +++---- .../tests/search/search_bar_menu_tests.js | 66 +++++++++++++++++++ .../static/tests/search/search_bar_tests.js | 25 +++++++ .../tests/views/fields/domain_field_tests.js | 5 ++ 5 files changed, 124 insertions(+), 15 deletions(-) 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 498146a74d1..43e386e1c3d 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 @@ -10,6 +10,7 @@ import { useService } from "../utils/hooks"; export class DomainSelectorDialog extends Component { setup() { this.notification = useService("notification"); + this.rpc = useService("rpc"); this.orm = useService("orm"); this.user = useService("user"); this.state = useState({ domain: this.props.domain }); @@ -50,13 +51,23 @@ export class DomainSelectorDialog extends Component { }; } - onConfirm() { + async onConfirm() { this.confirmButtonRef.el.disabled = true; - const evalContext = { ...this.user.context, ...this.props.context }; + let domain; + let isValid; try { - const domain = new Domain(this.state.domain); - domain.toList(evalContext); + const evalContext = { ...this.user.context, ...this.props.context }; + domain = new Domain(this.state.domain).toList(evalContext); } catch { + isValid = false; + } + if (isValid === undefined) { + isValid = await this.rpc("/web/domain/validate", { + model: this.props.resModel, + domain, + }); + } + if (!isValid) { if (this.confirmButtonRef.el) { this.confirmButtonRef.el.disabled = false; } diff --git a/addons/web/static/tests/core/domain_selector_dialog_tests.js b/addons/web/static/tests/core/domain_selector_dialog_tests.js index d7229e14ebf..ea5a1242544 100644 --- a/addons/web/static/tests/core/domain_selector_dialog_tests.js +++ b/addons/web/static/tests/core/domain_selector_dialog_tests.js @@ -109,15 +109,16 @@ QUnit.module("Components", (hooks) => { assert.strictEqual(domain, "[('foo', '=', uid)]"); assert.step("confirmed"); }, - mockRPC(_, args) { - if (args.method === "search_count") { - assert.step("rpc validation"); + mockRPC(route) { + if (route === "/web/domain/validate") { + assert.step("validation"); + return true; } }, }); const confirmButton = fixture.querySelector(".o_dialog footer button"); await click(confirmButton); - assert.verifySteps(["confirmed"]); + assert.verifySteps(["validation", "confirmed"]); }); QUnit.test("can extend eval context", async (assert) => { @@ -129,15 +130,16 @@ QUnit.module("Components", (hooks) => { assert.step("confirmed"); }, - mockRPC(_, args) { - if (args.method === "search_count") { - assert.step("rpc validation"); + mockRPC(route) { + if (route === "/web/domain/validate") { + assert.step("validation"); + return true; } }, }); const confirmButton = fixture.querySelector(".o_dialog footer button"); await click(confirmButton); - assert.verifySteps(["confirmed"]); + assert.verifySteps(["validation", "confirmed"]); }); QUnit.test("a domain with an unknown expression is not valid", async (assert) => { @@ -146,9 +148,9 @@ QUnit.module("Components", (hooks) => { onConfirm() { assert.step("confirmed"); }, - mockRPC(_, args) { - if (args.method === "search_count") { - assert.step("rpc validation"); + mockRPC(route) { + if (route === "/web/domain/validate") { + assert.step("validation"); } }, }); diff --git a/addons/web/static/tests/search/search_bar_menu_tests.js b/addons/web/static/tests/search/search_bar_menu_tests.js index f5271dd25d3..86259b9fa94 100644 --- a/addons/web/static/tests/search/search_bar_menu_tests.js +++ b/addons/web/static/tests/search/search_bar_menu_tests.js @@ -1612,6 +1612,11 @@ QUnit.module("Search", (hooks) => { context: { search_default_filter: true, }, + mockRPC(route) { + if (route === "/web/domain/validate") { + return true; + } + }, }); assert.deepEqual(getFacetTexts(target), ["Filter"]); assert.deepEqual(getDomain(controlPanel), [["foo", "=", "abc"]]); @@ -1669,6 +1674,11 @@ QUnit.module("Search", (hooks) => { searchMenuTypes: ["filter"], searchViewId: false, searchViewArch: ``, + mockRPC(route) { + if (route === "/web/domain/validate") { + return true; + } + }, }); assert.deepEqual(getFacetTexts(target), []); assert.deepEqual(getDomain(controlPanel), []); @@ -1694,6 +1704,11 @@ QUnit.module("Search", (hooks) => { searchMenuTypes: ["filter"], searchViewId: false, searchViewArch: ``, + mockRPC(route) { + if (route === "/web/domain/validate") { + return true; + } + }, }); assert.deepEqual(getFacetTexts(target), []); assert.deepEqual(getDomain(controlPanel), []); @@ -1717,6 +1732,11 @@ QUnit.module("Search", (hooks) => { searchMenuTypes: ["filter"], searchViewId: false, searchViewArch: ``, + mockRPC(route) { + if (route === "/web/domain/validate") { + return true; + } + }, }); await toggleSearchBarMenu(target); await openAddCustomFilterDialog(target); @@ -1747,6 +1767,11 @@ QUnit.module("Search", (hooks) => { resModel: "foo", Component: SearchBar, searchMenuTypes: ["filter"], + mockRPC(route) { + if (route === "/web/domain/validate") { + return true; + } + }, }); assert.deepEqual(getFacetTexts(target), []); assert.deepEqual(getDomain(controlPanel), []); @@ -1796,6 +1821,42 @@ QUnit.module("Search", (hooks) => { assert.deepEqual(getDomain(controlPanel), [["boolean", "!=", true]]); }); + QUnit.test("Add a custom filter: notification on invalid domain", async function (assert) { + assert.expect(3); + patchWithCleanup(odoo, { debug: true }); + registry.category("services").add( + "notification", + { + start() { + return { + add(message, options) { + assert.strictEqual(message, "Domain is invalid. Please correct it"); + assert.deepEqual(options, { type: "danger" }); + }, + }; + }, + }, + { force: true } + ); + await makeWithSearch({ + serverData, + resModel: "foo", + Component: SearchBar, + searchMenuTypes: ["filter"], + mockRPC(route) { + if (route === "/web/domain/validate") { + return false; + } + }, + }); + + await toggleSearchBarMenu(target); + await openAddCustomFilterDialog(target); + await editInput(target, ".o_domain_debug_input", "[(uid, uid, uid)]"); + await click(target.querySelector(".modal footer button")); + assert.containsOnce(target, ".modal .o_domain_selector"); + }); + QUnit.test("display names in facets", async function (assert) { patchWithCleanup(odoo, { debug: true }); serverData.models.partner = { @@ -1813,6 +1874,11 @@ QUnit.module("Search", (hooks) => { searchMenuTypes: ["filter"], searchViewId: false, searchViewArch: ``, + mockRPC(route) { + if (route === "/web/domain/validate") { + return true; + } + }, }); await toggleSearchBarMenu(target); await openAddCustomFilterDialog(target); diff --git a/addons/web/static/tests/search/search_bar_tests.js b/addons/web/static/tests/search/search_bar_tests.js index 7a7caae4f50..ad680d87c72 100644 --- a/addons/web/static/tests/search/search_bar_tests.js +++ b/addons/web/static/tests/search/search_bar_tests.js @@ -1440,6 +1440,11 @@ QUnit.module("Search", (hooks) => { 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); @@ -1497,6 +1502,11 @@ QUnit.module("Search", (hooks) => { 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"); @@ -1533,6 +1543,11 @@ QUnit.module("Search", (hooks) => { `, irFilters, + mockRPC(route) { + if (route === "/web/domain/validate") { + return true; + } + }, }); assert.deepEqual(getFacetTexts(target), ["My favorite"]); assert.containsOnce( @@ -1578,6 +1593,11 @@ QUnit.module("Search", (hooks) => { context: { search_default_birthday: true, }, + mockRPC(route) { + if (route === "/web/domain/validate") { + return true; + } + }, }); assert.deepEqual(getFacetTexts(target), ["Birthday: April 2023"]); assert.containsOnce( @@ -1633,6 +1653,11 @@ QUnit.module("Search", (hooks) => { context: { search_default_foo: "abc", }, + mockRPC(route) { + if (route === "/web/domain/validate") { + return true; + } + }, }); assert.deepEqual(getFacetTexts(target), ["Foo\nabc"]); assert.containsOnce( diff --git a/addons/web/static/tests/views/fields/domain_field_tests.js b/addons/web/static/tests/views/fields/domain_field_tests.js index 444707b2f1f..098101e76f2 100644 --- a/addons/web/static/tests/views/fields/domain_field_tests.js +++ b/addons/web/static/tests/views/fields/domain_field_tests.js @@ -914,6 +914,11 @@ QUnit.module("Fields", (hooks) => {
`, + mockRPC: (route) => { + if (route === "/web/domain/validate") { + return true; + } + }, }); assert.containsNone(target, ".o_domain_leaf"); assert.containsNone(target, ".modal");