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
");
});
});