release: WRNexusJS 0.8.3
This commit is contained in:
@@ -452,3 +452,124 @@ test("$emit inside component functions does not depend on a global browser event
|
||||
expect(win.document.querySelector("span")!.textContent).toBe("true");
|
||||
expect(detail).toEqual({ source: "click" });
|
||||
});
|
||||
|
||||
test("template literals work inside browser API call arguments", () => {
|
||||
const behavior = Buffer.from(
|
||||
JSON.stringify({
|
||||
functions: `function save() {
|
||||
window.localStorage.setItem(\`prefs:\${count}\`, JSON.stringify(count))
|
||||
message = \`Saved \${count}\`
|
||||
}`,
|
||||
watches: [],
|
||||
lifecycle: {},
|
||||
}),
|
||||
).toString("base64");
|
||||
const win = mount(
|
||||
`<div data-scope="count: 3, message: 'ready'" data-wrn-behavior="${behavior}">` +
|
||||
`<button data-on-click="save()">{message}</button></div>`,
|
||||
);
|
||||
win.document.querySelector("button")!.click();
|
||||
expect(win.localStorage.getItem("prefs:3")).toBe("3");
|
||||
expect(win.document.querySelector("button")!.textContent).toBe("Saved 3");
|
||||
});
|
||||
|
||||
test("compiled client functions are not overwritten by fallback behavior parsing", () => {
|
||||
const behavior = Buffer.from(
|
||||
JSON.stringify({
|
||||
functions: `function save() { message = \`fallback\` }`,
|
||||
watches: [],
|
||||
lifecycle: {},
|
||||
}),
|
||||
).toString("base64");
|
||||
const win = new Window() as unknown as Window & Record<string, unknown>;
|
||||
win.document.body.innerHTML =
|
||||
`<div id="scope" data-scope="message: 'ready'" data-wrn-behavior="${behavior}">` +
|
||||
`<button data-on-click="save()">{message}</button></div>`;
|
||||
const scope = win.document.getElementById("scope") as unknown as HTMLElement & {
|
||||
__wrnexusClientModule?: unknown;
|
||||
};
|
||||
scope.__wrnexusClientModule = {
|
||||
bindClientScope(context: { state: Record<string, unknown> }) {
|
||||
return {
|
||||
save() {
|
||||
context.state.message = "native";
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
(globalThis as Record<string, unknown>).window = win;
|
||||
(globalThis as Record<string, unknown>).document = win.document;
|
||||
(globalThis as Record<string, unknown>).location = win.location;
|
||||
(globalThis as Record<string, unknown>).NodeFilter = (
|
||||
win as unknown as { NodeFilter: unknown }
|
||||
).NodeFilter;
|
||||
(globalThis as Record<string, unknown>).MutationObserver = (
|
||||
win as unknown as { MutationObserver: unknown }
|
||||
).MutationObserver;
|
||||
(0, eval)(REACTIVE_RUNTIME);
|
||||
(win as unknown as { __wrnexusHydrateScopes?: (root: unknown) => void }).__wrnexusHydrateScopes?.(
|
||||
win.document,
|
||||
);
|
||||
win.document.querySelector("button")!.click();
|
||||
expect(win.document.querySelector("button")!.textContent).toBe("native");
|
||||
});
|
||||
|
||||
test("$route updates after client navigation", () => {
|
||||
const win = new Window({ url: "http://localhost/" }) as unknown as Window &
|
||||
Record<string, unknown>;
|
||||
win.document.body.innerHTML =
|
||||
`<span hidden data-wrn-route-params='{"id":"42"}'></span>` +
|
||||
`<div data-scope=""><strong>{$route.pathname}</strong><em>{$route.params.id}</em></div>`;
|
||||
(globalThis as Record<string, unknown>).window = win;
|
||||
(globalThis as Record<string, unknown>).document = win.document;
|
||||
(globalThis as Record<string, unknown>).location = win.location;
|
||||
(globalThis as Record<string, unknown>).NodeFilter = (
|
||||
win as unknown as { NodeFilter: unknown }
|
||||
).NodeFilter;
|
||||
(globalThis as Record<string, unknown>).MutationObserver = (
|
||||
win as unknown as { MutationObserver: unknown }
|
||||
).MutationObserver;
|
||||
(0, eval)(REACTIVE_RUNTIME);
|
||||
(win as unknown as { __wrnexusHydrateScopes?: (root: unknown) => void }).__wrnexusHydrateScopes?.(
|
||||
win.document,
|
||||
);
|
||||
expect(win.document.querySelector("strong")!.textContent).toBe("/");
|
||||
expect(win.document.querySelector("em")!.textContent).toBe("42");
|
||||
expect(win.document.querySelector("[data-wrn-route-params]")).toBeNull();
|
||||
win.history.pushState({}, "", "/settings?tab=chat");
|
||||
win.dispatchEvent(
|
||||
new (win as unknown as { CustomEvent: typeof CustomEvent }).CustomEvent(
|
||||
"wrnexus:navigated",
|
||||
) as unknown as Parameters<Window["dispatchEvent"]>[0],
|
||||
);
|
||||
expect(win.document.querySelector("strong")!.textContent).toBe("/settings");
|
||||
});
|
||||
|
||||
test("cache invalidation refetches matching client Async boundaries", async () => {
|
||||
let requests = 0;
|
||||
(globalThis as Record<string, unknown>).fetch = () => {
|
||||
requests++;
|
||||
return Promise.resolve(Response.json({ data: { value: requests } }));
|
||||
};
|
||||
const win = mount(
|
||||
`<section data-wrn-async="uploads" data-wrn-async-tags="uploads" data-wrn-async-retries="0">` +
|
||||
`<div data-wrn-async-content></div>` +
|
||||
`<template data-wrn-async-loading>Loading</template>` +
|
||||
`<template data-wrn-async-success data-wrn-async-alias="uploads"><b>{uploads.value}</b></template>` +
|
||||
`<template data-wrn-async-error data-wrn-async-alias="error">{error.message}</template>` +
|
||||
`</section>`,
|
||||
);
|
||||
const runtime = win as unknown as { __wrnexusHydrateAsyncBoundaries?: (root: unknown) => void };
|
||||
runtime.__wrnexusHydrateAsyncBoundaries?.(win.document);
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
expect(win.document.querySelector("b")?.textContent).toBe("1");
|
||||
win.dispatchEvent(
|
||||
new (win as unknown as { CustomEvent: typeof CustomEvent }).CustomEvent(
|
||||
"wrnexus:cache:invalidate",
|
||||
{ detail: { tags: ["uploads"] } },
|
||||
) as unknown as Parameters<Window["dispatchEvent"]>[0],
|
||||
);
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
expect(requests).toBe(2);
|
||||
expect(win.document.querySelector("b")?.textContent).toBe("2");
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user