import { Widget, WEnv } from "../src/ts/core/widget"; import { QWeb } from "../src/ts/core/qweb_vdom"; interface Type extends Function { new (...args: any[]): T; } type TestEnv = WEnv; type TestWidget = Widget; function makeWidget(W: Type): TestWidget { const env = { qweb: new QWeb() }; const w = new W(env); return w; } async function click(el: HTMLElement) { el.click(); return Promise.resolve(); } const template = `
`; class Counter extends Widget { name = "counter"; template = template; state = { counter: 0 }; inc() { this.updateState({ counter: this.state.counter + 1 }); } } describe("basic widget properties", () => { test("has no el after creation", async () => { const widget = makeWidget(Widget); expect(widget.el).toBe(null); }); test("can be mounted", async () => { const widget = makeWidget(Widget); const target = document.createElement("div"); await widget.mount(target); expect(target.innerHTML).toBe("
"); }); test("can be clicked on and updated", async () => { const counter = makeWidget(Counter); const target = document.createElement("div"); await counter.mount(target); expect(target.innerHTML).toBe("
0
"); await click((counter.el).getElementsByTagName("button")[0]); expect(target.innerHTML).toBe("
1
"); }); test("widget style and classname", async () => { class StyledWidget extends Widget { template = `
world
`; } const widget = makeWidget(StyledWidget); const target = document.createElement("div"); await widget.mount(target); expect(target.innerHTML).toBe( `
world
` ); }); test("updateState before first render does not trigger a render", async () => { let renderCalls = 0; class TestW extends Widget { async willStart() { this.updateState({}); } async render() { renderCalls++; return super.render(); } } const widget = makeWidget(TestW); await widget.mount(document.createElement("div")); expect(renderCalls).toBe(1); }); }); describe("lifecycle hooks", () => { test("willStart hook is called", async () => { let willstart = false; class HookWidget extends Widget { async willStart() { willstart = true; } } const widget = makeWidget(HookWidget); const target = document.createElement("div"); await widget.mount(target); expect(willstart).toBe(true); }); test("mounted hook is not called if not in DOM", async () => { let mounted = false; class HookWidget extends Widget { async mounted() { mounted = true; } } const widget = makeWidget(HookWidget); const target = document.createElement("div"); await widget.mount(target); expect(mounted).toBe(false); }); test("mounted hook is called if mounted in DOM", async () => { let mounted = false; class HookWidget extends Widget { async mounted() { mounted = true; } } const widget = makeWidget(HookWidget); const target = document.createElement("div"); document.body.appendChild(target); await widget.mount(target); expect(mounted).toBe(true); target.remove(); }); test("mounted hook is called on subwidgets, in proper order", async () => { expect.assertions(4); let parentMounted = false; let childMounted = false; class ParentWidget extends Widget { name = "a"; template = `
Hello
`; widgets = { child: ChildWidget }; mounted() { expect(childMounted).toBe(false); parentMounted = true; } } class ChildWidget extends Widget { mounted() { expect(document.body.contains(this.el)).toBe(true); expect(parentMounted).toBe(true); childMounted = true; } } const widget = makeWidget(ParentWidget); const target = document.createElement("div"); document.body.appendChild(target); await widget.mount(target); expect(childMounted).toBe(true); target.remove(); }); }); describe("destroy method", () => { test("destroy remove the widget from the DOM", async () => { const widget = makeWidget(Widget); const target = document.body; await widget.mount(target); expect(document.contains(widget.el)).toBe(true); widget.destroy(); expect(document.contains(widget.el)).toBe(false); }); }); describe("composition", () => { class WidgetA extends Widget { name = "a"; template = `
Hello
`; widgets = { b: WidgetB }; } class WidgetB extends Widget { template = `
world
`; } test("a widget with a sub widget", async () => { const widget = makeWidget(WidgetA); const target = document.createElement("div"); await widget.mount(target); expect(target.innerHTML).toBe("
Hello
world
"); }); test("t-refs on widget are widgets", async () => { class WidgetC extends Widget { name = "a"; template = `
Hello
`; widgets = { b: WidgetB }; } const widget = makeWidget(WidgetC); const target = document.createElement("div"); await widget.mount(target); expect(widget.refs.mywidgetb instanceof WidgetB).toBe(true); }); });