← Back to team overview

openerp-dev-web team mailing list archive

lp:~openerp-dev/openobject-client-web/proto61-qweb-tests-and-fixes-xmo into lp:~openerp-dev/openobject-client-web/trunk-proto61

 

Xavier (Open ERP) has proposed merging lp:~openerp-dev/openobject-client-web/proto61-qweb-tests-and-fixes-xmo into lp:~openerp-dev/openobject-client-web/trunk-proto61.

Requested reviews:
  Fabien Meghazi (OpenERP) (fme)
  Antony Lesuisse (al-openerp)
  OpenERP R&D Team (openerp-dev)

For more details, see:
https://code.launchpad.net/~openerp-dev/openobject-client-web/proto61-qweb-tests-and-fixes-xmo/+merge/52873

Addition of a pretty complete test suite for the user-facing part of qweb (template directives).

69 assertions in 22 tests, 2 of the assertions fail (on trim, when combining inner with left or right) but I'm not sure how it's actually supposed to work so I don't know who's wrong between the test cases and the code.

Most of qweb's template stuff should be exercised by the test cases.
-- 
https://code.launchpad.net/~openerp-dev/openobject-client-web/proto61-qweb-tests-and-fixes-xmo/+merge/52873
Your team OpenERP R&D Team is requested to review the proposed merge of lp:~openerp-dev/openobject-client-web/proto61-qweb-tests-and-fixes-xmo into lp:~openerp-dev/openobject-client-web/trunk-proto61.
=== added file 'addons/base/static/qweb/qweb-test-attributes.xml'
--- addons/base/static/qweb/qweb-test-attributes.xml	1970-01-01 00:00:00 +0000
+++ addons/base/static/qweb/qweb-test-attributes.xml	2011-03-10 15:51:52 +0000
@@ -0,0 +1,22 @@
+<templates>
+    <t t-name="fixed-literal">
+        <div t-att-foo="'bar'"/>
+    </t>
+    <t t-name="fixed-variable">
+        <div t-att-foo="value"/>
+    </t>
+
+    <t t-name="tuple-literal">
+        <div t-att="['foo', 'bar']"/>
+    </t>
+    <t t-name="tuple-variable">
+        <div t-att="att"/>
+    </t>
+
+    <t t-name="format-literal">
+        <div t-attf-foo="bar"/>
+    </t>
+    <t t-name="format-value">
+        <div t-attf-foo="b#{value}r"/>
+    </t>
+</templates>

=== added file 'addons/base/static/qweb/qweb-test-call.xml'
--- addons/base/static/qweb/qweb-test-call.xml	1970-01-01 00:00:00 +0000
+++ addons/base/static/qweb/qweb-test-call.xml	2011-03-10 15:51:52 +0000
@@ -0,0 +1,30 @@
+<templates>
+    <t t-name="basic-callee">ok</t>
+    <t t-name="callee-printsbody"><t t-esc="0"/></t>
+    <t t-name="callee-uses-foo"><t t-esc="foo"/></t>
+    <t t-name="callee-sets-foo"><t t-set="foo" t-value="'ok'"/></t>
+
+    <t t-name="basic-caller">
+        <t t-call="basic-callee"/>
+    </t>
+    <t t-name="with-unused-body">
+        <t t-call="basic-callee">WHEEE</t>
+    </t>
+    <t t-name="with-unused-setbody">
+        <t t-call="basic-callee">
+            <t t-set="qux" t-value="3"/>
+        </t>
+    </t>
+    <t t-name="with-used-body">
+        <t t-call="callee-printsbody">ok</t>
+    </t>
+    <t t-name="with-used-setbody">
+        <t t-call="callee-uses-foo">
+            <t t-set="foo" t-value="'ok'"/>
+        </t>
+    </t>
+    <t t-name="in-context-import">
+        <t t-call="callee-sets-foo" t-import='*'/>
+        <t t-esc="foo"/>
+    </t>
+</templates>

