/** * @jsxImportSource solid-js * @jest-environment jsdom */ import "../../test/MessageChannel"; import { lazy, createSignal, createResource, useTransition, enableScheduling } from "../../src"; import { render, Suspense, SuspenseList } from "../src"; import { createStore } from "../../store/src"; enableScheduling(); beforeEach(() => { jest.useFakeTimers(); }); afterEach(() => { jest.useRealTimers(); }); describe("Testing Basics", () => { test("Children are reactive", () => { let div = document.createElement("div"); let increment: () => void; render(() => { const [count, setCount] = createSignal(0); increment = () => setCount(count() + 1); return {count()}; }, div); expect(div.innerHTML).toBe("0"); increment!(); expect(div.innerHTML).toBe("1"); }); }); describe("Testing Suspense", () => { let div = document.createElement("div"), disposer: () => void, resolvers: Function[] = [], [triggered, trigger] = createSignal(); const LazyComponent = lazy(() => new Promise(r => resolvers.push(r))), ChildComponent = (props: { greeting: string }) => { const [value] = createResource( triggered, () => new Promise(r => setTimeout(() => r("Jo"), 300)), { initialValue: "" } ); return () => `${props.greeting} ${value()}`; }, Component = () => ( . ); test("Create Suspense control flow", () => { disposer = render(Component, div); expect(div.innerHTML).toBe("Loading"); }); test("Toggle Suspense control flow", async () => { for (const r of resolvers) r({ default: ChildComponent }); await Promise.resolve(); jest.runAllTimers(); await Promise.resolve(); expect(div.innerHTML).toBe("Hi, .Hello "); }); test("Toggle with refresh transition", async () => { const [pending, start] = useTransition(); let finished = false; start(() => trigger("Jo")).then(() => (finished = true)); expect(div.innerHTML).toBe("Hi, .Hello "); expect(finished).toBe(false); // wait trigger resource refetch await Promise.resolve(); expect(div.innerHTML).toBe("Hi, .Hello "); expect(pending()).toBe(true); expect(finished).toBe(false); // Exhausts create-resource setTimeout jest.runAllTimers(); // wait update suspence state await Promise.resolve(); // wait update computation jest.runAllTimers(); // wait write signal suc await Promise.resolve(); jest.runAllTimers(); await Promise.resolve(); expect(div.innerHTML).toBe("Hi, Jo.Hello Jo"); expect(pending()).toBe(false); expect(finished).toBe(true); }); test("Toggle with store and refresh transition", async () => { const [store, setStore] = createStore({ count: 0 }); const [pending, start] = useTransition(); let finished = false; start(() => { setStore({ count: 1 }); trigger("Jack"); }).then(() => (finished = true)); expect(store.count).toBe(0); expect(finished).toBe(false); // wait trigger resource refetch await Promise.resolve(); expect(store.count).toBe(0); expect(pending()).toBe(true); expect(finished).toBe(false); // Exhausts create-resource setTimeout jest.runAllTimers(); // wait update suspence state await Promise.resolve(); // wait update computation jest.runAllTimers(); // Await the rest of the things, TODO: figure out what these are await Promise.resolve(); jest.runAllTimers(); await Promise.resolve(); expect(pending()).toBe(false); expect(finished).toBe(true); expect(store.count).toBe(1); }); test("dispose", () => { div.innerHTML = ""; disposer(); }); }); describe("SuspenseList", () => { const promiseFactory = (time: number) => { return (v: string) => new Promise(r => { setTimeout(() => { r(v); }, time); }); }, A = () => { const [value] = createResource("A", promiseFactory(200)); return
{value()}
; }, B = () => { const [value] = createResource("B", promiseFactory(100)); return
{value()}
; }, C = () => { const [value] = createResource("C", promiseFactory(300)); return
{value()}
; }; test("revealOrder together", async () => { const div = document.createElement("div"), Comp = () => ( Loading 1}> Loading 2}> Loading 3}> ); const dispose = render(Comp, div); expect(div.innerHTML).toBe("
Loading 1
Loading 2
Loading 3
"); jest.advanceTimersByTime(110); await Promise.resolve(); expect(div.innerHTML).toBe("
Loading 1
Loading 2
Loading 3
"); jest.advanceTimersByTime(100); await Promise.resolve(); expect(div.innerHTML).toBe("
Loading 1
Loading 2
Loading 3
"); jest.advanceTimersByTime(100); // wait effect update await Promise.resolve(); expect(div.innerHTML).toBe("
A
B
C
"); dispose(); }); test("revealOrder forwards", async () => { const div = document.createElement("div"), Comp = () => ( Loading 1}>
Loading 2}> Loading 3}> ); const dispose = render(Comp, div); expect(div.innerHTML).toBe("
Loading 1
Loading 2
Loading 3
"); jest.advanceTimersByTime(110); await Promise.resolve(); expect(div.innerHTML).toBe("
Loading 1
Loading 2
Loading 3
"); jest.advanceTimersByTime(100); await Promise.resolve(); expect(div.innerHTML).toBe("
A
B
Loading 3
"); jest.advanceTimersByTime(100); await Promise.resolve(); expect(div.innerHTML).toBe("
A
B
C
"); dispose(); }); test("revealOrder forwards hidden", async () => { const div = document.createElement("div"), Comp = () => ( Loading 1}>
Loading 2}> Loading 3}> ); const dispose = render(Comp, div); expect(div.innerHTML).toBe(""); jest.advanceTimersByTime(110); await Promise.resolve(); expect(div.innerHTML).toBe(""); jest.advanceTimersByTime(100); await Promise.resolve(); expect(div.innerHTML).toBe("
A
B
"); jest.advanceTimersByTime(100); await Promise.resolve(); expect(div.innerHTML).toBe("
A
B
C
"); dispose(); }); test("revealOrder forwards", async () => { const div = document.createElement("div"), Comp = () => ( Loading 1}>
Loading 2}> Loading 3}> ); const dispose = render(Comp, div); expect(div.innerHTML).toBe("
Loading 1
Loading 2
Loading 3
"); jest.advanceTimersByTime(110); await Promise.resolve(); expect(div.innerHTML).toBe("
Loading 1
Loading 2
Loading 3
"); jest.advanceTimersByTime(100); await Promise.resolve(); expect(div.innerHTML).toBe("
A
B
Loading 3
"); jest.advanceTimersByTime(100); await Promise.resolve(); expect(div.innerHTML).toBe("
A
B
C
"); dispose(); }); test("revealOrder forwards collapse", async () => { const div = document.createElement("div"), Comp = () => ( Loading 1}>
Loading 2}> Loading 3}> ); const dispose = render(Comp, div); expect(div.innerHTML).toBe("
Loading 1
"); jest.advanceTimersByTime(110); await Promise.resolve(); expect(div.innerHTML).toBe("
Loading 1
"); jest.advanceTimersByTime(100); await Promise.resolve(); expect(div.innerHTML).toBe("
A
B
Loading 3
"); jest.advanceTimersByTime(100); await Promise.resolve(); expect(div.innerHTML).toBe("
A
B
C
"); dispose(); }); test("revealOrder backwards collapse", async () => { const div = document.createElement("div"), Comp = () => ( Loading 1}>
Loading 2}> Loading 3}> ); const dispose = render(Comp, div); expect(div.innerHTML).toBe("
Loading 3
"); jest.advanceTimersByTime(110); await Promise.resolve(); expect(div.innerHTML).toBe("
Loading 3
"); jest.advanceTimersByTime(100); await Promise.resolve(); expect(div.innerHTML).toBe("
Loading 3
"); jest.advanceTimersByTime(100); await Promise.resolve(); expect(div.innerHTML).toBe("
A
B
C
"); dispose(); }); test("nested SuspenseList together", async () => { const div = document.createElement("div"), Comp = () => ( Loading 1}>
Loading 2}> Loading 3}> ); const dispose = render(Comp, div); expect(div.innerHTML).toBe("
Loading 1
Loading 2
Loading 3
"); jest.advanceTimersByTime(110); await Promise.resolve(); expect(div.innerHTML).toBe("
Loading 1
Loading 2
Loading 3
"); jest.advanceTimersByTime(100); await Promise.resolve(); expect(div.innerHTML).toBe("
Loading 1
Loading 2
Loading 3
"); jest.advanceTimersByTime(100); await Promise.resolve(); expect(div.innerHTML).toBe("
A
B
C
"); dispose(); }); test("nested SuspenseList forwards", async () => { const div = document.createElement("div"), Comp = () => ( Loading 1}>
Loading 2}> Loading 3}> ); const dispose = render(Comp, div); expect(div.innerHTML).toBe("
Loading 1
Loading 2
Loading 3
"); jest.advanceTimersByTime(110); await Promise.resolve(); expect(div.innerHTML).toBe("
Loading 1
Loading 2
Loading 3
"); jest.advanceTimersByTime(100); await Promise.resolve(); expect(div.innerHTML).toBe("
A
B
Loading 3
"); jest.advanceTimersByTime(100); await Promise.resolve(); expect(div.innerHTML).toBe("
A
B
C
"); dispose(); }); });