import { Component, Env } from "../../src/component/component"; import { QWeb } from "../../src/qweb/qweb"; import { xml } from "../../src/tags"; import { useState, useRef } from "../../src/hooks"; import { makeTestFixture, makeTestEnv, nextTick } from "../helpers"; //------------------------------------------------------------------------------ // Setup and helpers //------------------------------------------------------------------------------ // We create before each test: // - fixture: a div, appended to the DOM, intended to be the target of dom // manipulations. Note that it is removed after each test. // - env: a WEnv, necessary to create new components let fixture: HTMLElement; let env: Env; beforeEach(() => { fixture = makeTestFixture(); env = makeTestEnv(); Component.env = env; }); afterEach(() => { fixture.remove(); }); describe("class and style attributes with t-component", () => { test("class is properly added on widget root el", async () => { class Child extends Component { static template = xml`
`; } class ParentWidget extends Component { static template = xml`
`; static components = { Child }; } const widget = new ParentWidget(); await widget.mount(fixture); expect(fixture.innerHTML).toBe(`
`); }); test("empty class attribute is not added on widget root el", async () => { class Child extends Component { static template = xml``; } class Parent extends Component { static template = xml`
`; static components = { Child }; } const widget = new Parent(); await widget.mount(fixture); expect(fixture.innerHTML).toBe(`
`); }); test("t-att-class is properly added/removed on widget root el", async () => { class Child extends Component { static template = xml`
`; } class ParentWidget extends Component { static template = xml`
`; static components = { Child }; state = useState({ a: true, b: false }); } const widget = new ParentWidget(); await widget.mount(fixture); expect(fixture.innerHTML).toBe(`
`); expect(QWeb.TEMPLATES[ParentWidget.template].fn.toString()); widget.state.a = false; widget.state.b = true; await nextTick(); expect(fixture.innerHTML).toBe(`
`); }); test("class with extra whitespaces", async () => { env.qweb.addTemplate( "ParentWidget", `
` ); class Child extends Component {} class ParentWidget extends Component { static components = { Child }; } env.qweb.addTemplate("Child", `
`); const widget = new ParentWidget(); await widget.mount(fixture); expect(fixture.innerHTML).toBe(`
`); }); test("t-att-class is properly added/removed on widget root el (v2)", async () => { env.qweb.addTemplates(`
`); class Child extends Component { state = useState({ d: true }); } class ParentWidget extends Component { static components = { Child }; state = useState({ b: true }); child = useRef("child"); } const widget = new ParentWidget(); await widget.mount(fixture); const span = fixture.querySelector("span")!; expect(span.className).toBe("c d a b"); widget.state.b = false; await nextTick(); expect(span.className).toBe("c d a"); (widget.child.comp as Child).state.d = false; await nextTick(); expect(span.className).toBe("c a"); widget.state.b = true; await nextTick(); expect(span.className).toBe("c a b"); (widget.child.comp as Child).state.d = true; await nextTick(); expect(span.className).toBe("c a b d"); expect(env.qweb.templates.ParentWidget.fn.toString()).toMatchSnapshot(); expect(env.qweb.templates.Child.fn.toString()).toMatchSnapshot(); }); test("t-att-class is properly added/removed on widget root el (v3)", async () => { env.qweb.addTemplates(`
`); class Child extends Component { state = useState({ d: true }); } class ParentWidget extends Component { static components = { Child }; state = useState({ b: true }); child = useRef("child"); } const widget = new ParentWidget(); await widget.mount(fixture); const span = fixture.querySelector("span")!; expect(span.className).toBe("c d a b"); widget.state.b = false; await nextTick(); expect(span.className).toBe("c d a"); (widget.child.comp as Child).state.d = false; await nextTick(); expect(span.className).toBe("c a"); widget.state.b = true; await nextTick(); expect(span.className).toBe("c a b"); (widget.child.comp as Child).state.d = true; await nextTick(); expect(span.className).toBe("c a b d"); expect(env.qweb.templates.ParentWidget.fn.toString()).toMatchSnapshot(); expect(env.qweb.templates.Child.fn.toString()).toMatchSnapshot(); }); test("class on components do not interfere with user defined classes", async () => { env.qweb.addTemplates(`
`); class App extends Component { state = useState({ c: true }); mounted() { this.el!.classList.add("user"); } } const widget = new App(); await widget.mount(fixture); expect(fixture.innerHTML).toBe('
'); widget.state.c = false; await nextTick(); expect(fixture.innerHTML).toBe('
'); }); test("style is properly added on widget root el", async () => { env.qweb.addTemplate( "ParentWidget", `
` ); class SomeComponent extends Component { static template = xml`
`; } class ParentWidget extends Component { static components = { child: SomeComponent }; } const widget = new ParentWidget(); await widget.mount(fixture); expect(fixture.innerHTML).toBe(`
`); }); test("dynamic t-att-style is properly added and updated on widget root el", async () => { env.qweb.addTemplate( "ParentWidget", `
` ); class SomeComponent extends Component { static template = xml`
`; } class ParentWidget extends Component { static components = { child: SomeComponent }; state = useState({ style: "font-size: 20px" }); } const widget = new ParentWidget(); await widget.mount(fixture); expect(env.qweb.templates.ParentWidget.fn.toString()).toMatchSnapshot(); expect(fixture.innerHTML).toBe(`
`); widget.state.style = "font-size: 30px"; await nextTick(); expect(fixture.innerHTML).toBe(`
`); }); test("error in subcomponent with class", async () => { class Child extends Component { static template = xml`
`; } class ParentWidget extends Component { static template = xml`
`; static components = { Child }; } const widget = new ParentWidget(); let error; try { await widget.mount(fixture); } catch (e) { error = e; } expect(error).toBeDefined(); 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(""); }); });