import QWeb, { EvalContext } from "../src/ts/core/qweb_vdom"; import { init } from "../libs/snabbdom/src/snabbdom"; import sdAttributes from "../libs/snabbdom/src/modules/attributes"; import sdListeners from "../libs/snabbdom/src/modules/eventlisteners"; const patch = init([sdAttributes, sdListeners]); function trim(str: string): string { return str.replace(/\s/g, ""); } function renderToDOM( qweb: QWeb, template: string, context: EvalContext = {} ): HTMLElement | Text { const vnode = qweb.render(template, context); if (vnode.sel === undefined) { return document.createTextNode(vnode.text!); } const node = document.createElement(vnode.sel!); patch(node, vnode); return node; } function renderToString( qweb: QWeb, t: string, context: EvalContext = {} ): string { const node = renderToDOM(qweb, t, context); return node instanceof Text ? node.textContent! : node.outerHTML; } describe("static templates", () => { test("simple string", () => { const qweb = new QWeb(); qweb.addTemplate("test", "hello vdom"); expect(renderToString(qweb, "test")).toBe("hello vdom"); }); test("empty div", () => { const qweb = new QWeb(); qweb.addTemplate("test", "
"); expect(renderToString(qweb, "test")).toBe("
"); }); test("div with a text node", () => { const qweb = new QWeb(); qweb.addTemplate("test", "
word
"); expect(renderToString(qweb, "test")).toBe("
word
"); }); test("div with a span child node", () => { const qweb = new QWeb(); qweb.addTemplate("test", "
word
"); expect(renderToString(qweb, "test")).toBe("
word
"); }); }); describe("error handling", () => { test("invalid xml", () => { const qweb = new QWeb(); expect(() => qweb.addTemplate("test", "
")).toThrow( "Invalid XML in template" ); }); test("template with text node and tag", () => { const qweb = new QWeb(); qweb.addTemplate("test", `textother node`); expect(() => renderToString(qweb, "test")).toThrow( "A template should not have more than one root node" ); }); test("nice warning if no template with given name", () => { const qweb = new QWeb(); expect(() => qweb.render("invalidname")).toThrow("does not exist"); }); }); describe("t-esc", () => { test("literal", () => { const qweb = new QWeb(); qweb.addTemplate("test", ``); expect(renderToString(qweb, "test")).toBe("ok"); }); test("variable", () => { const qweb = new QWeb(); qweb.addTemplate("test", ``); expect(renderToString(qweb, "test", { var: "ok" })).toBe("ok"); }); test.skip("escaping", () => { const qweb = new QWeb(); qweb.addTemplate("test", ``); expect(renderToString(qweb, "test", { var: "" })).toBe( "<ok>" ); }); test("escaping on a node", () => { const qweb = new QWeb(); qweb.addTemplate("test", ``); expect(renderToString(qweb, "test")).toBe("ok"); }); test("escaping on a node with a body", () => { const qweb = new QWeb(); qweb.addTemplate("test", `nope`); expect(renderToString(qweb, "test")).toBe("ok"); }); test("escaping on a node with a body, as a default", () => { const qweb = new QWeb(); qweb.addTemplate("test", `nope`); expect(renderToString(qweb, "test")).toBe("nope"); }); }); describe("t-raw", () => { test("literal", () => { const qweb = new QWeb(); qweb.addTemplate("test", ``); expect(renderToString(qweb, "test")).toBe("ok"); }); test("variable", () => { const qweb = new QWeb(); qweb.addTemplate("test", ``); expect(renderToString(qweb, "test", { var: "ok" })).toBe("ok"); }); test("not escaping", () => { const qweb = new QWeb(); qweb.addTemplate("test", `
`); expect(renderToString(qweb, "test", { var: "" })).toBe( "
" ); }); }); describe("t-set", () => { test("set from attribute literal", () => { const qweb = new QWeb(); qweb.addTemplate( "test", `
` ); expect(renderToString(qweb, "test")).toBe("
ok
"); }); test("set from body literal", () => { const qweb = new QWeb(); qweb.addTemplate( "test", `ok` ); expect(renderToString(qweb, "test")).toBe("ok"); }); test("set from attribute lookup", () => { const qweb = new QWeb(); qweb.addTemplate( "test", `
` ); expect(renderToString(qweb, "test", { value: "ok" })).toBe("
ok
"); }); test("set from body lookup", () => { const qweb = new QWeb(); qweb.addTemplate( "test", `
` ); expect(renderToString(qweb, "test", { value: "ok" })).toBe("
ok
"); }); test("set from empty body", () => { const qweb = new QWeb(); qweb.addTemplate("test", `
`); expect(renderToString(qweb, "test")).toBe("
"); }); test("value priority", () => { const qweb = new QWeb(); qweb.addTemplate( "test", `
2
` ); expect(renderToString(qweb, "test")).toBe("
1
"); }); test("evaluate value expression", () => { const qweb = new QWeb(); qweb.addTemplate( "test", `
` ); expect(renderToString(qweb, "test")).toBe("
3
"); }); test("evaluate value expression, part 2", () => { const qweb = new QWeb(); qweb.addTemplate( "test", `
` ); expect(renderToString(qweb, "test", { somevariable: 43 })).toBe( "
45
" ); }); }); describe("t-if", () => { test("boolean value true condition", () => { const qweb = new QWeb(); qweb.addTemplate("test", `
ok
`); expect(renderToString(qweb, "test", { condition: true })).toBe( "
ok
" ); }); test("boolean value false condition", () => { const qweb = new QWeb(); qweb.addTemplate("test", `
ok
`); expect(renderToString(qweb, "test", { condition: false })).toBe( "
" ); }); test("boolean value condition missing", () => { const qweb = new QWeb(); qweb.addTemplate("test", `fail`); expect(renderToString(qweb, "test")).toBe(""); }); test("boolean value condition elif", () => { const qweb = new QWeb(); qweb.addTemplate( "test", `
black pearl yellow submarine red is dead beer
` ); expect(renderToString(qweb, "test", { color: "red" })).toBe( "
red is dead
" ); }); test("boolean value condition else", () => { const qweb = new QWeb(); qweb.addTemplate( "test", `
begin ok ok-else end
` ); const result = trim(renderToString(qweb, "test", { condition: true })); expect(result).toBe("
beginokend
"); }); test("boolean value condition false else", () => { const qweb = new QWeb(); qweb.addTemplate( "test", `
beginfail fail-elseend
` ); const result = trim(renderToString(qweb, "test", { condition: false })); expect(result).toBe( "
beginfail-elseend
" ); }); }); describe("attributes", () => { test("static attributes", () => { const qweb = new QWeb(); qweb.addTemplate("test", `
`); const result = renderToString(qweb, "test"); const expected = `
`; expect(result).toBe(expected); }); test("static attributes on void elements", () => { const qweb = new QWeb(); qweb.addTemplate("test", `Test`); const result = renderToString(qweb, "test"); expect(result).toBe(`Test`); }); test("dynamic attributes", () => { const qweb = new QWeb(); qweb.addTemplate("test", `
`); const result = renderToString(qweb, "test"); expect(result).toBe(`
`); }); test("fixed variable", () => { const qweb = new QWeb(); qweb.addTemplate("test", `
`); const result = renderToString(qweb, "test", { value: "ok" }); expect(result).toBe(`
`); }); test("dynamic attribute falsy variable ", () => { const qweb = new QWeb(); qweb.addTemplate("test", `
`); const result = renderToString(qweb, "test", { value: false }); expect(result).toBe(`
`); }); test("tuple literal", () => { const qweb = new QWeb(); qweb.addTemplate("test", `
`); const result = renderToString(qweb, "test"); expect(result).toBe(`
`); }); test("tuple variable", () => { const qweb = new QWeb(); qweb.addTemplate("test", `
`); const result = renderToString(qweb, "test", { value: ["foo", "bar"] }); expect(result).toBe(`
`); }); test("object", () => { const qweb = new QWeb(); qweb.addTemplate("test", `
`); const result = renderToString(qweb, "test", { value: { a: 1, b: 2, c: 3 } }); expect(result).toBe(`
`); }); test("format literal", () => { const qweb = new QWeb(); qweb.addTemplate("test", `
`); const result = renderToString(qweb, "test"); expect(result).toBe(`
`); }); test("format value", () => { const qweb = new QWeb(); qweb.addTemplate("test", `
`); const result = renderToString(qweb, "test", { value: "a" }); expect(result).toBe(`
`); }); test("format expression", () => { const qweb = new QWeb(); qweb.addTemplate("test", `
`); const result = renderToString(qweb, "test", { value: 5 }); expect(result).toBe(`
`); }); test("format multiple", () => { const qweb = new QWeb(); qweb.addTemplate( "test", `
` ); const result = renderToString(qweb, "test", { value1: 0, value2: 1, value3: 2 }); expect(result).toBe(`
`); }); test.skip("various escapes", () => { // not needed?? const qweb = new QWeb(); qweb.addTemplate( "test", `
` ); const result = renderToString(qweb, "test", { bar: 0, baz: 1, qux: { qux: "<>" } }); const expected = `
`; expect(result).toBe(expected); }); }); describe("t-call (template calling", () => { test("basic caller", () => { const qweb = new QWeb(); qweb.addTemplate("_basic-callee", "
ok
"); qweb.addTemplate("caller", ''); const expected = "
ok
"; expect(renderToString(qweb, "caller")).toBe(expected); }); test("t-call not allowed on a non t node", () => { const qweb = new QWeb(); qweb.addTemplate("_basic-callee", "ok"); qweb.addTemplate("caller", '
'); expect(() => renderToString(qweb, "caller")).toThrow("Invalid tag"); }); test("with unused body", () => { const qweb = new QWeb(); qweb.addTemplate("_basic-callee", "
ok
"); qweb.addTemplate("caller", 'WHEEE'); const expected = "
ok
"; expect(renderToString(qweb, "caller")).toBe(expected); }); test("with unused setbody", () => { const qweb = new QWeb(); qweb.addTemplate("_basic-callee", "
ok
"); qweb.addTemplate( "caller", '' ); const expected = "
ok
"; expect(renderToString(qweb, "caller")).toBe(expected); }); test("with used body", () => { const qweb = new QWeb(); qweb.addTemplate("_callee-printsbody", '

'); qweb.addTemplate("caller", 'ok'); const expected = "

ok

"; expect(renderToString(qweb, "caller")).toBe(expected); }); test("with used set body", () => { const qweb = new QWeb(); qweb.addTemplate("_callee-uses-foo", ''); qweb.addTemplate( "caller", ` ` ); const expected = "ok"; expect(renderToString(qweb, "caller")).toBe(expected); }); test("inherit context", () => { const qweb = new QWeb(); qweb.addTemplate("_callee-uses-foo", ''); qweb.addTemplate( "caller", `
` ); const expected = "
1
"; expect(renderToString(qweb, "caller")).toBe(expected); }); test("scoped parameters", () => { const qweb = new QWeb(); qweb.addTemplate("_basic-callee", `ok`); qweb.addTemplate( "caller", `
` ); const expected = "
ok
"; expect(trim(renderToString(qweb, "caller"))).toBe(expected); }); }); describe("foreach", () => { test("iterate on items", () => { const qweb = new QWeb(); qweb.addTemplate( "test", `
[: ]
` ); const result = trim(renderToString(qweb, "test")); const expected = `
[0:33][1:22][2:11]
`; expect(result).toBe(expected); }); test("iterate on items (on a element node)", () => { const qweb = new QWeb(); qweb.addTemplate( "test", `
` ); const result = trim(renderToString(qweb, "test")); const expected = `
12
`; expect(result).toBe(expected); }); test("iterate, position", () => { const qweb = new QWeb(); qweb.addTemplate( "test", `
- first last ()
` ); const result = trim(renderToString(qweb, "test")); const expected = `
-first(even)-(odd)-(even)-(odd)-last(even)
`; expect(result).toBe(expected); }); test("iterate, integer param", () => { const qweb = new QWeb(); qweb.addTemplate( "test", `
[: ]
` ); const result = trim(renderToString(qweb, "test")); const expected = `
[0:00][1:11][2:22]
`; expect(result).toBe(expected); }); test("iterate, dict param", () => { const qweb = new QWeb(); qweb.addTemplate( "test", `
[: - ]
` ); const result = trim( renderToString(qweb, "test", { value: { a: 1, b: 2, c: 3 } }) ); const expected = `
[0:a1-even][1:b2-odd][2:c3-even]
`; expect(result).toBe(expected); }); }); describe("misc", () => { test("global", () => { const qweb = new QWeb(); qweb.addTemplate("_callee-asc", ``); qweb.addTemplate( "_callee-uses-foo", `foo default` ); qweb.addTemplate( "_callee-asc-toto", `
toto default
` ); qweb.addTemplate( "caller", `
` ); const result = trim(renderToString(qweb, "caller")); const expected = trim(`
4 aaa foo default bbb 5 aaa foo default bbb 6 aaa foo default bbb
toto default
`); expect(result).toBe(expected); }); }); describe("t-on", () => { test("can bind event handler", () => { const qweb = new QWeb(); qweb.addTemplate("test", ``); let a = 1; const node = renderToDOM(qweb, "test", { add() { a = 3; } }); (node).click(); expect(a).toBe(3); }); test("can bind handlers with arguments", () => { const qweb = new QWeb(); qweb.addTemplate("test", ``); let a = 1; const node = renderToDOM(qweb, "test", { add(n) { a = a + n; } }); (node).click(); expect(a).toBe(6); }); test("can bind handlers with loop variable as argument", () => { expect.assertions(1); const qweb = new QWeb(); qweb.addTemplate( "test", ` ` ); const node = renderToDOM(qweb, "test", { activate(action) { expect(action).toBe("someval"); } }); (node).getElementsByTagName("a")[0].click(); }); }); describe("t-ref", () => { test("can get a ref on a node", () => { const qweb = new QWeb(); qweb.addTemplate("test", `
`); let refs: any = {}; renderToDOM(qweb, "test", { refs }); expect(refs.myspan.tagName).toBe("SPAN"); }); });