odoo.define('web.qweb_tests', function (require) { "use strict"; const {Markup} = require('web.utils'); var qwebPath = '/web/static/lib/qweb/'; const {hushConsole} = require('@web/../tests/helpers/utils'); function trim(s) { return s.replace(/(^\s+|\s+$)/g, ''); } /** * Promise-based wrapper for QWeb2.Engine#add_template * * The base version is callbacks-based which is a bit shit, and it also has * variable asynchronicity: it'll be async if passed a URL but not if passed * either a Document or an XML string. * * Either way this converts the `(error, doc)` callback to a `Promise` * as in it literally returns the parsed DOM Document. * * @param qweb the qweb instance to load the template into * @param {String|Document} template qweb template (Document, template file, or template URL) */ function add_template(qweb, template) { return new Promise(function (resolve, reject) { qweb.add_template(template, (error, doc) => error ? reject(error) : resolve(doc)); }); } /** * Loads the template file, and executes all the test template in a * qunit module $title * * @param assert QUnit assertion module * @param {String|Document} templateFile template container to load * @param {Object} [context] additional rendering context */ async function loadTest(assert, templateFile, context) { const qweb = new window.QWeb2.Engine(); const doc = await add_template(qweb, qwebPath + templateFile); assert.expect(doc.querySelectorAll('result').length); const templates = qweb.templates; for (const template in templates) { if (!templates.hasOwnProperty(template)) { continue; } // ignore templates whose name starts with _, they're // helpers/internal if (/^_/.test(template)) { continue; } const results = doc.querySelector(`result#${template}`).textContent.replace(/\r/g, ''); const params = doc.querySelector(`params#${template}`) || {textContent: 'null'}; const args = {...JSON.parse(params.textContent), ...context}; try { assert.equal(trim(qweb.render(template, args)), trim(results), template); } catch (error) { assert.notOk(error.stack || error, `Rendering error for ${template} (in ${templateFile} with context ${JSON.stringify(args)}).`); } } } const table = [ {name: 'Output', file: 'qweb-test-output.xml'}, {name: 'Context-setting', file: 'qweb-test-set.xml'}, {name: 'Conditionals', file: 'qweb-test-conditionals.xml'}, {name: 'Attributes manipulation', file: 'qweb-test-attributes.xml'}, {name: 'Templates calling (to the faraway pages)', file: 'qweb-test-call.xml', context: {True: true}}, {name: 'Foreach', file: 'qweb-test-foreach.xml'}, { name: 'Global', file: 'qweb-test-global.xml', // test uses python syntax context: {bool: (v) => !!v ? 'True' : 'False'}, fixture: { before() { this.WORD_REPLACEMENT = window.QWeb2.WORD_REPLACEMENT; window.QWeb2.WORD_REPLACEMENT = _.extend( {not: '!', None: 'undefined'}, this.WORD_REPLACEMENT ) }, after() { window.QWeb2.WORD_REPLACEMENT = this.WORD_REPLACEMENT; } } }, {name: 'Template Inheritance', file: 'qweb-test-extend.xml'}, ]; QUnit.module('QWeb', { beforeEach() { this.oldConsole = window.console; window.console = hushConsole; }, afterEach() {window.console = this.oldConsole;} }, () => { for(const {name, file, context, fixture} of table) { QUnit.test(name, async assert => { // fake expect to avoid qunit being a pain in the ass, loadTest will // update it assert.expect(1); if (fixture && 'before' in fixture) { await fixture.before(); } try { await loadTest(assert, file, context); } finally { if (fixture && 'after' in fixture) { await fixture.after(); } } }); } QUnit.test('escape', assert => { // not strictly about qweb... assert.expect(8); assert.equal(_.escape('a'), 'a'); assert.equal(_.escape(''), '<a>'); assert.equal(_.escape({[_.escapeMethod]() { return 'a'; }}), 'a'); assert.equal(_.escape({[_.escapeMethod]() { return ''; }}), ''); assert.equal(_.escape(Markup('a')), 'a'); assert.equal(_.escape(Markup('')), ''); assert.equal(_.escape(Markup`a`), 'a'); assert.equal(_.escape(Markup``), ''); }); QUnit.module('t-out', {}, () => { QUnit.test("basics", async assert => { assert.expect(5); const qweb = new QWeb2.Engine; await add_template(qweb, `

`); assert.equal( qweb.render('t-out', {value: 'test'}), '

<i>test</i>

', "regular t-out should just escape the contents" ); assert.equal( qweb.render('t-out', {value: Markup('test')}), '

test

', "Markup contents should not be escaped" ); assert.equal( qweb.render('t-out', {value: Markup`test ${1}`}), '

test 1

', "markup template string should not be escaped" ); const teststr = 'test'; assert.equal( qweb.render('t-out', {value: Markup`${teststr}`}), '

<i>test</i>

', "the markup template should not be escaped but what it uses should be" ); const testMarkup = Markup(teststr); assert.equal( qweb.render('t-out', {value: Markup`${testMarkup}`}), '

test

', "markupception" ); }); QUnit.test("Set", async assert => { assert.expect(4); const qweb = new QWeb2.Engine; await add_template(qweb, `
`); assert.equal(trim(qweb.render('litval')), '<a/>'); assert.equal(trim(qweb.render('body')), ''); assert.equal(trim(qweb.render('value', {val: ''})), '<a/>'); assert.equal(trim(qweb.render('bodyout', {val: ''})), '<a/>'); }); }) }); });