From 85318b3ae6b40e7fd30e90a7d97f71015e4a18a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Mon, 25 May 2020 10:33:14 +0200 Subject: [PATCH] [FIX] qweb: properly handle subtemplates in shared templates With QWeb, we can register globally templates (using the xml tag or the registerTemplate function). However, these templates, once compiled, can generate sub template compiled functions. Before this commit, these sub functions were local to a specific instance. This means that creating a new QWeb instance and rendering a global parent template would crash, since it was unable to find the actual sub function. This commit fixes the issue: the sub functions are now shared statically, but with a unique ID, so we do not have issues with sub functions having a same name in different QWeb instance. closes #701 --- src/qweb/base_directives.ts | 16 +- src/qweb/qweb.ts | 10 +- .../__snapshots__/component.test.ts.snap | 6 +- tests/component/component.test.ts | 17 +- tests/qweb/__snapshots__/qweb.test.ts.snap | 154 +++++++----------- tests/qweb/qweb.test.ts | 29 +++- 6 files changed, 118 insertions(+), 114 deletions(-) diff --git a/src/qweb/base_directives.ts b/src/qweb/base_directives.ts index 9ac3fe46..6031e524 100644 --- a/src/qweb/base_directives.ts +++ b/src/qweb/base_directives.ts @@ -234,10 +234,12 @@ QWeb.addDirective({ // Step 2: compile target template in sub templates // ------------------------------------------------ - if (!qweb.subTemplates[subTemplate]) { - qweb.subTemplates[subTemplate] = true; + let subId = qweb.subTemplates[subTemplate]; + if (!subId) { + subId = QWeb.nextId++; + qweb.subTemplates[subTemplate] = subId; const subTemplateFn = qweb._compile(subTemplate, nodeTemplate.elem, ctx, true); - qweb.subTemplates[subTemplate] = subTemplateFn; + QWeb.subTemplates[subId] = subTemplateFn; } // Step 3: compile t-call body if necessary @@ -275,12 +277,16 @@ QWeb.addDirective({ const parentNode = ctx.parentNode ? `c${ctx.parentNode}` : "result"; const extra = `Object.assign({}, extra, {parentNode: ${parentNode}, parent: ${parentComponent}, key: ${key}})`; if (ctx.parentNode) { - ctx.addLine(`this.subTemplates['${subTemplate}'].call(this, ${callingScope}, ${extra});`); + ctx.addLine( + `this.constructor.subTemplates['${subId}'].call(this, ${callingScope}, ${extra});` + ); } else { // this is a t-call with no parentnode, we need to extract the result ctx.rootContext.shouldDefineResult = true; ctx.addLine(`result = []`); - ctx.addLine(`this.subTemplates['${subTemplate}'].call(this, ${callingScope}, ${extra});`); + ctx.addLine( + `this.constructor.subTemplates['${subId}'].call(this, ${callingScope}, ${extra});` + ); ctx.addLine(`result = result[0]`); } diff --git a/src/qweb/qweb.ts b/src/qweb/qweb.ts index a3e94cfd..618807ec 100644 --- a/src/qweb/qweb.ts +++ b/src/qweb/qweb.ts @@ -212,10 +212,12 @@ export class QWeb extends EventBus { static slots = {}; static nextSlotId = 1; - // recursiveTemplates contains sub templates called with t-call, but which - // ends up in recursive situations. This is very similar to the slot situation, - // as in we need to propagate the scope. - subTemplates = {}; + // subTemplates are stored in two objects: a (local) mapping from a name to an + // id, and a (global) mapping from an id to the compiled function. This is + // necessary to ensure that global templates can be called with more than one + // QWeb instance. + subTemplates: {[key: string]: number} = {}; + static subTemplates: {[id: number]: Function} = {}; isUpdating: boolean = false; translateFn?: QWebConfig["translateFn"]; diff --git a/tests/component/__snapshots__/component.test.ts.snap b/tests/component/__snapshots__/component.test.ts.snap index 6ea5f6d3..918198d0 100644 --- a/tests/component/__snapshots__/component.test.ts.snap +++ b/tests/component/__snapshots__/component.test.ts.snap @@ -1253,7 +1253,7 @@ exports[`other directives with t-component t-set can't alter from within callee if (scope.iter != null) { c2.push({text: scope.iter}); } - this.subTemplates['ChildWidget'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__4__'})); + this.constructor.subTemplates['2'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__4__'})); let c5 = [], p5 = {key:5}; let vn5 = h('p', p5, c5); c1.push(vn5); @@ -1289,7 +1289,7 @@ exports[`other directives with t-component t-set can't alter in t-call body 1`] utils.getScope(scope, 'iter').iter = 'inCall'; scope[utils.zero] = c__0; } - this.subTemplates['ChildWidget'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__5__'})); + this.constructor.subTemplates['2'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__5__'})); scope = _origScope4; } let c6 = [], p6 = {key:6}; @@ -1410,7 +1410,7 @@ exports[`random stuff/miscellaneous can inject values in tagged templates 1`] = let h = this.h; let c1 = [], p1 = {key:1}; let vn1 = h('div', p1, c1); - this.subTemplates['__template__1'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__4__'})); + this.constructor.subTemplates['3'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__4__'})); return vn1; }" `; diff --git a/tests/component/component.test.ts b/tests/component/component.test.ts index 96df72a2..d3dafbc6 100644 --- a/tests/component/component.test.ts +++ b/tests/component/component.test.ts @@ -3825,7 +3825,9 @@ describe("t-call", () => { expect(fixture.innerHTML).toBe("

