import { signal } from "$core/signal"; import { deserializeScope, getSerializedState, hydrate, isHydrated, isServerRendered, serializeScope } from "$core/ssr"; import { beforeEach, describe, expect, it } from "vitest"; describe("ssr", () => { describe("serializeScope", () => { it("serializes signals to their values", () => { const scope = { count: signal(42), name: signal("Alice") }; const json = serializeScope(scope); expect(JSON.parse(json)).toEqual({ count: 42, name: "Alice" }); }); it("handles primitives without signals", () => { const scope = { static: "value", number: 123 }; const json = serializeScope(scope); expect(JSON.parse(json)).toEqual({ static: "value", number: 123 }); }); it("handles mixed signals and primitives", () => { const scope = { reactive: signal(true), static: false }; const json = serializeScope(scope); expect(JSON.parse(json)).toEqual({ reactive: true, static: false }); }); it("handles empty scope", () => { const scope = {}; const json = serializeScope(scope); expect(JSON.parse(json)).toEqual({}); }); }); describe("deserializeScope", () => { it("creates signals from plain values", () => { const data = { count: 42, name: "Bob" }; const scope = deserializeScope(data); expect(scope.count).toBeDefined(); expect(scope.name).toBeDefined(); expect(typeof scope.count).toBe("object"); expect((scope.count as { get: () => number }).get()).toBe(42); expect((scope.name as { get: () => string }).get()).toBe("Bob"); }); it("handles empty data", () => { const scope = deserializeScope({}); expect(Object.keys(scope)).toHaveLength(0); }); it("handles various data types", () => { const data = { string: "hello", number: 123, boolean: true, nullValue: null }; const scope = deserializeScope(data); expect((scope.string as { get: () => string }).get()).toBe("hello"); expect((scope.number as { get: () => number }).get()).toBe(123); expect((scope.boolean as { get: () => boolean }).get()).toBe(true); expect((scope.nullValue as { get: () => null }).get()).toBe(null); }); }); describe("isHydrated", () => { it("returns false for non-hydrated elements", () => { const el = document.createElement("div"); expect(isHydrated(el)).toBe(false); }); it("returns true for hydrated elements", () => { const el = document.createElement("div"); el.dataset.voltHydrated = "true"; expect(isHydrated(el)).toBe(true); }); }); describe("isServerRendered", () => { it("returns false when no serialized state exists", () => { const el = document.createElement("div"); el.id = "app"; expect(isServerRendered(el)).toBe(false); }); it("returns true when serialized state exists", () => { const el = document.createElement("div"); el.id = "app"; el.innerHTML = ` `; expect(isServerRendered(el)).toBe(true); }); it("returns false when element has no id", () => { const el = document.createElement("div"); expect(isServerRendered(el)).toBe(false); }); }); describe("getSerializedState", () => { it("extracts state from script tag", () => { const el = document.createElement("div"); el.id = "app"; el.innerHTML = ` `; const state = getSerializedState(el); expect(state).toEqual({ count: 42, name: "Charlie" }); }); it("returns null when no script tag exists", () => { const el = document.createElement("div"); el.id = "app"; const state = getSerializedState(el); expect(state).toBeNull(); }); it("returns null when element has no id", () => { const el = document.createElement("div"); const state = getSerializedState(el); expect(state).toBeNull(); }); it("returns null for malformed JSON", () => { const el = document.createElement("div"); el.id = "app"; el.innerHTML = ` `; const state = getSerializedState(el); expect(state).toBeNull(); }); it("returns null for empty script tag", () => { const el = document.createElement("div"); el.id = "app"; el.innerHTML = ` `; const state = getSerializedState(el); expect(state).toBeNull(); }); }); describe("hydrate", () => { beforeEach(() => { document.body.innerHTML = ""; }); it("hydrates element with serialized state", () => { document.body.innerHTML = `
5
10
10