import { OwlError } from "../../src/common/owl_error"; import { App, Component, mount, onMounted, useState, xml } from "../../src"; import { makeTestFixture, nextAppError, nextTick, snapshotEverything } from "../helpers"; snapshotEverything(); let fixture: HTMLElement; let originalconsoleWarn = console.warn; let mockConsoleWarn: any; beforeEach(() => { fixture = makeTestFixture(); mockConsoleWarn = jest.fn(() => {}); console.warn = mockConsoleWarn; }); afterEach(() => { console.warn = originalconsoleWarn; }); describe("style and class handling", () => { test("can set style and class inside component", async () => { class Test extends Component { static template = xml`
world
`; } await mount(Test, fixture); expect(fixture.innerHTML).toBe(`
world
`); }); test("can set class on sub component, as prop", async () => { class Child extends Component { static template = xml`
child
`; } class Parent extends Component { static template = xml``; static components = { Child }; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe(`
child
`); }); test("no class is set is parent does not give it as prop", async () => { class Child extends Component { static template = xml`
child
`; } class Parent extends Component { static template = xml``; static components = { Child }; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe(`
child
`); }); test("no class is set is child ignores it", async () => { class Child extends Component { static template = xml`
child
`; } class Parent extends Component { static template = xml``; static components = { Child }; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe(`
child
`); }); 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 }; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe(`
`); }); test("can set more than one class on sub component", async () => { class Child extends Component { static template = xml`
child
`; } class Parent extends Component { static template = xml``; static components = { Child }; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe(`
child
`); }); test("component class and parent class combine together", async () => { class Child extends Component { static template = xml`
child
`; } class Parent extends Component { static template = xml``; static components = { Child }; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe(`
child
`); }); test("can set class on sub sub component", async () => { class ChildChild extends Component { static template = xml`
childchild
`; } class Child extends Component { static template = xml``; static components = { ChildChild }; } class Parent extends Component { static template = xml``; static components = { Child }; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe(`
childchild
`); }); test("can set class on multi root component", async () => { class Child extends Component { static template = xml`
a
b`; } class Parent extends Component { static template = xml``; static components = { Child }; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe(`
a
b`); }); test("class on sub component, which is switched to another", async () => { class ChildA extends Component { static template = xml`
a
`; } class ChildB extends Component { static template = xml`b`; } class Child extends Component { static template = xml``; static components = { ChildA, ChildB }; } class Parent extends Component { static template = xml``; static components = { Child }; state = useState({ child: "a" }); } const parent = await mount(Parent, fixture); expect(fixture.innerHTML).toBe(`
a
`); parent.state.child = "b"; await nextTick(); expect(fixture.innerHTML).toBe(`b`); }); // test("t-att-class is properly added/removed on widget root el", async () => { // class Child extends Component { // static template = xml`
`; // } // class Parent extends Component { // static template = xml`
`; // static components = { Child }; // state = useState({ a: true, b: false }); // } // const widget = await mount(Parent, fixture); // expect(fixture.innerHTML).toBe(`
`); // widget.state.a = false; // widget.state.b = true; // await nextTick(); // expect(fixture.innerHTML).toBe(`
`); // }); test("class with extra whitespaces", async () => { class Child extends Component { static template = xml`
`; } class Parent extends Component { static template = xml``; static components = { Child }; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe(`
`); }); test("class with extra whitespaces (variation)", async () => { class Child extends Component { static template = xml`
`; } class Parent extends Component { static template = xml`

`; static components = { Child }; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe(`

`); }); // TODO: adapt name (t-att-class no longer makes sense on Components) test("t-att-class is properly added/removed on widget root el (v2)", async () => { let child: Child; class Child extends Component { static template = xml``; state = useState({ d: true }); setup() { child = this; } } class Parent extends Component { static template = xml`
`; static components = { Child }; state = useState({ b: true }); } const widget = await mount(Parent, fixture); const span = fixture.querySelector("span")!; expect(span.className).toBe("c d b"); widget.state.b = false; await nextTick(); expect(span.className).toBe("c d"); child!.state.d = false; await nextTick(); expect(span.className).toBe("c"); widget.state.b = true; await nextTick(); expect(span.className).toBe("c b"); child!.state.d = true; await nextTick(); expect(span.className).toBe("c b d"); }); // TODO: adapt name (t-att-class no longer makes sense on Components) test("t-att-class is properly added/removed on widget root el (v3)", async () => { let child: Child; class Child extends Component { static template = xml``; state = useState({ d: true }); setup() { child = this; } } class Parent extends Component { static template = xml``; static components = { Child }; state = useState({ b: true }); } const widget = await mount(Parent, 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"); 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"); child!.state.d = true; await nextTick(); expect(span.className).toBe("c a b d"); }); test("class on components do not interfere with user defined classes", async () => { class App extends Component { static template = xml`
`; state = useState({ c: true }); setup() { onMounted(() => { fixture.querySelector("div")!.classList.add("user"); }); } } const widget = await mount(App, fixture); expect(fixture.innerHTML).toBe('
'); widget.state.c = false; await nextTick(); expect(fixture.innerHTML).toBe('
'); }); // TODO: adapt name test("style is properly added on widget root el", async () => { class SomeComponent extends Component { static template = xml`
`; } class ParentWidget extends Component { static components = { Child: SomeComponent }; static template = xml``; } await mount(ParentWidget, fixture); expect(fixture.innerHTML).toBe(`
`); }); // TODO: adapt name // TODO: t-att-style as object (like class) test.skip("dynamic t-att-style is properly added and updated on widget root el", async () => { class SomeComponent extends Component { static template = xml`
`; } class ParentWidget extends Component { static template = xml``; static components = { Child: SomeComponent }; state = useState({ style: { "font-size": "20px" } }); } const widget = await mount(ParentWidget, fixture); expect(fixture.innerHTML).toBe(`
`); widget.state.style["font-size"] = "30px"; await nextTick(); expect(fixture.innerHTML).toBe(`
`); }); // TODO: does this test need to be moved? (class now a standard prop) test("error in subcomponent with class", async () => { class Child extends Component { static template = xml`
`; } class Parent extends Component { static template = xml``; static components = { Child }; } let error: OwlError; const app = new App(Parent); const mountProm = app.mount(fixture).catch((e: Error) => (error = e)); await expect(nextAppError(app)).resolves.toThrow("error occured in the owl lifecycle"); await mountProm; expect(error!).toBeDefined(); expect(error!.cause).toBeDefined(); const regexp = /Cannot read properties of undefined \(reading 'crash'\)|Cannot read property 'crash' of undefined/g; expect(error!.cause.message).toMatch(regexp); expect(fixture.innerHTML).toBe(""); expect(mockConsoleWarn).toBeCalledTimes(1); }); });