import { AsyncRoot } from "../../src/misc/async_root"; import { config } from "../../src/config"; import { useState } from "../../src/hooks"; import { xml } from "../../src/tags"; import { makeDeferred, makeTestFixture, makeTestEnv, nextTick } from "../helpers"; import { Component } from "../../src/component/component"; //------------------------------------------------------------------------------ // Setup and helpers //------------------------------------------------------------------------------ // We create before each test: // - fixture: a div, appended to the DOM, intended to be the target of dom // manipulations. Note that it is removed after each test. // - a test env, necessary to create components, that is set as env let fixture: HTMLElement; beforeEach(() => { fixture = makeTestFixture(); config.env = makeTestEnv(); }); afterEach(() => { fixture.remove(); }); describe("Asyncroot", () => { test("delayed component with AsyncRoot component", async () => { let def; class Child extends Component { static template = xml``; } class AsyncChild extends Child { willUpdateProps() { return def; } } class Parent extends Component { static template = xml`
`; static components = { Child, AsyncChild, AsyncRoot }; state = useState({ val: 0 }); updateApp() { this.state.val++; } } const parent = new Parent(); await parent.mount(fixture); expect(fixture.querySelector(".children")!.innerHTML).toBe("00"); // click on button to increment Parent counter def = makeDeferred(); fixture.querySelector("button")!.click(); await nextTick(); expect(fixture.querySelector(".children")!.innerHTML).toBe("10"); def.resolve(); await nextTick(); expect(fixture.querySelector(".children")!.innerHTML).toBe("11"); }); test("fast component with AsyncRoot", async () => { let def; class Child extends Component { static template = xml``; } class AsyncChild extends Child { willUpdateProps() { return def; } } class Parent extends Component { static template = xml`
`; static components = { Child, AsyncChild, AsyncRoot }; state = useState({ val: 0 }); updateApp() { this.state.val++; } } const parent = new Parent(); await parent.mount(fixture); expect(fixture.querySelector(".children")!.innerHTML).toBe("00"); // click on button to increment Parent counter def = makeDeferred(); fixture.querySelector("button")!.click(); await nextTick(); expect(fixture.querySelector(".children")!.innerHTML).toBe("10"); def.resolve(); await nextTick(); expect(fixture.querySelector(".children")!.innerHTML).toBe("11"); }); test("asyncroot component: mixed re-renderings", async () => { let def; class Child extends Component { static template = xml` / `; state = useState({ val: 0 }); increment() { this.state.val++; } } class AsyncChild extends Child { willUpdateProps() { return def; } } class Parent extends Component { static template = xml`
`; static components = { Child, AsyncChild, AsyncRoot }; state = useState({ val: 0 }); updateApp() { this.state.val++; } } const parent = new Parent(); await parent.mount(fixture); expect(fixture.querySelector(".children")!.innerHTML).toBe("0/00/0"); // click on button to increment Parent counter def = makeDeferred(); fixture.querySelector("button")!.click(); await nextTick(); expect(fixture.querySelector(".children")!.innerHTML).toBe("0/10/0"); // click on each Child to increment their local counter const children = parent.el!.querySelectorAll("span"); children[0]!.click(); await nextTick(); expect(fixture.querySelector(".children")!.innerHTML).toBe("1/10/0"); children[1]!.click(); await nextTick(); expect(fixture.querySelector(".children")!.innerHTML).toBe("1/10/0"); // finalize first re-rendering (coming from the props update) def.resolve(); await nextTick(); expect(fixture.querySelector(".children")!.innerHTML).toBe("1/11/1"); }); });