=== added file 'addons/base/static/qweb/qweb-test-conditionals.xml'
--- addons/base/static/qweb/qweb-test-conditionals.xml	1970-01-01 00:00:00 +0000
+++ addons/base/static/qweb/qweb-test-conditionals.xml	2011-03-10 15:51:52 +0000
@@ -0,0 +1,59 @@
+<templates>
+    <t t-name="literal-conditional">
+        <t t-if="true">ok</t>
+    </t>
+    <t t-name="boolean-value-conditional">
+        <t t-if="value">ok</t>
+    </t>
+    <t t-name="boolean-value-conditional-false">
+        <t t-if="value">fail</t>
+    </t>
+
+    <t t-name="negify">
+        <t t-if="!false">ok</t>
+    </t>
+    <t t-name="equality">
+        <t t-if="1 == 1">ok</t>
+    </t>
+    <t t-name="difference">
+        <t t-if="1 != 2">ok</t>
+    </t>
+    <t t-name="and">
+        <t t-if="true and true">ok</t>
+    </t>
+    <t t-name="and-js">
+        <t t-if="true &amp;&amp; true">ok</t>
+    </t>
+    <t t-name="or">
+        <t t-if="false or true">ok</t>
+    </t>
+    <t t-name="or-js">
+        <t t-if="false || true">ok</t>
+    </t>
+
+    <t t-name="greater">
+        <t t-if="2 gt 1">ok</t>
+    </t>
+    <t t-name="greater-js">
+        <t t-if="2 &gt; 1">ok</t>
+    </t>
+    <t t-name="lower">
+        <t t-if="1 lt 2">ok</t>
+    </t>
+    <t t-name="lower-js">
+        <t t-if="1 &lt; 2">ok</t>
+    </t>
+
+    <t t-name="greater-or-equal">
+        <t t-if="2 gte 1">o</t><t t-if="2 gte 2">k</t>
+    </t>
+    <t t-name="greater-or-equal-js">
+        <t t-if="2 &gt;= 1">o</t><t t-if="2 &gt;= 2">k</t>
+    </t>
+    <t t-name="lower-or-equal">
+        <t t-if="1 lte 2">o</t><t t-if="2 lte 2">k</t>
+    </t>
+    <t t-name="lower-or-equal-js">
+        <t t-if="1 &lt;= 2">o</t><t t-if="2 &lt;= 2">k</t>
+    </t>
+</templates>

=== added file 'addons/base/static/qweb/qweb-test-foreach.xml'
--- addons/base/static/qweb/qweb-test-foreach.xml	1970-01-01 00:00:00 +0000
+++ addons/base/static/qweb/qweb-test-foreach.xml	2011-03-10 15:51:52 +0000
@@ -0,0 +1,40 @@
+<templates>
+    <t t-name="repetition-text-content" t-trim="both">
+        <t t-foreach="seq">*</t>
+    </t>
+    <t t-name="repetition-dom-content" t-trim="both">
+        <t t-foreach="seq"><b/></t>
+    </t>
+    <t t-name="repetition-self" t-trim="both">
+        <b t-foreach="seq"/>
+    </t>
+
+    <t t-name="scope-self" t-trim="both">
+        <t t-foreach="seq"><t t-esc="seq"/></t>
+    </t>
+    <t t-name="scope-value" t-trim="both">
+        <t t-foreach="seq"><t t-esc="seq_value"/></t>
+    </t>
+    <t t-name="scope-index" t-trim="both">
+        <t t-foreach="seq"><t t-esc="seq_index"/></t>
+    </t>
+    <t t-name="scope-first" t-trim="both">
+        <t t-foreach="seq"><t t-esc="seq_first"/> </t>
+    </t>
+    <t t-name="scope-last" t-trim="both">
+        <t t-foreach="seq"><t t-esc="seq_last"/> </t>
+    </t>
+    <t t-name="scope-parity" t-trim="both">
+        <t t-foreach="seq"><t t-esc="seq_parity"/> </t>
+    </t>
+    <t t-name="scope-size" t-trim="both">
+        <t t-foreach="seq"><t t-esc="seq_size"/> </t>
+    </t>
+
+    <t t-name="aliasing" t-trim="both">
+        <t t-foreach="seq" t-as="item"><t t-esc="item"/></t>
+    </t>
+    <t t-name="loopvars-aliasing" t-trim="both">
+        <t t-foreach="seq" t-as="item"><t t-esc="item_parity"/> </t>
+    </t>
+</templates>