lucas

"); fixture.querySelector("p")!.click(); - expect(env.qweb.subTemplates["sub"].toString()).toMatchSnapshot(); + const subId = env.qweb.subTemplates["sub"]; + const subFn = QWeb.subTemplates[subId] as any; + expect(subFn.toString()).toMatchSnapshot(); }); test("parent is set within t-call", async () => { @@ -3847,7 +3849,10 @@ describe("t-call", () => { expect(fixture.innerHTML).toBe("
lucas
"); expect(child.__owl__.parent).toBe(parent); - expect(env.qweb.subTemplates["sub"].toString()).toMatchSnapshot(); + const subId = env.qweb.subTemplates["sub"]; + const subFn = QWeb.subTemplates[subId] as any; + + expect(subFn.toString()).toMatchSnapshot(); }); test("t-call in t-foreach and children component", async () => { @@ -3889,7 +3894,9 @@ describe("t-call", () => { expect(fixture.innerHTML).toBe("lucas"); expect(child.__owl__.parent).toBe(parent); - expect(env.qweb.subTemplates["sub"].toString()).toMatchSnapshot(); + const subId = env.qweb.subTemplates["sub"]; + const subFn = QWeb.subTemplates[subId] as any; + expect(subFn.toString()).toMatchSnapshot(); }); test("handlers with arguments are properly bound through a t-call", async () => { @@ -3905,7 +3912,9 @@ describe("t-call", () => { } const parent = new Parent(); await parent.mount(fixture); - expect(env.qweb.subTemplates["sub"].toString()).toMatchSnapshot(); + const subId = env.qweb.subTemplates["sub"]; + const subFn = QWeb.subTemplates[subId] as any; + expect(subFn.toString()).toMatchSnapshot(); fixture.querySelector("p")!.click(); }); diff --git a/tests/qweb/__snapshots__/qweb.test.ts.snap b/tests/qweb/__snapshots__/qweb.test.ts.snap index b56c3230..abdc10ce 100644 --- a/tests/qweb/__snapshots__/qweb.test.ts.snap +++ b/tests/qweb/__snapshots__/qweb.test.ts.snap @@ -749,7 +749,7 @@ exports[`loading templates can load a few templates from a xml string 1`] = ` let h = this.h; let c1 = [], p1 = {key:1}; let vn1 = h('ul', p1, c1); - this.subTemplates['items'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__4__'})); + this.constructor.subTemplates['1'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__4__'})); return vn1; }" `; @@ -803,23 +803,23 @@ exports[`misc global 1`] = ` scope[utils.zero] = c__0; } let k14 = \`__14__\${key1}__\`; - this.subTemplates['_callee-uses-foo'].call(this, scope, Object.assign({}, extra, {parentNode: c__0, parent: utils.getComponent(context), key: k14})); + this.constructor.subTemplates['2'].call(this, scope, Object.assign({}, extra, {parentNode: c__0, parent: utils.getComponent(context), key: k14})); scope = _origScope13; } let k15 = \`__15__\${key1}__\`; - this.subTemplates['_callee-uses-foo'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c__0, parent: utils.getComponent(context), key: k15})); + this.constructor.subTemplates['2'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c__0, parent: utils.getComponent(context), key: k15})); utils.getScope(scope, 'foo').foo = 'bbb'; let k16 = \`__16__\${key1}__\`; - this.subTemplates['_callee-uses-foo'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c__0, parent: utils.getComponent(context), key: k16})); + this.constructor.subTemplates['2'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c__0, parent: utils.getComponent(context), key: k16})); scope[utils.zero] = c__0; } let k17 = \`__17__\${key1}__\`; - this.subTemplates['_callee-asc'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: k17})); + this.constructor.subTemplates['1'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: k17})); scope = _origScope10; } } scope = _origScope5; - this.subTemplates['_callee-asc-toto'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__20__'})); + this.constructor.subTemplates['3'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__20__'})); return vn1; }" `; @@ -1108,25 +1108,11 @@ exports[`t-call (template calling basic caller 1`] = ` let h = this.h; let c1 = [], p1 = {key:1}; let vn1 = h('div', p1, c1); - this.subTemplates['_basic-callee'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__3__'})); + this.constructor.subTemplates['1'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__3__'})); return vn1; }" `; -exports[`t-call (template calling basic caller 2`] = ` -"function anonymous(context, extra -) { - // Template name: \\"_basic-callee\\" - let h = this.h; - let c1 = extra.parentNode; - let key0 = extra.key || \\"\\"; - let c2 = [], p2 = {key:\`\${key0}_2\`}; - let vn2 = h('span', p2, c2); - c1.push(vn2); - c2.push({text: \`ok\`}); -}" -`; - exports[`t-call (template calling basic caller, no parent node 1`] = ` "function anonymous(context, extra ) { @@ -1136,26 +1122,12 @@ exports[`t-call (template calling basic caller, no parent node 1`] = ` let result; let h = this.h; result = [] - this.subTemplates['_basic-callee'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: result, parent: utils.getComponent(context), key: '__3__'})); + this.constructor.subTemplates['1'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: result, parent: utils.getComponent(context), key: '__3__'})); result = result[0] return result; }" `; -exports[`t-call (template calling basic caller, no parent node 2`] = ` -"function anonymous(context, extra -) { - // Template name: \\"_basic-callee\\" - let h = this.h; - let c1 = extra.parentNode; - let key0 = extra.key || \\"\\"; - let c2 = [], p2 = {key:\`\${key0}_2\`}; - let vn2 = h('div', p2, c2); - c1.push(vn2); - c2.push({text: \`ok\`}); -}" -`; - exports[`t-call (template calling call with several sub nodes on same line 1`] = ` "function anonymous(context, extra ) { @@ -1182,28 +1154,14 @@ exports[`t-call (template calling call with several sub nodes on same line 1`] = c5.push({text: \`yay\`}); scope[utils.zero] = c__0; } - this.subTemplates['SubTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__6__'})); + this.constructor.subTemplates['1'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__6__'})); scope = _origScope3; } return vn1; }" `; -exports[`t-call (template calling call with several sub nodes on same line 2`] = ` -"function anonymous(context, extra -) { - // Template name: \\"SubTemplate\\" - let utils = this.constructor.utils; - let scope = Object.create(context); - let h = this.h; - let c1 = extra.parentNode; - let key0 = extra.key || \\"\\"; - let c2 = [], p2 = {key:\`\${key0}_2\`}; - let vn2 = h('div', p2, c2); - c1.push(vn2); - c2.push(...scope[utils.zero]); -}" -`; +exports[`t-call (template calling call with several sub nodes on same line 2`] = `"1"`; exports[`t-call (template calling cascading t-call t-raw='0' 1`] = ` "function anonymous(context, extra @@ -1231,7 +1189,7 @@ exports[`t-call (template calling cascading t-call t-raw='0' 1`] = ` c14.push({text: \`yay\`}); scope[utils.zero] = c__0; } - this.subTemplates['SubTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__15__'})); + this.constructor.subTemplates['1'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__15__'})); scope = _origScope12; } return vn1; @@ -1248,7 +1206,7 @@ exports[`t-call (template calling inherit context 1`] = ` let c1 = [], p1 = {key:1}; let vn1 = h('div', p1, c1); scope.foo = 1; - this.subTemplates['_callee-uses-foo'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__2__'})); + this.constructor.subTemplates['1'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__2__'})); return vn1; }" `; @@ -1267,7 +1225,7 @@ exports[`t-call (template calling recursive template, part 1 1`] = ` c1.push(vn2); c2.push({text: \`hey\`}); if (false) { - this.subTemplates['recursive'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__6__'})); + this.constructor.subTemplates['1'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__6__'})); } return vn1; }" @@ -1291,7 +1249,7 @@ exports[`t-call (template calling recursive template, part 1 2`] = ` c4.push({text: \`hey\`}); if (false) { let k5 = \`__5__\${key0}__\`; - this.subTemplates['recursive'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c3, parent: utils.getComponent(context), key: k5})); + this.constructor.subTemplates['1'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c3, parent: utils.getComponent(context), key: k5})); } }" `; @@ -1314,7 +1272,7 @@ exports[`t-call (template calling recursive template, part 2 1`] = ` utils.getScope(scope, 'node').node = scope['root']; scope[utils.zero] = c__0; } - this.subTemplates['nodeTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__12__'})); + this.constructor.subTemplates['1'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__12__'})); scope = _origScope11; } return vn1; @@ -1367,7 +1325,7 @@ exports[`t-call (template calling recursive template, part 2 2`] = ` scope[utils.zero] = c__0; } let k10 = \`__10__\${key0}__\${key1}__\`; - this.subTemplates['nodeTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c2, parent: utils.getComponent(context), key: k10})); + this.constructor.subTemplates['1'].call(this, scope, Object.assign({}, extra, {parentNode: c2, parent: utils.getComponent(context), key: k10})); scope = _origScope9; } } @@ -1393,7 +1351,7 @@ exports[`t-call (template calling recursive template, part 3 1`] = ` utils.getScope(scope, 'node').node = scope['root']; scope[utils.zero] = c__0; } - this.subTemplates['nodeTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__12__'})); + this.constructor.subTemplates['1'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__12__'})); scope = _origScope11; } return vn1; @@ -1446,7 +1404,7 @@ exports[`t-call (template calling recursive template, part 3 2`] = ` scope[utils.zero] = c__0; } let k10 = \`__10__\${key0}__\${key1}__\`; - this.subTemplates['nodeTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c2, parent: utils.getComponent(context), key: k10})); + this.constructor.subTemplates['1'].call(this, scope, Object.assign({}, extra, {parentNode: c2, parent: utils.getComponent(context), key: k10})); scope = _origScope9; } } @@ -1473,7 +1431,7 @@ exports[`t-call (template calling recursive template, part 4: with t-set recursi utils.getScope(scope, 'node').node = scope['root']; scope[utils.zero] = c__0; } - this.subTemplates['nodeTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__12__'})); + this.constructor.subTemplates['1'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__12__'})); scope = _origScope11; } return vn1; @@ -1531,7 +1489,7 @@ exports[`t-call (template calling recursive template, part 4: with t-set recursi scope[utils.zero] = c__0; } let k10 = \`__10__\${key0}__\${key1}__\`; - this.subTemplates['nodeTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c2, parent: utils.getComponent(context), key: k10})); + this.constructor.subTemplates['1'].call(this, scope, Object.assign({}, extra, {parentNode: c2, parent: utils.getComponent(context), key: k10})); scope = _origScope9; } } @@ -1557,7 +1515,7 @@ exports[`t-call (template calling scoped parameters 1`] = ` utils.getScope(scope, 'foo').foo = 42; scope[utils.zero] = c__0; } - this.subTemplates['_basic-callee'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__3__'})); + this.constructor.subTemplates['1'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__3__'})); scope = _origScope2; } if (scope.foo != null) { @@ -1577,26 +1535,12 @@ exports[`t-call (template calling t-call with t-if 1`] = ` let c1 = [], p1 = {key:1}; let vn1 = h('div', p1, c1); if (scope['flag']) { - this.subTemplates['sub'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__3__'})); + this.constructor.subTemplates['1'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__3__'})); } return vn1; }" `; -exports[`t-call (template calling t-call with t-if 2`] = ` -"function anonymous(context, extra -) { - // Template name: \\"sub\\" - let h = this.h; - let c1 = extra.parentNode; - let key0 = extra.key || \\"\\"; - let c2 = [], p2 = {key:\`\${key0}_2\`}; - let vn2 = h('span', p2, c2); - c1.push(vn2); - c2.push({text: \`ok\`}); -}" -`; - exports[`t-call (template calling t-call with t-set inside and outside 1`] = ` "function anonymous(context, extra ) { @@ -1634,7 +1578,7 @@ exports[`t-call (template calling t-call with t-set inside and outside 1`] = ` scope[utils.zero] = c__0; } let k9 = \`__9__\${key1}__\`; - this.subTemplates['sub'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: k9})); + this.constructor.subTemplates['1'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: k9})); scope = _origScope8; } } @@ -1653,7 +1597,7 @@ exports[`t-call (template calling t-call with t-set inside and outside. 2 1`] = let c1 = [], p1 = {key:1}; let vn1 = h('p', p1, c1); scope.w = 'fromwrapper'; - this.subTemplates['main'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__11__'})); + this.constructor.subTemplates['1'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__11__'})); return vn1; }" `; @@ -1669,7 +1613,7 @@ exports[`t-call (template calling t-call, conditional and t-set in t-call body 1 let vn1 = h('div', p1, c1); scope.v1 = 'elif'; if (scope.v1==='if') { - this.subTemplates['callee1'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__3__'})); + this.constructor.subTemplates['1'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__3__'})); } else if (scope.v1==='elif') { { @@ -1681,7 +1625,7 @@ exports[`t-call (template calling t-call, conditional and t-set in t-call body 1 utils.getScope(scope, 'v').v = 'success'; scope[utils.zero] = c__0; } - this.subTemplates['callee2'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__7__'})); + this.constructor.subTemplates['2'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__7__'})); scope = _origScope6; } } @@ -1698,7 +1642,35 @@ exports[`t-call (template calling t-call, global templates 1`] = ` let h = this.h; let c1 = [], p1 = {key:1}; let vn1 = h('div', p1, c1); - this.subTemplates['john'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__3__'})); + this.constructor.subTemplates['1'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__3__'})); + return vn1; +}" +`; + +exports[`t-call (template calling two different QWeb instances, and shared templates 1`] = ` +"function anonymous(context, extra +) { + // Template name: \\"main\\" + let utils = this.constructor.utils; + let scope = Object.create(context); + let h = this.h; + let c1 = [], p1 = {key:1}; + let vn1 = h('div', p1, c1); + this.constructor.subTemplates['1'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__3__'})); + return vn1; +}" +`; + +exports[`t-call (template calling two different QWeb instances, and shared templates 2`] = ` +"function anonymous(context, extra +) { + // Template name: \\"main\\" + let utils = this.constructor.utils; + let scope = Object.create(context); + let h = this.h; + let c1 = [], p1 = {key:1}; + let vn1 = h('div', p1, c1); + this.constructor.subTemplates['1'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__3__'})); return vn1; }" `; @@ -1721,7 +1693,7 @@ exports[`t-call (template calling with unused body 1`] = ` scope[utils.zero] = c__0; } result = [] - this.subTemplates['_basic-callee'].call(this, scope, Object.assign({}, extra, {parentNode: result, parent: utils.getComponent(context), key: '__4__'})); + this.constructor.subTemplates['1'].call(this, scope, Object.assign({}, extra, {parentNode: result, parent: utils.getComponent(context), key: '__4__'})); result = result[0] scope = _origScope3; } @@ -1747,7 +1719,7 @@ exports[`t-call (template calling with unused setbody 1`] = ` scope[utils.zero] = c__0; } result = [] - this.subTemplates['_basic-callee'].call(this, scope, Object.assign({}, extra, {parentNode: result, parent: utils.getComponent(context), key: '__4__'})); + this.constructor.subTemplates['1'].call(this, scope, Object.assign({}, extra, {parentNode: result, parent: utils.getComponent(context), key: '__4__'})); result = result[0] scope = _origScope3; } @@ -1773,7 +1745,7 @@ exports[`t-call (template calling with used body 1`] = ` scope[utils.zero] = c__0; } result = [] - this.subTemplates['_callee-printsbody'].call(this, scope, Object.assign({}, extra, {parentNode: result, parent: utils.getComponent(context), key: '__4__'})); + this.constructor.subTemplates['1'].call(this, scope, Object.assign({}, extra, {parentNode: result, parent: utils.getComponent(context), key: '__4__'})); result = result[0] scope = _origScope3; } @@ -1799,7 +1771,7 @@ exports[`t-call (template calling with used set body 1`] = ` utils.getScope(scope, 'foo').foo = 'ok'; scope[utils.zero] = c__0; } - this.subTemplates['_callee-uses-foo'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__4__'})); + this.constructor.subTemplates['1'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__4__'})); scope = _origScope3; } return vn1; @@ -1949,7 +1921,7 @@ exports[`t-esc t-esc inside t-call, with t-set outside 1`] = ` let c2 = new utils.VDomArray(); c2.push({text: \`Hi\`}); scope.v = c2 - this.subTemplates['sub'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__5__'})); + this.constructor.subTemplates['1'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__5__'})); return vn1; }" `; @@ -2018,7 +1990,7 @@ exports[`t-esc t-esc=0 is escaped 1`] = ` c4.push({text: \`escaped\`}); scope[utils.zero] = c__0; } - this.subTemplates['test'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__5__'})); + this.constructor.subTemplates['1'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__5__'})); scope = _origScope3; } return vn1; @@ -2814,7 +2786,7 @@ exports[`t-on t-on with t-call 1`] = ` let h = this.h; let c1 = [], p1 = {key:1}; let vn1 = h('div', p1, c1); - this.subTemplates['sub'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__4__'})); + this.constructor.subTemplates['1'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__4__'})); return vn1; }" `; @@ -2828,7 +2800,7 @@ exports[`t-on t-on, with arguments and t-call 1`] = ` let h = this.h; let c1 = [], p1 = {key:1}; let vn1 = h('div', p1, c1); - this.subTemplates['sub'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__4__'})); + this.constructor.subTemplates['1'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__4__'})); return vn1; }" `; diff --git a/tests/qweb/qweb.test.ts b/tests/qweb/qweb.test.ts index 4036633a..c8919e29 100644 --- a/tests/qweb/qweb.test.ts +++ b/tests/qweb/qweb.test.ts @@ -747,7 +747,7 @@ describe("t-call (template calling", () => { qweb.addTemplate("caller", '
'); const expected = "
ok
"; expect(renderToString(qweb, "caller")).toBe(expected); - expect(qweb.subTemplates["_basic-callee"].toString()).toMatchSnapshot(); + expect(qweb.subTemplates["_basic-callee"]).toBeTruthy(); }); test("basic caller, no parent node", () => { @@ -755,7 +755,7 @@ describe("t-call (template calling", () => { qweb.addTemplate("caller", ''); const expected = "
ok
"; expect(renderToString(qweb, "caller")).toBe(expected); - expect(qweb.subTemplates["_basic-callee"].toString()).toMatchSnapshot(); + expect(qweb.subTemplates["_basic-callee"]).toBeTruthy(); }); test("t-call with t-if", () => { @@ -763,7 +763,7 @@ describe("t-call (template calling", () => { qweb.addTemplate("caller", '
'); const expected = "
ok
"; expect(renderToString(qweb, "caller", { flag: true })).toBe(expected); - expect(qweb.subTemplates["sub"].toString()).toMatchSnapshot(); + expect(qweb.subTemplates["sub"]).toBeTruthy(); }); test("t-call not allowed on a non t node", () => { @@ -898,7 +898,8 @@ describe("t-call (template calling", () => { `); const expected = "
hey
"; expect(renderToString(qweb, "recursive")).toBe(expected); - const recursiveFn = Object.values(qweb.subTemplates)[0] as any; + const subId = qweb.subTemplates["recursive"]; + const recursiveFn = QWeb.subTemplates[subId] as any; expect(recursiveFn.toString()).toMatchSnapshot(); }); @@ -926,7 +927,8 @@ describe("t-call (template calling", () => { expect(renderToString(qweb, "Parent", { root }, { fiber: { vars: {}, scope: {} } })).toBe( expected ); - const recursiveFn = Object.values(qweb.subTemplates)[0] as any; + const subId = qweb.subTemplates["nodeTemplate"]; + const recursiveFn = QWeb.subTemplates[subId] as any; expect(recursiveFn.toString()).toMatchSnapshot(); }); @@ -953,7 +955,8 @@ describe("t-call (template calling", () => { const expected = "

a

b

d

c

"; expect(renderToString(qweb, "Parent", { root }, { fiber: {} })).toBe(expected); - const recursiveFn = Object.values(qweb.subTemplates)[0] as any; + const subId = qweb.subTemplates["nodeTemplate"]; + const recursiveFn = QWeb.subTemplates[subId] as any; expect(recursiveFn.toString()).toMatchSnapshot(); }); @@ -985,7 +988,8 @@ describe("t-call (template calling", () => { const expected = "

a 2

b 3

c 4

d 5

"; expect(renderToString(qweb, "Parent", { root })).toBe(expected); - const recursiveFn = Object.values(qweb.subTemplates)[0] as any; + const subId = qweb.subTemplates["nodeTemplate"]; + const recursiveFn = QWeb.subTemplates[subId] as any; expect(recursiveFn.toString()).toMatchSnapshot(); }); @@ -1057,6 +1061,17 @@ describe("t-call (template calling", () => { const context = { list: [{ val: 1 }, { val: 2 }, { val: 3 }] }; expect(trim(renderToString(qweb, "wrapper", context))).toBe(expected); }); + + test("two different QWeb instances, and shared templates", () => { + QWeb.registerTemplate("sub", `ok`); + QWeb.registerTemplate("main", `
`); + + const qweb1 = new QWeb(); + const qweb2 = new QWeb(); + + expect(renderToString(qweb1, "main")).toBe("
ok
"); + expect(renderToString(qweb2, "main")).toBe("
ok
"); + }); }); describe("foreach", () => {