diff --git a/addons/web/static/src/js/core/py_utils.js b/addons/web/static/src/js/core/py_utils.js index c077cfc2e7e..f275302279b 100644 --- a/addons/web/static/src/js/core/py_utils.js +++ b/addons/web/static/src/js/core/py_utils.js @@ -407,6 +407,35 @@ function formatAST(ast, lbp) { throw new Error("Unimplemented python construct"); } +/** + * Normalize a domain, at the level of the AST. + * + * Note: this function does not evaluate anything inside the domain. This is + * actually quite critical because this allows the manipulation of unevaluated + * (dynamic) domains. + * + * @param {PyJS AST} domain valid AST representing a domain + * @returns {PyJS AST} normalized domain AST + */ +function normalizeDomain(domain) { + var expected = 1; + for (var i = 0; i < domain.first.length; i++) { + var value = domain.first[i].value; + if (value === '&' || value === '|') { + expected++; + } else if (value !== '!') { + expected--; + } + } + var andOperator = py.tokenize("'&'")[0]; + + if (expected < 0) { + domain.first.unshift.apply(domain.first, _.times(Math.abs(expected), _.constant(andOperator))); + } + + return domain; +} + return { context: pycontext, ensure_evaluated: ensure_evaluated, @@ -414,6 +443,7 @@ return { eval_domains_and_contexts: eval_domains_and_contexts, py_eval: py_eval, formatAST: formatAST, + normalizeDomain: normalizeDomain, }; }); diff --git a/addons/web/static/tests/core/py_utils_tests.js b/addons/web/static/tests/core/py_utils_tests.js index 99fe3c60862..fafb3d8f6ca 100644 --- a/addons/web/static/tests/core/py_utils_tests.js +++ b/addons/web/static/tests/core/py_utils_tests.js @@ -1159,6 +1159,45 @@ QUnit.module('core', function () { assert.checkAST(expr); }); + QUnit.module('pyutils (normalizeDomain)'); + + QUnit.assert.checkNormalization = function (domainStr, normalizedDomain) { + normalizedDomain = normalizedDomain || domainStr; + var domainAST = py.parse(py.tokenize(domainStr)); + var normalizedDomainAST = pyUtils.normalizeDomain(domainAST); + var result = pyUtils.formatAST(normalizedDomainAST); + this.pushResult({ + result: result === normalizedDomain, + actual: result, + expected: normalizedDomain + }); + }; + + + QUnit.test("return simple (normalized) domains", function (assert) { + assert.expect(3); + + assert.checkNormalization("[]"); + assert.checkNormalization("[('a', '=', 1)]"); + assert.checkNormalization("['!', ('a', '=', 1)]"); + }); + + QUnit.test("properly add the & in a non normalized domain", function (assert) { + assert.expect(1); + assert.checkNormalization( + "[('a', '=', 1), ('b', '=', 2)]", + "['&', ('a', '=', 1), ('b', '=', 2)]" + ); + }); + + QUnit.test("normalize domain with ! operator", function (assert) { + assert.expect(1); + assert.checkNormalization( + "['!', ('a', '=', 1), ('b', '=', 2)]", + "['&', '!', ('a', '=', 1), ('b', '=', 2)]" + ); + }); + }); });