=== added file 'addons/base/static/qweb/qweb-test-output.xml'
--- addons/base/static/qweb/qweb-test-output.xml	1970-01-01 00:00:00 +0000
+++ addons/base/static/qweb/qweb-test-output.xml	2011-03-10 15:51:52 +0000
@@ -0,0 +1,51 @@
+<templates>
+    <!-- esc, evaluates and returns @t-esc after having xml-escaped it -->
+    <t t-name="esc-literal">
+        <t t-esc="'ok'"/>
+    </t>
+    <t t-name="esc-variable">
+        <t t-esc="ok"/>
+    </t>
+    <t t-name="esc-toescape">
+        <t t-esc="ok"/>
+    </t>
+
+    <!-- escf, same as t-esc, but @t-escf is a format string -->
+    <t t-name="escf-noformat-literal">
+        <t t-escf="ok"/>
+    </t>
+    <t t-name="escf-simpleformat-variable">
+        <t t-escf="#{value}"/>
+    </t>
+    <t t-name="escf-format-variable">
+        <t t-escf="[#{value}]"/>
+    </t>
+    <t t-name="escf-format-variable-with-escapes">
+        <t t-escf="[#{value}]"/>
+    </t>
+
+    <!-- raw, evaluates and returns @t-raw directly (no escaping) -->
+    <t t-name="raw-literal">
+        <t t-raw="'ok'"/>
+    </t>
+    <t t-name="raw-variable">
+        <t t-raw="ok"/>
+    </t>
+    <t t-name="raw-notescaped">
+        <t t-raw="ok"/>
+    </t>
+
+    <!-- rawf, same as t-raw, but @t-rawf is a format string -->
+    <t t-name="rawf-noformat-literal">
+        <t t-escf="ok"/>
+    </t>
+    <t t-name="rawf-simpleformat-variable">
+        <t t-escf="#{value}"/>
+    </t>
+    <t t-name="rawf-format-variable">
+        <t t-escf="[#{value}]"/>
+    </t>
+    <t t-name="rawf-format-variable-notescaped">
+        <t t-rawf="[#{value}]"/>
+    </t>
+</templates>

=== added file 'addons/base/static/qweb/qweb-test-set.xml'
--- addons/base/static/qweb/qweb-test-set.xml	1970-01-01 00:00:00 +0000
+++ addons/base/static/qweb/qweb-test-set.xml	2011-03-10 15:51:52 +0000
@@ -0,0 +1,15 @@
+<templates>
+    <t t-name="set-from-attribute-literal">
+        <t t-set="value" t-value="'ok'"/><t t-esc="value"/>
+    </t>
+    <t t-name="set-from-body-literal">
+        <t t-set="value">ok</t><t t-esc="value"/>
+    </t>
+
+    <t t-name="set-from-attribute-lookup">
+        <t t-set="stuff" t-value="value"/><t t-esc="stuff"/>
+    </t>
+    <t t-name="set-from-body-lookup">
+        <t t-set="stuff"><t t-esc="value"/></t><t t-esc="stuff"/>
+    </t>
+</templates>

=== added file 'addons/base/static/qweb/qweb-test-trim.xml'
--- addons/base/static/qweb/qweb-test-trim.xml	1970-01-01 00:00:00 +0000
+++ addons/base/static/qweb/qweb-test-trim.xml	2011-03-10 15:51:52 +0000
@@ -0,0 +1,19 @@
+<templates>
+    <t t-name="trim-content-both" t-trim="both">
+        ok
+    </t>
+    <t t-name="trim-content-before" t-trim="left"> ok </t>
+    <t t-name="trim-content-after" t-trim="right"> ok </t>
+    <t t-name="trim-content-unknown" t-trim="stuff"> ok </t>
+
+    <t t-name="trim-children-text" t-trim="inner">o k</t>
+    <t t-name="trim-children-elements" t-trim="inner">
+        <foo/>
+        <bar/>
+    </t>
+    <t t-name="trim-mixed-children" t-trim="inner">
+        [ <foo/> ]
+    </t>
+    <t t-name='trim-children-left' t-trim="left inner"> <foo/> <bar/> </t>
+    <t t-name='trim-children-right' t-trim="right inner"> <foo/> <bar/> </t>
+</templates>

