diff --git a/addons/web/static/lib/qweb/qweb-test-call.xml b/addons/web/static/lib/qweb/qweb-test-call.xml index b0c5deb9fe6b57658ae1c994bb41b56d915665de..45a65eabce981c46a22fcc1784df9cb24faeb477 100644 --- a/addons/web/static/lib/qweb/qweb-test-call.xml +++ b/addons/web/static/lib/qweb/qweb-test-call.xml @@ -57,7 +57,7 @@ </result> <t t-name="basic-caller"> - <t t-call="{{1 and '_basic-callee' or 'other'}}"/> + <t t-call="{{True and '_basic-callee' or 'other'}}"/> </t> <result id="basic-caller">ok</result> </templates> diff --git a/addons/web/static/lib/qweb/qweb-test-global.xml b/addons/web/static/lib/qweb/qweb-test-global.xml index bfcca8c1eba3ffc3e09f2786dcdd7e943c248593..1db3e5920fb254c973e638c97dfe306e569e74ee 100644 --- a/addons/web/static/lib/qweb/qweb-test-global.xml +++ b/addons/web/static/lib/qweb/qweb-test-global.xml @@ -18,7 +18,7 @@ </t> </t> <t t-call="_callee-asc-toto"/> - <t t-set="toto"><t t-set="truc" t-value="'bbb'"/><i t-att-notruc="not truc" t-att-truc="truc and 'ok'">i</i></t> + <t t-set="toto"><t t-set="truc" t-value="'bbb'"/><i t-att-notruc="not truc" t-att-truc="bool(truc)">i</i></t> <t t-call="_callee-asc-toto"/> </t> @@ -49,6 +49,6 @@ <div>toto default</div> - <div><i truc="ok">i</i></div> + <div><i truc="True">i</i></div> ]]></result> </templates> diff --git a/addons/web/static/lib/qweb/qweb-test.js.html b/addons/web/static/lib/qweb/qweb-test.js.html new file mode 100644 index 0000000000000000000000000000000000000000..0ee5f27c3bdb05ddf86ba0952c3bb3b9da69f26e --- /dev/null +++ b/addons/web/static/lib/qweb/qweb-test.js.html @@ -0,0 +1,73 @@ +<!doctype html> +<html> +<head> + <script src="/web/static/lib/jquery/jquery.js"></script> + <link rel="stylesheet" href="/web/static/lib/qunit/qunit.css" type="text/css" media="screen"/> + <script type="text/javascript" src="/web/static/lib/qunit/qunit.js"></script> + + <script type="text/javascript" src="qweb2.js"></script> + + <script> + QWeb = new QWeb2.Engine(); + function trim(s) { + return s.replace(/(^\s+|\s+$)/g, ''); + } + function render(template, context) { + return trim(QWeb.render(template, context)).toLowerCase(); + } + + /** + * Loads the template file, and executes all the test template in a + * qunit module $title + */ + function test(title, template) { + QUnit.module(title, { + setup: function () { + var self = this; + this.qweb = new QWeb2.Engine(); + QUnit.stop(); + this.qweb.add_template(template, function (_, doc) { + self.doc = doc; + QUnit.start(); + }) + } + }); + QUnit.test('autotest', function (assert) { + var templates = this.qweb.templates; + for (var template in templates) { + if (!templates.hasOwnProperty(template)) { continue; } + // ignore templates whose name starts with _, they're + // helpers/internal + if (/^_/.test(template)) { continue; } + + var params = this.doc.querySelector('params#' + template); + var args = params ? JSON.parse(params.textContent) : {}; + + var results = this.doc.querySelector('result#' + template); + assert.equal( + trim(this.qweb.render(template, args)), + trim(results.textContent), + template); + } + }); + } + $(document).ready(function() { + test("Output", 'qweb-test-output.xml'); + test("Context-setting", 'qweb-test-set.xml'); + test("Conditionals", 'qweb-test-conditionals.xml'); + test("Attributes manipulation", 'qweb-test-attributes.xml'); + test("Template calling (to the faraway pages)", + 'qweb-test-call.xml'); + test("Foreach", 'qweb-test-foreach.xml'); + test("Global", 'qweb-test-global.xml'); + + test('Template inheritance', 'qweb-test-extend.xml'); + }); + </script> + +</head> +<body> + <div id="qunit"></div> + <div id="qunit-fixture"></div> +</body> +</html> diff --git a/addons/web/static/lib/qweb/qweb2.js b/addons/web/static/lib/qweb/qweb2.js index 8058cb7745f645e9b352ac96c9a6b2f020d48336..c4bb7128e9bec9fadd91f3aa4efe38d762ab4883 100644 --- a/addons/web/static/lib/qweb/qweb2.js +++ b/addons/web/static/lib/qweb/qweb2.js @@ -28,10 +28,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. var QWeb2 = { expressions_cache: { }, RESERVED_WORDS: 'true,false,NaN,null,undefined,debugger,console,window,in,instanceof,new,function,return,this,typeof,eval,void,Math,RegExp,Array,Object,Date'.split(','), - ACTIONS_PRECEDENCE: 'foreach,if,elif,else,call,set,js,debug,log'.split(','), + ACTIONS_PRECEDENCE: 'foreach,if,elif,else,call,set,esc,raw,js,debug,log'.split(','), WORD_REPLACEMENT: { - // FUCKING CHM FOR FUCK'S SAKE - 'not': '!', 'and': '&&', 'or': '||', 'gt': '>', @@ -78,9 +76,9 @@ var QWeb2 = { return s; }, gen_attribute: function(o) { - if (o) { + if (o !== null && o !== undefined) { if (o.constructor === Array) { - if (o[1]) { + if (o[1] !== null && o[1] !== undefined) { return this.format_attribute(o[0], o[1]); } } else if (typeof o === 'object') { @@ -720,52 +718,36 @@ QWeb2.Element = (function() { }, compile_element : function() { for (var i = 0, ilen = this.engine.actions_precedence.length; i < ilen; i++) { - var compiled_action = this.engine.actions_precedence[i]; - if (compiled_action in this.actions) { - var value = this.actions[compiled_action]; - var key = 'compile_action_' + compiled_action; + var a = this.engine.actions_precedence[i]; + if (a in this.actions) { + var value = this.actions[a]; + var key = 'compile_action_' + a; if (this[key]) { this[key](value); } else if (this.engine[key]) { this.engine[key].call(this, value); } else { - this.engine.tools.exception("No handler method for action '" + compiled_action + "'"); + this.engine.tools.exception("No handler method for action '" + a + "'"); } } } - function print_contents(self) { - var expr, esc, raw; - if (esc = self.actions['esc']) { - expr = "context.engine.tools.html_escape(" + self.format_str(esc) + ");"; - } else if(raw = self.actions['raw']) { - expr = self.format_str(raw); - } - // print non-empty result of t-esc or t-raw, otherwise - // print/process body - if (expr) { - self.top('var expr_result = ' + expr + ';'); - self.top('if (expr_result) {'); - self.top(' r.push(expr_result);'); - self.top('} else {'); - self.bottom('}'); - } - } if (this.tag.toLowerCase() !== this.engine.prefix) { var tag = "<" + this.tag; - for (var att in this.attributes) { - tag += this.engine.tools.gen_attribute([att, this.attributes[att]]); + for (var a in this.attributes) { + tag += this.engine.tools.gen_attribute([a, this.attributes[a]]); } this.top_string(tag); if (this.actions.att) { - + this.top("r.push(context.engine.tools.gen_attribute(" + (this.format_expression(this.actions.att)) + "));"); } for (var a in this.actions) { - var m, v = this.actions[a]; - if (a === 'att') { - this.top("r.push(context.engine.tools.gen_attribute(" + (this.format_expression(v)) + "));"); - } else if (m = a.match(/att-(.+)/)) { + var v = this.actions[a]; + var m = a.match(/att-(.+)/); + if (m) { this.top("r.push(context.engine.tools.gen_attribute(['" + m[1] + "', (" + (this.format_expression(v)) + ")]));"); - } else if (m = a.match(/attf-(.+)/)) { + } + var m = a.match(/attf-(.+)/); + if (m) { this.top("r.push(context.engine.tools.gen_attribute(['" + m[1] + "', (" + (this.string_interpolation(v)) + ")]));"); } } @@ -773,13 +755,11 @@ QWeb2.Element = (function() { // We do not enforce empty content on void elements // because QWeb rendering is not necessarily html. this.top_string("/>"); - return; // ensure we skip processing content } else { this.top_string(">"); this.bottom_string("</" + this.tag + ">"); } } - print_contents(this); }, compile_action_if : function(value) { this.top("if (" + (this.format_expression(value)) + ") {"); @@ -837,6 +817,14 @@ QWeb2.Element = (function() { } } }, + compile_action_esc : function(value) { + this.top("r.push(context.engine.tools.html_escape(" + + this.format_expression(value) + + "));"); + }, + compile_action_raw : function(value) { + this.top("r.push(" + (this.format_str(value)) + ");"); + }, compile_action_js : function(value) { this.top("(function(" + value + ") {"); this.bottom("})(dict);"); diff --git a/addons/web/static/lib/qweb/tests.js b/addons/web/static/lib/qweb/tests.js deleted file mode 100644 index 5b961acd427bdc0a32a2a458d1b896108d0c786f..0000000000000000000000000000000000000000 --- a/addons/web/static/lib/qweb/tests.js +++ /dev/null @@ -1,77 +0,0 @@ -odoo.define('qweb.tests', function () { -'use strict'; - -function trim(s) { - return s.replace(/(^\s+|\s+$)/g, ''); -} - -/** - * Generates a QUnit.module hook object loading the specified test file - * (from /web/static/lib/qweb) and setting ``this.qweb`` (the qweb - * instance for this module) and ``this.doc`` (the loaded XML file). - * - * Note that test files mix template elements <t t-name>, params elements - * <params> and result elements <result>. A result and an optional params - * object are linked to the corresponding template via the ``id`` - * attribute (result and params have the template name as id). - */ -function hooks(testfile) { - var template = '/web/static/lib/qweb/' + testfile; - return { - before: function () { - var self = this; - this.qweb = new QWeb2.Engine(); - var p = $.Deferred(); - this.qweb.add_template(template, function (_, doc) { - self.doc = doc; - p.resolve(doc); - }); - return p.promise(); - } - } -} -// can't get generative QUnit.test (e.g. QUnit.test in a for(;;) -// or Array#forEach() loop) to work, so each test file has a single test, -// that seems to work -function runtest() { - QUnit.test('', function (assert) { - var templates = this.qweb.templates; - assert.expect(_.reduce(templates, function (acc, _, k) { - return acc + (/^_/.test(k) ? 0 : 1); - }, 0)); - for (var template in templates) { - if (!templates.hasOwnProperty(template)) { continue; } - // ignore templates whose name starts with _, they're - // helpers/internal - if (/^_/.test(template)) { continue; } - - var params = this.doc.querySelector('params#' + template); - var args = params ? JSON.parse(params.textContent) : {}; - - var results = this.doc.querySelector('result#' + template); - assert.equal( - trim(this.qweb.render(template, args)), - trim(results.textContent), - template); - } - }); -} - -var TEMPLATES = [ - ["Output", 'qweb-test-output.xml'], - ["Context-setting", 'qweb-test-set.xml'], - ["Conditionals", 'qweb-test-conditionals.xml'], - ["Attributes manipulation", 'qweb-test-attributes.xml'], - ["Template calling (to the faraway pages)", 'qweb-test-call.xml'], - ["Foreach", 'qweb-test-foreach.xml'], - ["Global", 'qweb-test-global.xml'], - ['Template inheritance', 'qweb-test-extend.xml'] -]; - -QUnit.module('qweb', {}, function () { - TEMPLATES.forEach(function (it) { - QUnit.module(it[0], hooks(it[1]), runtest); - }) -}); - -}); diff --git a/addons/web/views/webclient_templates.xml b/addons/web/views/webclient_templates.xml index 605003443d079d346473b9709b18fcefdc5cec29..4f69ed286b33dd399f2b214f584153bc25b0463a 100644 --- a/addons/web/views/webclient_templates.xml +++ b/addons/web/views/webclient_templates.xml @@ -448,9 +448,6 @@ display: inherit; } </style> - - <script type="text/javascript" src="/web/static/lib/qweb/tests.js"></script> - <script type="text/javascript" src="/web/static/tests/helpers/test_utils.js"></script> <script type="text/javascript" src="/web/static/tests/helpers/mock_server.js"></script>