From 7ebea5a4865920fd38426f54c3950ee8a64294aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Tue, 2 Apr 2019 09:55:33 +0200 Subject: [PATCH] imp: better error message when evaluating/compiling templates fixes #3 --- src/qweb.ts | 21 ++++++++++++++++----- tests/__snapshots__/qweb.test.ts.snap | 18 +++++++++--------- tests/qweb.test.ts | 17 +++++++++++++++++ 3 files changed, 42 insertions(+), 14 deletions(-) diff --git a/src/qweb.ts b/src/qweb.ts index f62d0521..68e4d61f 100644 --- a/src/qweb.ts +++ b/src/qweb.ts @@ -53,9 +53,11 @@ export class Context { shouldProtectContext: boolean = false; inLoop: boolean = false; inPreTag: boolean = false; + templateName: string; - constructor() { + constructor(name?: string) { this.rootContext = this; + this.templateName = name || "noname"; this.addLine("let h = this.utils.h;"); } @@ -337,7 +339,7 @@ export class QWeb { const mainNode = this.processedTemplates[name]; const isDebug = (mainNode).attributes.hasOwnProperty("t-debug"); - const ctx = new Context(); + const ctx = new Context(name); this._compileNode(mainNode, ctx); if (ctx.shouldProtectContext) { @@ -364,7 +366,12 @@ export class QWeb { ctx.code.join("\n") ) as CompiledTemplate; } catch (e) { - throw new Error(`Invalid template (or compiled code): ${e.message}`); + throw new Error( + `Invalid generated code while compiling template '${ctx.templateName.replace( + /`/g, + "'" + )}': ${e.message}` + ); } if (isDebug) { console.log( @@ -869,15 +876,19 @@ const onDirective: Directive = { extraArgs = args.slice(1, -1); return ""; }); + let error = `(function () {throw new Error('Missing handler \\'' + '${handler}' + \`\\' when evaluating template '${ctx.templateName.replace( + /`/g, + "'" + )}'\`)})()`; if (extraArgs) { ctx.addLine( - `p${nodeID}.on['${eventName}'] = context['${handler}'].bind(owner, ${ctx.formatExpression( + `p${nodeID}.on['${eventName}'] = (context['${handler}'] || ${error}).bind(owner, ${ctx.formatExpression( extraArgs )});` ); } else { ctx.addLine( - `extra.handlers['${eventName}' + ${nodeID}] = extra.handlers['${eventName}' + ${nodeID}] || context['${handler}'].bind(owner);` + `extra.handlers['${eventName}' + ${nodeID}] = extra.handlers['${eventName}' + ${nodeID}] || (context['${handler}'] || ${error}).bind(owner);` ); ctx.addLine( `p${nodeID}.on['${eventName}'] = extra.handlers['${eventName}' + ${nodeID}];` diff --git a/tests/__snapshots__/qweb.test.ts.snap b/tests/__snapshots__/qweb.test.ts.snap index 414b8711..b6cac985 100644 --- a/tests/__snapshots__/qweb.test.ts.snap +++ b/tests/__snapshots__/qweb.test.ts.snap @@ -987,7 +987,7 @@ exports[`t-on can bind event handler 1`] = ` let h = this.utils.h; let c1 = [], p1 = {key:1,on:{}}; let vn1 = h('button', p1, c1); - extra.handlers['click' + 1] = extra.handlers['click' + 1] || context['add'].bind(owner); + extra.handlers['click' + 1] = extra.handlers['click' + 1] || (context['add'] || (function () {throw new Error('Missing handler \\\\'' + 'add' + \`\\\\' when evaluating template 'test'\`)})()).bind(owner); p1.on['click'] = extra.handlers['click' + 1]; c1.push({text: \`Click\`}); return vn1; @@ -1001,7 +1001,7 @@ exports[`t-on can bind handlers with arguments 1`] = ` let h = this.utils.h; let c1 = [], p1 = {key:1,on:{}}; let vn1 = h('button', p1, c1); - p1.on['click'] = context['add'].bind(owner, 5); + p1.on['click'] = (context['add'] || (function () {throw new Error('Missing handler \\\\'' + 'add' + \`\\\\' when evaluating template 'test'\`)})()).bind(owner, 5); c1.push({text: \`Click\`}); return vn1; }" @@ -1014,7 +1014,7 @@ exports[`t-on can bind handlers with empty object (with non empty inner string 1 let h = this.utils.h; let c1 = [], p1 = {key:1,on:{}}; let vn1 = h('button', p1, c1); - p1.on['click'] = context['doSomething'].bind(owner, {}); + p1.on['click'] = (context['doSomething'] || (function () {throw new Error('Missing handler \\\\'' + 'doSomething' + \`\\\\' when evaluating template 'test'\`)})()).bind(owner, {}); c1.push({text: \`Click\`}); return vn1; }" @@ -1027,7 +1027,7 @@ exports[`t-on can bind handlers with empty object 1`] = ` let h = this.utils.h; let c1 = [], p1 = {key:1,on:{}}; let vn1 = h('button', p1, c1); - p1.on['click'] = context['doSomething'].bind(owner, {}); + p1.on['click'] = (context['doSomething'] || (function () {throw new Error('Missing handler \\\\'' + 'doSomething' + \`\\\\' when evaluating template 'test'\`)})()).bind(owner, {}); c1.push({text: \`Click\`}); return vn1; }" @@ -1059,7 +1059,7 @@ exports[`t-on can bind handlers with loop variable as argument 1`] = ` let c6 = [], p6 = {key:6,on:{}}; let vn6 = h('a', p6, c6); c5.push(vn6); - p6.on['click'] = context['activate'].bind(owner, context['action']); + p6.on['click'] = (context['activate'] || (function () {throw new Error('Missing handler \\\\'' + 'activate' + \`\\\\' when evaluating template 'test'\`)})()).bind(owner, context['action']); c6.push({text: \`link\`}); } return vn1; @@ -1073,7 +1073,7 @@ exports[`t-on can bind handlers with object arguments 1`] = ` let h = this.utils.h; let c1 = [], p1 = {key:1,on:{}}; let vn1 = h('button', p1, c1); - p1.on['click'] = context['add'].bind(owner, {val: 5}); + p1.on['click'] = (context['add'] || (function () {throw new Error('Missing handler \\\\'' + 'add' + \`\\\\' when evaluating template 'test'\`)})()).bind(owner, {val: 5}); c1.push({text: \`Click\`}); return vn1; }" @@ -1086,9 +1086,9 @@ exports[`t-on can bind two event handlers 1`] = ` let h = this.utils.h; let c1 = [], p1 = {key:1,on:{}}; let vn1 = h('button', p1, c1); - extra.handlers['click' + 1] = extra.handlers['click' + 1] || context['handleClick'].bind(owner); + extra.handlers['click' + 1] = extra.handlers['click' + 1] || (context['handleClick'] || (function () {throw new Error('Missing handler \\\\'' + 'handleClick' + \`\\\\' when evaluating template 'test'\`)})()).bind(owner); p1.on['click'] = extra.handlers['click' + 1]; - extra.handlers['dblclick' + 1] = extra.handlers['dblclick' + 1] || context['handleDblClick'].bind(owner); + extra.handlers['dblclick' + 1] = extra.handlers['dblclick' + 1] || (context['handleDblClick'] || (function () {throw new Error('Missing handler \\\\'' + 'handleDblClick' + \`\\\\' when evaluating template 'test'\`)})()).bind(owner); p1.on['dblclick'] = extra.handlers['dblclick' + 1]; c1.push({text: \`Click\`}); return vn1; @@ -1102,7 +1102,7 @@ exports[`t-on handler is bound to proper owner 1`] = ` let h = this.utils.h; let c1 = [], p1 = {key:1,on:{}}; let vn1 = h('button', p1, c1); - extra.handlers['click' + 1] = extra.handlers['click' + 1] || context['add'].bind(owner); + extra.handlers['click' + 1] = extra.handlers['click' + 1] || (context['add'] || (function () {throw new Error('Missing handler \\\\'' + 'add' + \`\\\\' when evaluating template 'test'\`)})()).bind(owner); p1.on['click'] = extra.handlers['click' + 1]; c1.push({text: \`Click\`}); return vn1; diff --git a/tests/qweb.test.ts b/tests/qweb.test.ts index fb027ecf..8b585dfc 100644 --- a/tests/qweb.test.ts +++ b/tests/qweb.test.ts @@ -107,6 +107,23 @@ describe("error handling", () => { qweb.loadTemplates(">"); }).toThrow("Invalid XML in template"); }); + + test("nice error when t-on-directive is evaluated with a missing handler", () => { + qweb.addTemplate("templatename", `
`); + expect(() => qweb.render("templatename", {}, { handlers: [] })).toThrow( + "Missing handler 'somemethod' when evaluating template 'templatename'" + ); + }); + + test("error when compiled code is invalid", () => { + qweb.addTemplate( + "templatename", + `
` + ); + expect(() => qweb.render("templatename")).toThrow( + "Invalid generated code while compiling template 'templatename': Unexpected token }" + ); + }); }); describe("t-esc", () => {