=== added file 'addons/base/static/qweb/qweb-test.js.html'
--- addons/base/static/qweb/qweb-test.js.html	1970-01-01 00:00:00 +0000
+++ addons/base/static/qweb/qweb-test.js.html	2011-03-10 15:51:52 +0000
@@ -0,0 +1,284 @@
+<!doctype html>
+<html>
+<head>
+    <script src="http://code.jquery.com/jquery-1.5.1.js";></script>
+    <link rel="stylesheet" href="http://code.jquery.com/qunit/git/qunit.css"; type="text/css" media="screen"/>
+    <script type="text/javascript" src="http://code.jquery.com/qunit/git/qunit.js";></script>
+
+    <script type="text/javascript" src="qweb.js"></script>
+
+    <script>
+        function trim(s) {
+            return s.replace(/(^\s+|\s+$)/g, '');
+        }
+        function render(template, context) {
+            return trim(QWeb.render(template, context));
+        }
+        $(document).ready(function() {
+            module("Basic output tests", {
+                setup: function () {
+                    QWeb.add_template('qweb-test-output.xml');
+                },
+                teardown: function () {
+                    QWeb.templates = [];
+                    QWeb.tag = {};
+                    QWeb.att = {};
+                }
+            });
+
+            test("Basic escaped output", function () {
+                equals(render('esc-literal', {}), "ok", "Render a literal string");
+                equals(render('esc-variable', {ok: 'ok'}), "ok", "Render a string variable");
+                equals(render('esc-toescape', {ok: '<ok>'}), "&lt;ok&gt;", "Render a string with data to escape");
+            });
+            test("Formatted escaped output", function () {
+                equals(render('escf-noformat-literal', {}), "ok", "Render a literal string");
+                equals(render('escf-simpleformat-variable', {value: 'ok'}), "ok",
+                        "Only render an interpolated variable");
+                equals(render('escf-format-variable', {value: 'ok'}), "[ok]",
+                        "Actually formatted variable");
+                equals(render('escf-format-variable-with-escapes', {value: '<ok>'}), '[&lt;ok&gt;]',
+                        "Render a formatted string with data to escape");
+            });
+            test("Basic unescaped output", function () {
+                equals(render('raw-literal', {}), "ok", "Render a literal string");
+                equals(render('raw-variable', {ok: 'ok'}), "ok", "Render a string variable");
+                equals(render('raw-notescaped', {ok: '<ok>'}), "<ok>", "Render a string with data not escaped");
+            });
+            test("Formatted unescaped output", function () {
+                equals(render('rawf-noformat-literal', {}), "ok", "Render a literal string");
+                equals(render('rawf-simpleformat-variable', {value: 'ok'}), "ok",
+                        "Only render an interpolated variable");
+                equals(render('rawf-format-variable', {value: 'ok'}), "[ok]",
+                        "Actually formatted variable");
+                equals(render('rawf-format-variable-notescaped', {value: '<ok>'}), '[<ok>]',
+                        "Render a formatted string with data not escaped");
+            });
+
+            module("Context-setting tests", {
+                setup: function () {
+                    QWeb.add_template('qweb-test-set.xml');
+                },
+                teardown: function () {
+                    QWeb.templates = [];
+                    QWeb.tag = {};
+                    QWeb.att = {};
+                }
+            });
+            test("Set literal value", function () {
+                equals(render('set-from-attribute-literal', {}), "ok",
+                        "Set a literal value via @t-value");
+                equals(render('set-from-body-literal', {}), "ok",
+                        "Set a literal value via @t-set body");
+            });
+            test("Set value looked up from context", function () {
+                equals(render('set-from-attribute-lookup', {value: 'ok'}), "ok",
+                        "Set a value looked up in context via @t-value");
+                equals(render('set-from-body-lookup', {value: 'ok'}), 'ok',
+                        "Set a value looked up in context via @t-set body and @t-esc");
+            });
+
+            module("Conditionals", {
+                setup: function () {
+                    QWeb.add_template('qweb-test-conditionals.xml');
+                },
+                teardown: function () {
+                    QWeb.templates = [];
+                    QWeb.tag = {};
+                    QWeb.att = {};
+                }
+            });
+            test('Basic (single boolean) conditionals', function () {
+                equals(render('literal-conditional', {}), 'ok',
+                    "Test on a literal value");
+                equals(render('boolean-value-conditional', {value: true}), 'ok',
+                    "Test on a truthy variable value");
+                equals(render('boolean-value-conditional-false', {value: false}), '',
+                    "Test on a falsy variable value");
+            });
+            test('Boolean expressions in conditionals', function () {
+                equals(render('negify', {}), 'ok',
+                    "Negative");
+                equals(render('equality', {}), 'ok',
+                    "Equality");
+                equals(render('difference', {}), 'ok',
+                    "Difference");
+                equals(render('and', {}), 'ok',
+                    "Boolean and");
+                equals(render('and-js', {}), 'ok',
+                    "Boolean and via manually escaped JS operator");
+                equals(render('or', {}), 'ok',
+                    "Boolean or");
+                equals(render('or-js', {}), 'ok',
+                    "Boolean or using JS operator");
+            });
+            test('Comparison boolean tests in conditionals', function () {
+                equals(render('greater', {}), 'ok',
+                    "Greater");
+                equals(render('greater-js', {}), 'ok',
+                    "Greater, JS operator");
+                equals(render('lower', {}), 'ok',
+                    "Lower");
+                equals(render('lower-js', {}), 'ok',
+                    "Lower, JS operator");
+                equals(render('greater-or-equal', {}), 'ok',
+                    "Greater or Equal");
+                equals(render('greater-or-equal-js', {}), 'ok',
+                    "Greater or Equal, JS operator");
+                equals(render('lower-or-equal', {}), 'ok',
+                    "Lower or Equal");
+                equals(render('lower-or-equal-js', {}), 'ok',
+                    "Lower or Equal, JS operator");
+            });
+
+            module("Attributes manipulation", {
+                setup: function () {
+                    QWeb.add_template('qweb-test-attributes.xml');
+                },
+                teardown: function () {
+                    QWeb.templates = [];
+                    QWeb.tag = {};
+                    QWeb.att = {};
+                }
+            });
+            test('Fixed-name attributes', function () {
+                equals(render('fixed-literal', {}), '<div foo="bar"/>',
+                    "Fixed name and literal attribute value");
+                equals(render('fixed-variable', {value: 'ok'}), '<div foo="ok"/>',
+                    "Fixed name and variable attribute value");
+            });
+            test('Tuple-based attributes', function () {
+                equals(render('tuple-literal', {}), '<div foo="bar"/>',
+                    "Tuple-based literal attributes");
+                equals(render('tuple-variable', {att: ['foo', 'bar']}), '<div foo="bar"/>',
+                    "Tuple-based variable attributes");
+            });
+            test('Fixed name, formatted value attributes', function () {
+                equals(render('format-literal', {}), '<div foo="bar"/>',
+                    "Literal format");
+                equals(render('format-value', {value:'a'}), '<div foo="bar"/>',
+                    "Valued format");
+            });
+
+            module("Template calling (including)", {
+                setup: function () {
+                    QWeb.add_template('qweb-test-call.xml');
+                },
+                teardown: function () {
+                    QWeb.templates = [];
+                    QWeb.tag = {};
+                    QWeb.att = {};
+                }
+            });
+            test('Trivial call invocation', function () {
+                equals(render('basic-caller', {}), 'ok',
+                    "Direct call of a second template");
+            });
+            test('Call invocation with body', function () {
+                equals(render('with-unused-body', {}), 'ok',
+                    "Call of a second template with body unused");
+                equals(render('with-unused-setbody', {}), 'ok',
+                    "Call of a second template with body directives unused");
+            });
+            test('Call invocation with body (used by callee)', function () {
+                equals(render('with-used-body', {}), 'ok',
+                    "Call of a second template with body used");
+            });
+            test('Call invocation with parameters set (in body)', function () {
+                equals(render('with-used-setbody', {}), 'ok',
+                    "Call of a second template with parameters");
+            });
+            test('Call invocation in-context (via import)', function () {
+                equals(render('in-context-import', {}), 'ok',
+                    "Call with t-import (calls in current context)");
+            });
+
+            module("Trim", {
+                setup: function () {
+                    QWeb.add_template('qweb-test-trim.xml');
+                },
+                teardown: function () {
+                    QWeb.templates = [];
+                    QWeb.tag = {};
+                    QWeb.att = {};
+                }
+            });
+            test('Trim content', function () {
+                equals(QWeb.render('trim-content-both', {}), 'ok',
+                    "Trims before and after content");
+                equals(QWeb.render('trim-content-before', {}), 'ok ',
+                    "Trims before content");
+                equals(QWeb.render('trim-content-after', {}), ' ok',
+                    "Trims after content");
+                raises(function () {QWeb.render('trim-content-unknown', {});},
+                    "Trims with an unknown mode");
+            });
+            test('Trim content *and* children', function () {
+                equals(QWeb.render('trim-children-text', {}), 'o k',
+                    "Trims text children");
+                equals(QWeb.render('trim-children-elements', {}), '<foo/><bar/>',
+                    "Full trim");
+                equals(QWeb.render('trim-mixed-children', {}), '[<foo/>]',
+                    "Trims mixed content of text and elements");
+                equals(QWeb.render('trim-children-left', {}), '<foo/><bar/> ',
+                    "Trims children and left of content");
+                equals(QWeb.render('trim-children-right', {}), ' <foo/><bar/>',
+                    "Trims children and right of content");
+            });
+
+            module("Foreach", {
+                setup: function () {
+                    QWeb.add_template('qweb-test-foreach.xml');
+                },
+                teardown: function () {
+                    QWeb.templates = [];
+                    QWeb.tag = {};
+                    QWeb.att = {};
+                }
+            });
+            var seq = [4,3,2,1,0];
+            test('Basic foreach repetition', function () {
+                equals(QWeb.render('repetition-text-content', {seq:seq}), '*****',
+                    "Repetition of text content via foreach");
+                equals(QWeb.render('repetition-dom-content', {seq:seq}), '<b/><b/><b/><b/><b/>',
+                    "Repetition of node content via foreach");
+                equals(QWeb.render('repetition-self', {seq:seq}), '<b/><b/><b/><b/><b/>',
+                    "A node with a foreach repeats itself");
+            });
+            test("Foreach scope content", function () {
+                equals(QWeb.render('scope-self', {seq:seq}), '43210',
+                    "each value of the sequence is available via the sequence name");
+                equals(QWeb.render('scope-value', {seq:seq}), '43210',
+                    "each value of the sequence is also via the _value");
+                equals(QWeb.render('scope-index', {seq:seq}), '01234',
+                    "the current 0-based index is available via _index");
+                equals(QWeb.render('scope-first', {seq:seq}), 'true false false false false',
+                    "_first says whether the current item is the first of the sequence");
+                equals(QWeb.render('scope-last', {seq:seq}), 'false false false false true',
+                    "_last says whether the current item is the last of the sequence");
+                equals(QWeb.render('scope-parity', {seq:seq}), 'even odd even odd even',
+                    "the parity (odd/even) of the current row is available via _parity");
+                equals(QWeb.render('scope-size', {seq:seq}), '5 5 5 5 5',
+                    "the total length of the sequence is available through _size");
+            });
+            test('Name aliasing via t-as', function () {
+                equals(QWeb.render('aliasing', {seq:seq}), '43210',
+                    "the inner value can be re-bound via t-as");
+                equals(QWeb.render('loopvars-aliasing', {seq:seq}), 'even odd even odd even',
+                    "inner loop variables should be rebound as well");
+            });
+        });
+    </script>
+
+</head>
+<body>
+<h1 id="qunit-header">QWeb test suite</h1>
+
+<h2 id="qunit-banner"></h2>
+
+<div id="qunit-testrunner-toolbar"></div>
+<h2 id="qunit-userAgent"></h2>
+<ol id="qunit-tests"></ol>
+<div id="qunit-fixture">test markup, will be hidden</div>
+</body>
+</html>

