Files
WRNexusJS/packages/store/test/store.test.ts
2026-08-01 01:09:58 +05:30

89 lines
2.9 KiB
TypeScript

import { expect, test } from "bun:test";
import { createStoreContainer, defineStore } from "../src/index.ts";
const CounterStore = defineStore({
name: "CounterStore",
kind: "global" as const,
createSharedState: () => ({ count: 0 }),
createClientState: () => ({ viewport: 0 }),
createServerState: () => ({ secret: "server-only" }),
computed: { doubled: (state: Readonly<{ count: number }>) => state.count * 2 },
actions: {
increment: [
{
runtime: "client" as const,
handler: ({ state }: any, amount = 1) => {
state.count += amount;
},
},
{
runtime: "server" as const,
handler: ({ state }: any, amount = 1) => {
state.count += amount * 2;
},
},
],
},
});
test("isolates request-scoped server stores and excludes server state", async () => {
const first = createStoreContainer({ runtime: "server", request: {} });
const second = createStoreContainer({ runtime: "server", request: {} });
const a = await first.use(CounterStore);
const b = await second.use(CounterStore);
await a.increment(2);
expect(a.count).toBe(4);
expect(a.doubled).toBe(8);
expect(b.count).toBe(0);
expect(first.serialize()).toEqual({ CounterStore: { count: 4 } });
});
test("store state is readonly outside actions and supports snapshots/reset", async () => {
const container = createStoreContainer({ runtime: "client" });
const store = await container.use(CounterStore);
expect(() => {
(store.state as any).count = 5;
}).toThrow("WRN-STORE-READONLY");
await store.increment(3);
expect(store.snapshot().count).toBe(3);
store.reset();
expect(store.count).toBe(0);
});
test("HMR preserves compatible state and resets incompatible fields", async () => {
const container = createStoreContainer({ runtime: "client" });
const store = await container.use(CounterStore);
await store.increment(2);
const result = await container.hotUpdate(
defineStore({
...CounterStore,
createSharedState: () => ({ count: 0, added: true }),
} as any),
);
expect(result.preserved).toContain("count");
});
test("store lifecycle hooks may update state and page stores dispose cleanly", async () => {
const calls: string[] = [];
const LifecycleStore = defineStore({
name: "LifecycleStore",
kind: "page" as const,
createSharedState: () => ({ ready: false }),
lifecycle: {
clientInit: async ({ state }: any) => {
calls.push("clientInit");
state.ready = true;
},
dispose: async ({ state }: any) => {
calls.push("dispose");
state.ready = false;
},
},
});
const container = createStoreContainer({ runtime: "client", routeId: "/one" });
const store = await container.use(LifecycleStore);
expect(store.ready).toBe(true);
await container.disposePageStores();
expect(calls).toEqual(["clientInit", "dispose"]);
});