diff --git a/tests/components/__snapshots__/reactivity.test.ts.snap b/tests/components/__snapshots__/reactivity.test.ts.snap new file mode 100644 index 00000000..0d05e657 --- /dev/null +++ b/tests/components/__snapshots__/reactivity.test.ts.snap @@ -0,0 +1,65 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`reactivity in lifecycle can use a state hook 1`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component } = bdom; + let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue } = helpers; + + let block1 = createBlock(\`
\`); + + return function template(ctx, node, key = \\"\\") { + let d1 = ctx['counter'].value; + return block1([d1]); + } +}" +`; + +exports[`reactivity in lifecycle change state while mounting component 1`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component } = bdom; + let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue } = helpers; + + let block1 = createBlock(\`
\`); + + return function template(ctx, node, key = \\"\\") { + let d1 = ctx['state'].val; + return block1([d1]); + } +}" +`; + +exports[`reactivity in lifecycle state changes in willUnmount do not trigger rerender 1`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component } = bdom; + let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue } = helpers; + + let block1 = createBlock(\`\`); + + return function template(ctx, node, key = \\"\\") { + let d1 = ctx['props'].val; + let d2 = ctx['state'].n; + return block1([d1, d2]); + } +}" +`; + +exports[`reactivity in lifecycle state changes in willUnmount do not trigger rerender 2`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component } = bdom; + let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue } = helpers; + + let block1 = createBlock(\`
\`); + + return function template(ctx, node, key = \\"\\") { + let b2; + if (ctx['state'].flag) { + b2 = component(\`Child\`, {val: ctx['state'].val}, key + \`__1\`, node, ctx); + } + return block1([], [b2]); + } +}" +`; diff --git a/tests/components/basics.test.ts b/tests/components/basics.test.ts index 5730bce1..698738ec 100644 --- a/tests/components/basics.test.ts +++ b/tests/components/basics.test.ts @@ -938,22 +938,23 @@ describe("mount targets", () => { }); describe.skip("mount special cases", () => { - test("widget can be mounted on different target", async () => { - // class MyWidget extends Component { - // static template = xml`
Hey
`; - // patched() { - // throw new Error("patched should not be called"); + test("component can be mounted on different target", async () => { + // class Comp extends Component { + // static template = xml`
Hey
`; + // setup() { + // onPatched(() => { + // throw new Error("patched should not be called"); + // }); + // } // } - // } - // const div = document.createElement("div"); - // const span = document.createElement("span"); - // fixture.appendChild(div); - // fixture.appendChild(span); - // const w = new MyWidget(); - // await w.mount(div); - // expect(fixture.innerHTML).toBe("
Hey
"); - // await w.mount(span); - // expect(fixture.innerHTML).toBe("
Hey
"); + // const div = document.createElement("div"); + // const span = document.createElement("span"); + // fixture.appendChild(div); + // fixture.appendChild(span); + // const comp = await mount(Comp, div); + // expect(fixture.innerHTML).toBe("
Hey
"); + // await comp.__owl__.mount(span, ); + // expect(fixture.innerHTML).toBe("
Hey
"); }); test("widget can be mounted on different target, another situation", async () => { @@ -1017,119 +1018,6 @@ describe.skip("mount special cases", () => { // expect(fixture.innerHTML).toBe("
Hey
"); // expect(steps).toEqual(["1 resolved", "2 resolved"]); }); - - test("mounting a destroyed widget", async () => { - // class MyWidget extends Component { - // static template = xml`
Hey
`; - // } - // const w = new MyWidget(); - // w.destroy(); // because, why not - // let error; - // try { - // await w.mount(fixture); - // } catch (e) { - // error = e; - // } - // expect(scheduler.tasks.length).toBe(0); - // expect(error).toBeDefined(); - // expect(error.message).toBe("Cannot mount a destroyed component"); - }); - - test("destroying a sub-component cleans itself from parent's vnode", async () => { - // class C1 extends Component { - // static template = xml`
`; - // } - // class P extends Component { - // static components = { C1 }; - // static template = xml`
`; - // state = { - // a: "first", - // }; - // } - // const parent = new P(); - // await parent.mount(fixture); - // expect(fixture.textContent).toBe("first"); - // parent.unmount(); - // parent.state.a = ""; - // parent.mount(fixture); - // parent.state.a = "fixed"; - // await parent.render(); - // expect(fixture.textContent).toBe("fixed"); - }); - - test("destroying a sub-component cleans itself from parent's vnode, part 2", async () => { - // class C1 extends Component { - // static template = xml`
`; - // } - // class P extends Component { - // static components = { C1 }; - // static template = xml`
some text
`; - // state = { - // a: "first", - // }; - // } - // const parent = new P(); - // await parent.mount(fixture); - // expect(fixture.textContent).toBe("firstsome text"); - // parent.unmount(); - // parent.state.a = ""; - // parent.mount(fixture); - // parent.state.a = "fixed"; - // await parent.render(); - // expect(fixture.textContent).toBe("fixedsome text"); - }); - - test("destroying a sub-component cleans itself from parent's vnode, part 3", async () => { - // class C1 extends Component { - // static template = xml`
`; - // } - // class C2 extends Component { - // static template = xml``; - // static components = { C1 }; - // } - // class P extends Component { - // static components = { C2 }; - // static template = xml`
`; - // state = { - // a: "first", - // }; - // } - // const parent = new P(); - // await parent.mount(fixture); - // expect(fixture.textContent).toBe("first"); - // parent.unmount(); - // parent.state.a = ""; - // parent.mount(fixture); - // parent.state.a = "fixed"; - // await parent.render(); - // expect(fixture.textContent).toBe("fixed"); - }); - - test("destroying a sub-component cleans itself from parent's vnode, part 4", async () => { - // class C1 extends Component { - // static template = xml`
`; - // } - // class C2 extends Component { - // static template = xml``; - // static components = { C1 }; - // } - // class P extends Component { - // static components = { C2 }; - // static template = xml`
some text
`; - // state = { - // a: "first", - // }; - // } - // const parent = new P(); - // await parent.mount(fixture); - // expect(fixture.textContent).toBe("firstsome text"); - // parent.unmount(); - // parent.state.a = ""; - // parent.mount(fixture); - // parent.state.a = "fixed"; - // await parent.render(); - // expect(fixture.textContent).toBe("fixedsome text"); - }); }); describe("support svg components", () => { diff --git a/tests/components/reactivity.test.ts b/tests/components/reactivity.test.ts index c64b8da8..14dcec63 100644 --- a/tests/components/reactivity.test.ts +++ b/tests/components/reactivity.test.ts @@ -1,77 +1,75 @@ -describe.skip("reactivity in lifecycle", () => { - test.skip("can use a state hook", async () => { - /* class Counter extends Component { +import { + Component, + mount, + onPatched, + onRender, + onWillPatch, + onWillUnmount, + useState, +} from "../../src"; +import { xml } from "../../src/tags"; +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 = new Counter(); - await counter.mount(fixture); + const counter = await mount(Counter, fixture); expect(fixture.innerHTML).toBe("
42
"); counter.counter.value = 3; await nextTick(); - expect(fixture.innerHTML).toBe("
3
");*/ + expect(fixture.innerHTML).toBe("
3
"); }); test("state changes in willUnmount do not trigger rerender", async () => { - // const steps: string[] = []; - // class Child extends Component { - // static template = xml` - // - // `; - // state = useState({ n: 2 }); - // __render(f) { - // steps.push("render"); - // return super.__render(f); - // } - // willPatch() { - // steps.push("willPatch"); - // } - // patched() { - // steps.push("patched"); - // } - // willUnmount() { - // steps.push("willUnmount"); - // this.state.n = 3; - // } - // } - // class Parent extends Component { - // static template = xml` - //
- // - //
- // `; - // static components = { Child }; - // state = useState({ val: 1, flag: true }); - // } - // const widget = await mount(Parent, { target: fixture }); - // expect(steps).toEqual(["render"]); - // expect(fixture.innerHTML).toBe("
12
"); - // widget.state.flag = false; - // await nextTick(); - // // we make sure here that no call to __render is done - // expect(steps).toEqual(["render", "willUnmount"]); - }); - - test("state changes in willUnmount will be applied on remount", async () => { - // class TestWidget extends Component { - // static template = xml` - //
- // `; - // state = useState({ val: 1 }); - // willUnmount() { - // this.state.val = 3; - // } - // } - // const widget = new TestWidget(); - // await widget.mount(fixture); - // expect(fixture.innerHTML).toBe("
1
"); - // widget.unmount(); - // expect(fixture.innerHTML).toBe(""); - // await nextTick(); // wait for changes to be detected before remounting - // await widget.mount(fixture); - // expect(fixture.innerHTML).toBe("
3
"); - // // we want to make sure that there are no remaining tasks left at this point. - // expect(Component.scheduler.tasks.length).toBe(0); + const steps: string[] = []; + class Child extends Component { + static template = xml` + + `; + state = useState({ n: 2 }); + setup() { + onRender(() => { + 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 () => { @@ -103,89 +101,24 @@ describe.skip("reactivity in lifecycle", () => { }); test("change state while 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); - // TestWidget.prototype.__patch = jest.fn(TestWidget.prototype.__patch); - // const widget = new TestWidget(); - // let prom = widget.mount(fixture); - // widget.state.val = 2; - // await prom; - // 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(); - // prom = widget.mount(fixture); - // widget.state.val = 3; - // await prom; - // expect(fixture.innerHTML).toBe("
3
"); - // expect(TestWidget.prototype.__render).toHaveBeenCalledTimes(3); - // expect(TestWidget.prototype.__patch).toHaveBeenCalledTimes(2); - // expect(steps).toEqual([2, 2, 3]); - }); - - test("change state and render while mounted in detached dom", async () => { - // class App extends Component { - // static template = xml`
`; - // state = useState({ val: 1 }); - // } - // const detachedDiv = document.createElement("div"); - // const app = await mount(App, { target: detachedDiv }); - // expect(detachedDiv.innerHTML).toBe("
1
"); - // app.state.val = 2; - // await nextTick(); - // expect(detachedDiv.innerHTML).toBe("
2
"); - }); - - test("destroy and change state after mounted in detached dom", async () => { - // class App extends Component { - // static template = xml`
`; - // state = useState({ val: 1 }); - // } - // const detachedDiv = document.createElement("div"); - // const app = await mount(App, { target: detachedDiv }); - // expect(detachedDiv.innerHTML).toBe("
1
"); - // app.destroy(); - // app.state.val = 2; - // await nextTick(); - // expect(detachedDiv.innerHTML).toBe(""); - }); - - test("change state while component is unmounted", async () => { - // let child; - // class Child extends Component { - // static template = xml``; - // state = useState({ - // val: "C1", - // }); - // constructor(parent, props) { - // super(parent, props); - // child = this; - // } - // } - // class Parent extends Component { - // static components = { Child }; - // static template = xml`
`; - // state = useState({ val: "P1" }); - // } - // const parent = new Parent(); - // await parent.mount(fixture); - // expect(fixture.innerHTML).toBe("
P1C1
"); - // parent.unmount(); - // expect(fixture.innerHTML).toBe(""); - // parent.state.val = "P2"; - // child.state.val = "C2"; - // await parent.mount(fixture); - // expect(fixture.innerHTML).toBe("
P2C2
"); + const steps: any[] = []; + let STATE; + class Comp extends Component { + static template = xml` +
+ `; + state = useState({ val: 1 }); + setup() { + STATE = this.state; + onRender(() => { + 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
"); }); });