import { Component, mount, onPatched, onWillRender, onWillPatch, onWillUnmount, useState, xml, } from "../../src"; import { makeTestFixture, nextTick, snapshotEverything } from "../helpers"; let fixture: HTMLElement; snapshotEverything(); beforeEach(() => { fixture = makeTestFixture(); }); describe("reactivity in lifecycle", () => { test("can use a state hook", async () => { class Counter extends Component { static template = xml`
`; counter = useState({ value: 42 }); } const counter = await mount(Counter, fixture); expect(fixture.innerHTML).toBe("
42
"); counter.counter.value = 3; await nextTick(); expect(fixture.innerHTML).toBe("
3
"); }); test("can use a state hook 2", async () => { let n = 0; class Comp extends Component { static template = xml`
`; state = useState({ a: 5, b: 7 }); setup() { onWillRender(() => n++); } } const comp = await mount(Comp, fixture); expect(fixture.innerHTML).toBe("
5
"); expect(n).toBe(1); comp.state.a = 11; await nextTick(); expect(fixture.innerHTML).toBe("
11
"); expect(n).toBe(2); comp.state.b = 13; await nextTick(); expect(fixture.innerHTML).toBe("
11
"); expect(n).toBe(2); // no new rendering occured: b was never read via state! }); test("state changes in willUnmount do not trigger rerender", async () => { const steps: string[] = []; class Child extends Component { static template = xml` `; state = useState({ n: 2 }); setup() { onWillRender(() => { steps.push("render"); }); onWillPatch(() => { steps.push("willPatch"); }); onPatched(() => { steps.push("patched"); }); onWillUnmount(() => { steps.push("willUnmount"); this.state.n = 3; }); } } class Parent extends Component { static template = xml`
`; static components = { Child }; state = useState({ val: 1, flag: true }); } const parent = await mount(Parent, fixture); expect(steps).toEqual(["render"]); expect(fixture.innerHTML).toBe("
12
"); parent.state.flag = false; await nextTick(); // we make sure here that no call to __render is done expect(steps).toEqual(["render", "willUnmount"]); }); test("change state just before mounting component", async () => { // const steps: number[] = []; // class TestWidget extends Component { // static template = xml` //
// `; // state = useState({ val: 1 }); // __render(f) { // steps.push(this.state.val); // return super.__render(f); // } // } // TestWidget.prototype.__render = jest.fn(TestWidget.prototype.__render); // const widget = new TestWidget(); // widget.state.val = 2; // await widget.mount(fixture); // expect(fixture.innerHTML).toBe("
2
"); // expect(TestWidget.prototype.__render).toHaveBeenCalledTimes(1); // // unmount and re-mount, as in this case, willStart won't be called, so it's // // slightly different // widget.unmount(); // widget.state.val = 3; // await widget.mount(fixture); // expect(fixture.innerHTML).toBe("
3
"); // expect(TestWidget.prototype.__render).toHaveBeenCalledTimes(2); // expect(steps).toEqual([2, 3]); }); test("change state while mounting component", async () => { const steps: any[] = []; let STATE; class Comp extends Component { static template = xml`
`; state = useState({ val: 1 }); setup() { STATE = this.state; onWillRender(() => { steps.push(this.state.val); }); } } const prom = mount(Comp, fixture); (STATE as any).val = 2; await prom; expect(steps).toEqual([2]); expect(fixture.innerHTML).toBe("
2
"); }); });