89 lines
2.9 KiB
TypeScript
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"]);
|
|
});
|