From ca139166ab3067eca2f0a661e8d62e19ba05d3a4 Mon Sep 17 00:00:00 2001 From: Bruno Boi Date: Tue, 9 Nov 2021 15:16:37 +0100 Subject: [PATCH] [IMP] qweb: reintroduce t-tag directive will not be compatible with t-model directive ! --- src/qweb/compiler.ts | 35 ++++-- src/qweb/inline_expressions.ts | 7 +- src/qweb/parser.ts | 21 ++-- tests/components/error_handling.test.ts | 15 ++- tests/components/style_class.test.ts | 3 +- tests/misc/portal.test.ts | 3 +- tests/qweb/__snapshots__/t_tag.test.ts.snap | 128 ++++++++++++++++++++ tests/qweb/parser.test.ts | 60 +++++++++ tests/qweb/t_tag.test.ts | 51 +++++++- 9 files changed, 289 insertions(+), 34 deletions(-) create mode 100644 tests/qweb/__snapshots__/t_tag.test.ts.snap diff --git a/src/qweb/compiler.ts b/src/qweb/compiler.ts index 15e92dd0..c76e4196 100644 --- a/src/qweb/compiler.ts +++ b/src/qweb/compiler.ts @@ -54,6 +54,7 @@ class BlockDescription { varName: string; blockName: string; + dynamicTagName: string | null = null; isRoot: boolean = false; hasDynamicChildren: boolean = false; children: BlockDescription[] = []; @@ -95,6 +96,9 @@ class BlockDescription { if (hasChildren) { params += ", [" + this.children.map((c) => c.varName).join(", ") + "]"; } + if (this.dynamicTagName) { + return `toggler(${this.dynamicTagName}, ${this.blockName}(${this.dynamicTagName})(${params}))`; + } return `${this.blockName}(${params})`; } else if (this.type === "list") { return `list(c_block${this.id})`; @@ -304,7 +308,14 @@ export class QWebCompiler { this.addLine(``); for (let block of this.blocks) { if (block.dom) { - this.addLine(`let ${block.blockName} = createBlock(\`${block.asXmlString()}\`);`); + let xmlString = block.asXmlString(); + if (block.dynamicTagName) { + xmlString = xmlString.replace(/^<\w+/, `<\${tag || '${block.dom.nodeName}'}`); + xmlString = xmlString.replace(/\w+>$/, `\${tag || '${block.dom.nodeName}'}>`); + this.addLine(`let ${block.blockName} = tag => createBlock(\`${xmlString}\`);`); + } else { + this.addLine(`let ${block.blockName} = createBlock(\`${xmlString}\`);`); + } } } } @@ -514,11 +525,19 @@ export class QWebCompiler { compileTDomNode(ast: ASTDomNode, ctx: Context) { let { block, forceNewBlock } = ctx; - const isNewBlock = !block || forceNewBlock; + const isNewBlock = !block || forceNewBlock || ast.dynamicTag !== null; let codeIdx = this.target.code.length; if (isNewBlock) { + if (ast.dynamicTag && ctx.block) { + this.insertAnchor(ctx.block!); + } block = this.createBlock(block, "block", ctx); this.blocks.push(block); + if (ast.dynamicTag) { + const tagExpr = this.generateId("tag"); + this.addLine(`let ${tagExpr} = ${compileExpr(ast.dynamicTag)};`); + block.dynamicTagName = tagExpr; + } } // attributes const attrs: { [key: string]: string } = {}; @@ -989,12 +1008,6 @@ export class QWebCompiler { propString = propVar; } - let keyArg = `key + \`${key}\``; - if (ctx.tKeyExpr) { - keyArg = `${ctx.tKeyExpr} + ${keyArg}`; - } - let blockArgs = `${expr}, ${propString}, ${keyArg}, node, ctx`; - // slots const hasSlot = !!Object.keys(ast.slots).length; let slotDef: string; @@ -1034,6 +1047,12 @@ export class QWebCompiler { // todo: check the forcenewblock condition this.insertAnchor(block); } + + let keyArg = `key + \`${key}\``; + if (ctx.tKeyExpr) { + keyArg = `${ctx.tKeyExpr} + ${keyArg}`; + } + const blockArgs = `${expr}, ${propString}, ${keyArg}, node, ctx`; let blockExpr = `component(${blockArgs})`; if (Object.keys(extraArgs).length) { this.shouldDefineAssign = true; diff --git a/src/qweb/inline_expressions.ts b/src/qweb/inline_expressions.ts index 2474c414..24a533d8 100644 --- a/src/qweb/inline_expressions.ts +++ b/src/qweb/inline_expressions.ts @@ -25,9 +25,10 @@ // Misc types, constants and helpers //------------------------------------------------------------------------------ -const RESERVED_WORDS = "true,false,NaN,null,undefined,debugger,console,window,in,instanceof,new,function,return,this,eval,void,Math,RegExp,Array,Object,Date".split( - "," -); +const RESERVED_WORDS = + "true,false,NaN,null,undefined,debugger,console,window,in,instanceof,new,function,return,this,eval,void,Math,RegExp,Array,Object,Date".split( + "," + ); const WORD_REPLACEMENT: { [key: string]: string } = Object.assign(Object.create(null), { and: "&&", diff --git a/src/qweb/parser.ts b/src/qweb/parser.ts index c185d0d9..e4401492 100644 --- a/src/qweb/parser.ts +++ b/src/qweb/parser.ts @@ -35,6 +35,7 @@ export interface ASTComment { export interface ASTDomNode { type: ASTType.DomNode; tag: string; + dynamicTag: string | null; attrs: { [key: string]: string }; content: AST[]; ref: string | null; @@ -289,7 +290,12 @@ const hasBracketsAtTheEnd = /\[[^\[]+\]\s*$/; function parseDOMNode(node: Element, ctx: ParsingContext): AST | null { const { tagName } = node; - if (tagName === "t") { + let dynamicTag = null; + if (node.hasAttribute("t-tag")) { + dynamicTag = node.getAttribute("t-tag"); + node.removeAttribute("t-tag"); + } + if (tagName === "t" && !dynamicTag) { return null; } const children: AST[] = []; @@ -373,6 +379,7 @@ function parseDOMNode(node: Element, ctx: ParsingContext): AST | null { return { type: ASTType.DomNode, tag: tagName, + dynamicTag, attrs, on, ref, @@ -404,13 +411,9 @@ function parseTEscNode(node: Element, ctx: ParsingContext): AST | null { } if (ast && ast.type === ASTType.DomNode) { return { - type: ASTType.DomNode, - tag: ast.tag, - attrs: ast.attrs, - on: ast.on, + ...ast, ref, content: [tesc], - model: ast.model, }; } if (ast && ast.type === ASTType.TComponent) { @@ -443,13 +446,9 @@ function parseTRawNode(node: Element, ctx: ParsingContext): AST | null { if (ast && ast.type === ASTType.DomNode) { tRaw.body = ast.content.length ? ast.content : null; return { - type: ASTType.DomNode, - tag: ast.tag, - attrs: ast.attrs, - on: ast.on, + ...ast, ref, content: [tRaw], - model: ast.model, }; } diff --git a/tests/components/error_handling.test.ts b/tests/components/error_handling.test.ts index 67ef11ef..963f5676 100644 --- a/tests/components/error_handling.test.ts +++ b/tests/components/error_handling.test.ts @@ -43,7 +43,8 @@ describe("basics", () => { expect(fixture.innerHTML).toBe(""); expect(status(parent)).toBe("destroyed"); expect(error).toBeDefined(); - const regexp = /Cannot read properties of undefined \(reading 'this'\)|Cannot read property 'this' of undefined/g; + const regexp = + /Cannot read properties of undefined \(reading 'this'\)|Cannot read property 'this' of undefined/g; expect(error.message).toMatch(regexp); }); @@ -112,7 +113,8 @@ describe.skip("errors and promises", () => { error = e; } expect(error).toBeDefined(); - const regexp = /Cannot read properties of undefined \(reading 'crash'\)|Cannot read property 'crash' of undefined/g; + const regexp = + /Cannot read properties of undefined \(reading 'crash'\)|Cannot read property 'crash' of undefined/g; expect(error.message).toMatch(regexp); expect(console.error).toBeCalledTimes(0); @@ -225,7 +227,8 @@ describe.skip("errors and promises", () => { error = e; } expect(error).toBeDefined(); - const regexp = /Cannot read properties of undefined \(reading 'crash'\)|Cannot read property 'crash' of undefined/g; + const regexp = + /Cannot read properties of undefined \(reading 'crash'\)|Cannot read property 'crash' of undefined/g; expect(error.message).toMatch(regexp); expect(console.error).toBeCalledTimes(0); @@ -251,7 +254,8 @@ describe.skip("errors and promises", () => { error = e; } expect(error).toBeDefined(); - const regexp = /Cannot read properties of undefined \(reading 'crash'\)|Cannot read property 'crash' of undefined/g; + const regexp = + /Cannot read properties of undefined \(reading 'crash'\)|Cannot read property 'crash' of undefined/g; expect(error.message).toMatch(regexp); expect(console.error).toBeCalledTimes(0); @@ -274,7 +278,8 @@ describe.skip("errors and promises", () => { error = e; } expect(error).toBeDefined(); - const regexp = /Cannot read properties of undefined \(reading 'y'\)|Cannot read property 'y' of undefined/g; + const regexp = + /Cannot read properties of undefined \(reading 'y'\)|Cannot read property 'y' of undefined/g; expect(error.message).toMatch(regexp); }); diff --git a/tests/components/style_class.test.ts b/tests/components/style_class.test.ts index e44a1262..2f75798f 100644 --- a/tests/components/style_class.test.ts +++ b/tests/components/style_class.test.ts @@ -341,7 +341,8 @@ describe("style and class handling", () => { error = e; } expect(error).toBeDefined(); - const regexp = /Cannot read properties of undefined \(reading 'crash'\)|Cannot read property 'crash' of undefined/g; + const regexp = + /Cannot read properties of undefined \(reading 'crash'\)|Cannot read property 'crash' of undefined/g; expect(error.message).toMatch(regexp); expect(fixture.innerHTML).toBe(""); }); diff --git a/tests/misc/portal.test.ts b/tests/misc/portal.test.ts index 6e7bee56..2ee53eba 100644 --- a/tests/misc/portal.test.ts +++ b/tests/misc/portal.test.ts @@ -405,7 +405,8 @@ describe("Portal", () => { error = e; } expect(error).toBeDefined(); - const regexp = /Cannot read properties of undefined \(reading 'crash'\)|Cannot read property 'crash' of undefined/g; + const regexp = + /Cannot read properties of undefined \(reading 'crash'\)|Cannot read property 'crash' of undefined/g; expect(error.message).toMatch(regexp); }); diff --git a/tests/qweb/__snapshots__/t_tag.test.ts.snap b/tests/qweb/__snapshots__/t_tag.test.ts.snap new file mode 100644 index 00000000..3c95eecb --- /dev/null +++ b/tests/qweb/__snapshots__/t_tag.test.ts.snap @@ -0,0 +1,128 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`qweb t-tag can fallback if falsy tag 1`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component } = bdom; + let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers; + + let block1 = tag => createBlock(\`<\${tag || 'fallback'}/>\`); + + return function template(ctx, node, key = \\"\\") { + let tag1 = ctx['tag']; + return toggler(tag1, block1(tag1)()); + } +}" +`; + +exports[`qweb t-tag can update 1`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component } = bdom; + let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers; + + let block1 = tag => createBlock(\`<\${tag || 't'}/>\`); + + return function template(ctx, node, key = \\"\\") { + let tag1 = ctx['tag']; + return toggler(tag1, block1(tag1)()); + } +}" +`; + +exports[`qweb t-tag simple usecases 1`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component } = bdom; + let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers; + + let block1 = tag => createBlock(\`<\${tag || 't'}/>\`); + + return function template(ctx, node, key = \\"\\") { + let tag1 = 'div'; + return toggler(tag1, block1(tag1)()); + } +}" +`; + +exports[`qweb t-tag simple usecases 2`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component } = bdom; + let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers; + + let block1 = tag => createBlock(\`<\${tag || 't'}>text\`); + + return function template(ctx, node, key = \\"\\") { + let tag1 = ctx['tag']; + return toggler(tag1, block1(tag1)()); + } +}" +`; + +exports[`qweb t-tag with multiple attributes 1`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component } = bdom; + let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers; + + let block1 = tag => createBlock(\`<\${tag || 't'} class=\\"blueberry\\" taste=\\"raspberry\\">gooseberry\`); + + return function template(ctx, node, key = \\"\\") { + let tag1 = ctx['tag']; + return toggler(tag1, block1(tag1)()); + } +}" +`; + +exports[`qweb t-tag with multiple child nodes 1`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component } = bdom; + let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers; + + let block1 = tag => createBlock(\`<\${tag || 't'}> pear apple strawberry \`); + + return function template(ctx, node, key = \\"\\") { + let tag1 = ctx['tag']; + return toggler(tag1, block1(tag1)()); + } +}" +`; + +exports[`qweb t-tag with multiple t-tag in same template 1`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component } = bdom; + let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers; + + let block1 = tag => createBlock(\`<\${tag || 't'}>\`); + let block2 = tag => createBlock(\`<\${tag || 't'}>baz\`); + + return function template(ctx, node, key = \\"\\") { + let tag1 = ctx['outer']; + let tag2 = ctx['inner']; + let b2 = toggler(tag2, block2(tag2)()); + return toggler(tag1, block1(tag1)([], [b2])); + } +}" +`; + +exports[`qweb t-tag with multiple t-tag in same template, part 2 1`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component } = bdom; + let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers; + + let block2 = tag => createBlock(\`<\${tag || 't'}>bar\`); + let block3 = tag => createBlock(\`<\${tag || 't'}>baz\`); + + return function template(ctx, node, key = \\"\\") { + let tag1 = ctx['brother']; + let b2 = toggler(tag1, block2(tag1)()); + let tag2 = ctx['brother']; + let b3 = toggler(tag2, block3(tag2)()); + return multi([b2, b3]); + } +}" +`; diff --git a/tests/qweb/parser.test.ts b/tests/qweb/parser.test.ts index 41418346..c29f6338 100644 --- a/tests/qweb/parser.test.ts +++ b/tests/qweb/parser.test.ts @@ -40,6 +40,7 @@ describe("qweb parser", () => { expect(parse(template)).toEqual({ type: ASTType.DomNode, tag: "div", + dynamicTag: null, content: [], attrs: {}, on: {}, @@ -66,6 +67,7 @@ describe("qweb parser", () => { expect(parse("
")).toEqual({ type: ASTType.DomNode, tag: "div", + dynamicTag: null, attrs: {}, on: {}, ref: null, @@ -78,6 +80,7 @@ describe("qweb parser", () => { expect(parse("
some text
")).toEqual({ type: ASTType.DomNode, tag: "div", + dynamicTag: null, attrs: {}, on: {}, ref: null, @@ -90,6 +93,7 @@ describe("qweb parser", () => { expect(parse("
some textinside
")).toEqual({ type: ASTType.DomNode, tag: "div", + dynamicTag: null, attrs: {}, on: {}, ref: null, @@ -99,6 +103,7 @@ describe("qweb parser", () => { { type: ASTType.DomNode, tag: "span", + dynamicTag: null, attrs: {}, on: {}, ref: null, @@ -116,6 +121,7 @@ describe("qweb parser", () => { { type: ASTType.DomNode, tag: "div", + dynamicTag: null, attrs: {}, on: {}, ref: null, @@ -125,6 +131,7 @@ describe("qweb parser", () => { { type: ASTType.DomNode, tag: "span", + dynamicTag: null, attrs: {}, on: {}, ref: null, @@ -143,6 +150,7 @@ describe("qweb parser", () => { { type: ASTType.DomNode, tag: "span", + dynamicTag: null, attrs: {}, on: {}, ref: null, @@ -157,6 +165,7 @@ describe("qweb parser", () => { expect(parse(`
foo
`)).toEqual({ type: ASTType.DomNode, tag: "div", + dynamicTag: null, attrs: { class: "abc" }, on: {}, ref: null, @@ -186,6 +195,7 @@ describe("qweb parser", () => { expect(parse(``)).toEqual({ type: ASTType.DomNode, tag: "span", + dynamicTag: null, attrs: {}, on: {}, ref: null, @@ -206,6 +216,7 @@ describe("qweb parser", () => { expect(parse(`
hey
`)).toEqual({ type: ASTType.DomNode, tag: "div", + dynamicTag: null, attrs: {}, on: {}, ref: null, @@ -230,6 +241,7 @@ describe("qweb parser", () => { expect(parse(`
`)).toEqual({ type: ASTType.DomNode, tag: "div", + dynamicTag: null, attrs: {}, on: {}, ref: null, @@ -242,6 +254,7 @@ describe("qweb parser", () => { expect(parse(`
body
`)).toEqual({ type: ASTType.DomNode, tag: "div", + dynamicTag: null, attrs: {}, on: {}, ref: null, @@ -260,6 +273,7 @@ describe("qweb parser", () => { expect(parse(`
hey
`)).toEqual({ type: ASTType.DomNode, tag: "div", + dynamicTag: null, attrs: {}, on: {}, ref: null, @@ -286,6 +300,7 @@ describe("qweb parser", () => { content: { type: ASTType.DomNode, tag: "div", + dynamicTag: null, attrs: {}, on: {}, ref: null, @@ -361,6 +376,7 @@ describe("qweb parser", () => { content: { type: ASTType.DomNode, tag: "div", + dynamicTag: null, attrs: {}, on: {}, ref: null, @@ -378,6 +394,7 @@ describe("qweb parser", () => { content: { type: ASTType.DomNode, tag: "h1", + dynamicTag: null, attrs: {}, on: {}, ref: null, @@ -389,6 +406,7 @@ describe("qweb parser", () => { tElse: { type: ASTType.DomNode, tag: "h2", + dynamicTag: null, attrs: {}, on: {}, ref: null, @@ -437,6 +455,7 @@ describe("qweb parser", () => { attrs: {}, on: {}, tag: "div", + dynamicTag: null, ref: null, model: null, content: [{ type: ASTType.Text, value: "ok" }], @@ -455,6 +474,7 @@ describe("qweb parser", () => { attrs: {}, on: {}, tag: "div", + dynamicTag: null, ref: null, model: null, content: [{ type: ASTType.Text, value: "ok" }], @@ -486,6 +506,7 @@ describe("qweb parser", () => { ).toEqual({ type: ASTType.DomNode, tag: "div", + dynamicTag: null, attrs: {}, on: {}, ref: null, @@ -559,6 +580,7 @@ describe("qweb parser", () => { body: { type: ASTType.DomNode, tag: "div", + dynamicTag: null, attrs: {}, on: {}, ref: null, @@ -603,6 +625,7 @@ describe("qweb parser", () => { body: { type: ASTType.DomNode, tag: "span", + dynamicTag: null, attrs: {}, on: {}, ref: null, @@ -637,6 +660,7 @@ describe("qweb parser", () => { content: { type: ASTType.DomNode, tag: "span", + dynamicTag: null, attrs: {}, on: {}, ref: null, @@ -667,6 +691,7 @@ describe("qweb parser", () => { body: { type: ASTType.DomNode, tag: "option", + dynamicTag: null, attrs: { "t-att-selected": "category.id==options.active_category_id", "t-att-value": "category.id", @@ -694,6 +719,7 @@ describe("qweb parser", () => { ref: null, model: null, tag: "div", + dynamicTag: null, content: [ { type: ASTType.TForEach, @@ -734,6 +760,7 @@ describe("qweb parser", () => { body: { type: ASTType.DomNode, tag: "span", + dynamicTag: null, on: {}, ref: null, model: null, @@ -840,6 +867,7 @@ describe("qweb parser", () => { expect(parse(`
`)).toEqual({ type: ASTType.DomNode, tag: "div", + dynamicTag: null, attrs: {}, on: {}, ref: null, @@ -876,6 +904,7 @@ describe("qweb parser", () => { expect(parse(``)).toEqual({ type: ASTType.DomNode, tag: "button", + dynamicTag: null, attrs: {}, on: { click: "add" }, ref: null, @@ -952,6 +981,7 @@ describe("qweb parser", () => { { type: ASTType.DomNode, tag: "span", + dynamicTag: null, attrs: {}, content: [], ref: null, @@ -961,6 +991,7 @@ describe("qweb parser", () => { { type: ASTType.DomNode, tag: "div", + dynamicTag: null, attrs: {}, content: [], ref: null, @@ -1156,6 +1187,7 @@ describe("qweb parser", () => { content: { type: ASTType.DomNode, tag: "div", + dynamicTag: null, attrs: {}, on: {}, ref: null, @@ -1172,6 +1204,7 @@ describe("qweb parser", () => { content: { type: ASTType.DomNode, tag: "div", + dynamicTag: null, attrs: {}, on: {}, ref: null, @@ -1189,6 +1222,7 @@ describe("qweb parser", () => { expect(parse(`
hey
`)).toEqual({ type: ASTType.DomNode, tag: "div", + dynamicTag: null, attrs: {}, on: {}, ref: "name", @@ -1201,6 +1235,7 @@ describe("qweb parser", () => { expect(parse(`
body
`)).toEqual({ type: ASTType.DomNode, tag: "div", + dynamicTag: null, attrs: {}, on: {}, ref: "name", @@ -1215,6 +1250,7 @@ describe("qweb parser", () => { expect(parse(`
body
`)).toEqual({ type: ASTType.DomNode, tag: "div", + dynamicTag: null, attrs: {}, on: {}, ref: "name", @@ -1261,6 +1297,7 @@ describe("qweb parser", () => { ref: null, model: null, tag: "div", + dynamicTag: null, type: 2, }, type: 16, @@ -1290,6 +1327,7 @@ describe("qweb parser", () => { on: {}, ref: null, tag: "input", + dynamicTag: null, model: { baseExpr: "state", expr: "'stuff'", @@ -1307,6 +1345,7 @@ describe("qweb parser", () => { on: {}, ref: null, tag: "input", + dynamicTag: null, model: { baseExpr: "state", expr: "'stuff'", @@ -1324,6 +1363,7 @@ describe("qweb parser", () => { on: {}, ref: null, tag: "input", + dynamicTag: null, model: { baseExpr: "state", expr: "'stuff'", @@ -1342,6 +1382,7 @@ describe("qweb parser", () => { on: {}, ref: null, tag: "textarea", + dynamicTag: null, model: { baseExpr: "state", expr: "'stuff'", @@ -1359,6 +1400,7 @@ describe("qweb parser", () => { on: {}, ref: null, tag: "input", + dynamicTag: null, model: { baseExpr: "state", expr: "'stuff'", @@ -1376,6 +1418,7 @@ describe("qweb parser", () => { on: {}, ref: null, tag: "input", + dynamicTag: null, model: { baseExpr: "state", expr: "'stuff'", @@ -1393,6 +1436,7 @@ describe("qweb parser", () => { on: {}, ref: null, tag: "input", + dynamicTag: null, model: { baseExpr: "state", expr: "'stuff'", @@ -1403,4 +1447,20 @@ describe("qweb parser", () => { specialInitTargetAttr: "checked", }, }); + + // --------------------------------------------------------------------------- + // t-tag + // --------------------------------------------------------------------------- + test("t-tag", async () => { + expect(parse(`
`)).toEqual({ + type: ASTType.DomNode, + attrs: {}, + content: [], + on: {}, + ref: null, + tag: "div", + dynamicTag: "theTag", + model: null, + }); + }); }); diff --git a/tests/qweb/t_tag.test.ts b/tests/qweb/t_tag.test.ts index 0ff5f40b..46955f18 100644 --- a/tests/qweb/t_tag.test.ts +++ b/tests/qweb/t_tag.test.ts @@ -1,14 +1,25 @@ -import { renderToString, snapshotEverything } from "../helpers"; +import { mount, patch } from "../../src/blockdom"; +import { makeTestFixture, renderToBdom, renderToString, snapshotEverything } from "../helpers"; snapshotEverything(); +let fixture: HTMLElement; + +beforeEach(() => { + fixture = makeTestFixture(); +}); + +afterEach(() => { + fixture.remove(); +}); + describe("qweb t-tag", () => { - test.skip("simple usecases", () => { + test("simple usecases", () => { expect(renderToString(``)).toBe("
"); expect(renderToString(`text`, { tag: "span" })).toBe("text"); }); - test.skip("with multiple child nodes", () => { + test("with multiple child nodes", () => { const template = ` pear @@ -20,9 +31,39 @@ describe("qweb t-tag", () => { ); }); - test.skip("with multiple attributes", () => { + test("with multiple attributes", () => { const template = `gooseberry`; - const expected = `
gooseberry
`; + const expected = `
gooseberry
`; expect(renderToString(template, { tag: "div" })).toBe(expected); }); + + test("can fallback if falsy tag", () => { + const template = ``; + expect(renderToString(template, { tag: "div" })).toBe(`
`); + expect(renderToString(template, { tag: "" })).toBe(``); + expect(renderToString(template, { tag: undefined })).toBe(``); + expect(renderToString(template, { tag: null })).toBe(``); + expect(renderToString(template, { tag: false })).toBe(``); + }); + + test("with multiple t-tag in same template", () => { + const template = `baz`; + expect(renderToString(template, { outer: "foo", inner: "bar" })).toBe( + `baz` + ); + }); + + test("with multiple t-tag in same template, part 2", () => { + const template = `barbaz`; + expect(renderToString(template, { brother: "foo" })).toBe(`barbaz`); + }); + + test("can update", () => { + const template = ``; + const bdom = renderToBdom(template, { tag: "yop" }); + mount(bdom, fixture); + expect(fixture.innerHTML).toBe(""); + patch(bdom, renderToBdom(template, { tag: "gnap" })); + expect(fixture.innerHTML).toBe(""); + }); });