@virtualstate/hooks
Version:
177 lines • 6.96 kB
JavaScript
import { createNode, h } from "@virtualstate/fringe";
import { Hook } from "./index.js";
import { assertElement, render } from "@virtualstate/dom";
import { AsyncLocalStorage } from "async_hooks";
describe.each([
[true],
[false]
])("index, mutate: %p", (mutate) => {
it("hooks only a single node", async () => {
const reference = Symbol();
const root = document.createElement("div");
function Component() {
return createNode("div", { reference: reference });
}
const references = [];
const hook = function (node) {
references.push(node.reference);
return node;
};
const node = (h(Hook, { hook: hook, mutate: mutate },
h(Component, null)));
await render(node, root);
expect(references).toContain(reference);
});
it("hooks two nodes", async () => {
const root = document.createElement("div");
function Component() {
return [
createNode("div", { reference: 1 }),
createNode("div", { reference: 2 })
];
}
const references = [];
const hook = function (node) {
references.push(node.reference);
return node;
};
const node = (h(Hook, { hook: hook, mutate: mutate },
h(Component, null)));
await render(node, root);
expect(references).toContain(1);
expect(references).toContain(2);
});
it("hooks four nodes", async () => {
const root = document.createElement("div");
function Component() {
return [
createNode("div", { reference: 1 }),
createNode("div", { reference: 2 }, createNode("span", { reference: 3 }), createNode("span", { reference: 4 }))
];
}
const references = [];
const hook = function (node) {
references.push(node.reference);
return node;
};
const node = (h(Hook, { hook: hook, mutate: mutate },
h(Component, null)));
await render(node, root);
console.log(root.outerHTML);
expect(references).toContain(1);
expect(references).toContain(2);
expect(references).toContain(3);
expect(references).toContain(4);
});
it("hooks four nodes with hook pair", async () => {
const root = document.createElement("div");
function Component() {
return [
createNode("div", { reference: 1 }),
createNode("div", { reference: 2 }, createNode("span", { reference: 3 }), createNode("span", { reference: 4 }))
];
}
const references = [];
const otherReferences = [];
const otherHook = function (node) {
otherReferences.push(node.reference);
return [node, otherHook];
};
const hook = function (node) {
references.push(node.reference);
return [node, node.reference === 2 ? otherHook : hook];
};
const node = (h(Hook, { hook: hook, mutate: mutate },
h(Component, null)));
await render(node, root);
console.log(root.outerHTML);
expect(references).toContain(1);
expect(references).toContain(2);
expect(otherReferences).toContain(3);
expect(otherReferences).toContain(4);
});
it("Throws with no hooks", async () => {
await expect(() => Hook({}, createNode("p"))).rejects.toThrow();
});
describe("node async hooks", () => {
it.each([[true], [false]])("works, children hook: %p", async (childrenHook) => {
const key = `${Math.random()}`;
const value = `${Math.random()}`;
const asyncKey = `${Math.random()}`;
const asyncValue = `${Math.random()}`;
const store = {
[key]: value,
[asyncKey]: asyncValue
};
const storage = new AsyncLocalStorage();
function Sync() {
const store = storage.getStore();
expect(store).toBeTruthy();
return store[key];
}
async function* Async() {
const initialStore = storage.getStore();
await new Promise(resolve => setTimeout(resolve, 50));
const store = storage.getStore();
expect(store).toEqual(initialStore);
expect(store).toBeTruthy();
yield store[key];
}
const hooked = (h(Hook, { hook: childrenHook ? undefined : hookStorage(storage, store), hookChildren: childrenHook ? hookChildrenStorage(storage, store) : undefined, mutate: mutate },
h("a", {}, h(Sync, null)),
h("b", {}, h(Async, null))));
const root = document.createElement("div");
await render(hooked, root);
const syncResult = root.querySelector("a");
const asyncResult = root.querySelector("b");
assertElement(syncResult);
assertElement(asyncResult);
expect(syncResult.innerHTML).toEqual(value);
expect(asyncResult.innerHTML).toEqual(value);
});
function hookStorage(storage, store) {
const childrenHook = hookChildrenStorage(storage, store);
return hook;
function hook(node) {
return [node, hook, childrenHook];
}
}
function hookChildrenStorage(storage, store) {
return node => hookChildren(node, storage, store);
}
async function* hookChildren(node, storage, store) {
const iterator = await storage.run(store, () => node.children[Symbol.asyncIterator]());
let result;
try {
do {
result = await storage.run(store, () => {
return iterator.next();
});
try {
if (isYieldResult(result)) {
yield result.value;
}
}
catch (error) {
const throwResult = await storage.run(store, () => {
return iterator.throw(error);
});
if (isYieldResult(throwResult)) {
yield throwResult.value;
}
result = throwResult ?? result;
}
} while (!result.done);
}
finally {
await storage.run(store, () => {
return iterator.return();
});
}
function isYieldResult(result) {
return !result.done;
}
}
});
});
//# sourceMappingURL=index.spec.js.map