=== modified file 'addons/base/static/qweb/qweb.js'
--- addons/base/static/qweb/qweb.js	2011-03-10 09:10:10 +0000
+++ addons/base/static/qweb/qweb.js	2011-03-10 15:51:52 +0000
@@ -43,16 +43,29 @@
     reg:new RegExp(),
     tag:{},
     att:{},
+    ValueException: function (value, message) {
+        this.value = value;
+        this.message = message;
+    },
     eval_object:function(e, v) {
         // TODO: Currently this will also replace and, or, ... in strings. Try
         // 'hi boys and girls' != '' and 1 == 1  -- will be replaced to : 'hi boys && girls' != '' && 1 == 1
         // try to find a solution without tokenizing
+<<<<<<< TREE
         e = e.replace(/\band\b/g, " && ");
         e = e.replace(/\bor\b/g, " || ");
         e = e.replace(/\bgt\b/g, " > ");
         e = e.replace(/\bgte\b/g, " >= ");
         e = e.replace(/\blt\b/g, " < ");
         e = e.replace(/\blte\b/g, " <= ");
+=======
+        e = e.replace(/\Wand\W/g, " && ");
+        e = e.replace(/\Wor\W/g, " || ");
+        e = e.replace(/\Wgt\W/g, " > ");
+        e = e.replace(/\Wgte\W/g, " >= ");
+        e = e.replace(/\Wlt\W/g, " < ");
+        e = e.replace(/\Wlte\W/g, " <= ");
+>>>>>>> MERGE-SOURCE
         if (v[e] != undefined) {
             return v[e];
         } else {
@@ -76,7 +89,7 @@
         return r;
     },
     eval_bool:function(e, v) {
-        return this.eval_object(e, v) ? true : false;
+        return !!this.eval_object(e, v);
     },
     trim : function(v, mode) {
         if (!v || !mode) return v;
@@ -88,6 +101,8 @@
             case "right":
                 return v.replace(/\s*$/, "");
         }
+        throw new QWeb.ValueException(
+            mode, "unknown trimming mode, trim mode must follow the pattern '[inner] (left|right|both)'");
     },
     escape_text:function(s) {
         return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
@@ -139,7 +154,9 @@
         if (trim) {
             if (/\binner\b/.test(trim)) {
                 inner_trim = true;
-                trim = "both";
+                if (trim == 'inner') {
+                    trim = "both";
+                }
             }
             var tm = /\b(both|left|right)\b/.exec(trim);
             if (tm) trim = tm[1];
@@ -174,13 +191,13 @@
         return this.eval_str(t_att["raw"], v);
     },
     render_tag_rawf:function(e, t_att, g_att, v) {
-        return this.eval_format(t_att["raw"], v);
+        return this.eval_format(t_att["rawf"], v);
     },
     render_tag_esc:function(e, t_att, g_att, v) {
         return this.escape_text(this.eval_str(t_att["esc"], v));
     },
     render_tag_escf:function(e, t_att, g_att, v) {
-        return this.escape_text(this.eval_format(t_att["esc"], v));
+        return this.escape_text(this.eval_format(t_att["escf"], v));
     },
     render_tag_if:function(e, t_att, g_att, v) {
         return this.eval_bool(t_att["if"], v) ? this.render_element(e, t_att, g_att, v) : "";


Follow ups