This commit is contained in:
81
frontend/__tests__/hooks/createEvent.spec.ts
Normal file
81
frontend/__tests__/hooks/createEvent.spec.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import { createRoot } from "solid-js";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { createEvent } from "../../src/ts/hooks/createEvent";
|
||||
|
||||
describe("createEvent", () => {
|
||||
it("dispatch notifies subscribers", () => {
|
||||
const event = createEvent<string>();
|
||||
const fn = vi.fn();
|
||||
event.subscribe(fn);
|
||||
event.dispatch("hello");
|
||||
expect(fn).toHaveBeenCalledWith("hello");
|
||||
});
|
||||
|
||||
it("dispatch notifies multiple subscribers", () => {
|
||||
const event = createEvent<number>();
|
||||
const fn1 = vi.fn();
|
||||
const fn2 = vi.fn();
|
||||
event.subscribe(fn1);
|
||||
event.subscribe(fn2);
|
||||
event.dispatch(42);
|
||||
expect(fn1).toHaveBeenCalledWith(42);
|
||||
expect(fn2).toHaveBeenCalledWith(42);
|
||||
});
|
||||
|
||||
it("dispatch with no type arg requires no arguments", () => {
|
||||
const event = createEvent();
|
||||
const fn = vi.fn();
|
||||
event.subscribe(fn);
|
||||
event.dispatch();
|
||||
expect(fn).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("subscribe returns an unsubscribe function", () => {
|
||||
const event = createEvent<string>();
|
||||
const fn = vi.fn();
|
||||
const unsub = event.subscribe(fn);
|
||||
event.dispatch("a");
|
||||
unsub();
|
||||
event.dispatch("b");
|
||||
expect(fn).toHaveBeenCalledTimes(1);
|
||||
expect(fn).toHaveBeenCalledWith("a");
|
||||
});
|
||||
|
||||
it("two independent events do not share state", () => {
|
||||
const eventA = createEvent<string>();
|
||||
const eventB = createEvent<string>();
|
||||
const fnA = vi.fn();
|
||||
const fnB = vi.fn();
|
||||
eventA.subscribe(fnA);
|
||||
eventB.subscribe(fnB);
|
||||
eventA.dispatch("a");
|
||||
expect(fnA).toHaveBeenCalledWith("a");
|
||||
expect(fnB).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("useListener auto-unsubscribes on dispose", () => {
|
||||
const event = createEvent<string>();
|
||||
const fn = vi.fn();
|
||||
createRoot((dispose) => {
|
||||
event.useListener(fn);
|
||||
event.dispatch("inside");
|
||||
dispose();
|
||||
});
|
||||
event.dispatch("outside");
|
||||
expect(fn).toHaveBeenCalledTimes(1);
|
||||
expect(fn).toHaveBeenCalledWith("inside");
|
||||
});
|
||||
|
||||
it("subscriber errors do not prevent other subscribers from running", () => {
|
||||
const event = createEvent<string>();
|
||||
const fn1 = vi.fn(() => {
|
||||
throw new Error("oops");
|
||||
});
|
||||
const fn2 = vi.fn();
|
||||
event.subscribe(fn1);
|
||||
event.subscribe(fn2);
|
||||
event.dispatch("test");
|
||||
expect(fn1).toHaveBeenCalled();
|
||||
expect(fn2).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
102
frontend/__tests__/hooks/createSignalWithSetters.spec.ts
Normal file
102
frontend/__tests__/hooks/createSignalWithSetters.spec.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { createRoot } from "solid-js";
|
||||
import { createSignalWithSetters } from "../../src/ts/hooks/createSignalWithSetters";
|
||||
|
||||
describe("createSignalWithSetters", () => {
|
||||
it("returns default value from getter", () => {
|
||||
createRoot((dispose) => {
|
||||
const [count] = createSignalWithSetters(42)({});
|
||||
expect(count()).toBe(42);
|
||||
dispose();
|
||||
});
|
||||
});
|
||||
|
||||
it("exposes raw set on the setters object", () => {
|
||||
createRoot((dispose) => {
|
||||
const [count, { set }] = createSignalWithSetters(0)({});
|
||||
set(7);
|
||||
expect(count()).toBe(7);
|
||||
dispose();
|
||||
});
|
||||
});
|
||||
|
||||
it("raw set accepts an updater function", () => {
|
||||
createRoot((dispose) => {
|
||||
const [count, { set }] = createSignalWithSetters(3)({});
|
||||
set((prev) => prev * 2);
|
||||
expect(count()).toBe(6);
|
||||
dispose();
|
||||
});
|
||||
});
|
||||
|
||||
it("calls a no-arg named setter", () => {
|
||||
createRoot((dispose) => {
|
||||
const [count, { increment }] = createSignalWithSetters(0)({
|
||||
increment: (set) => set((n) => n + 1),
|
||||
});
|
||||
increment();
|
||||
expect(count()).toBe(1);
|
||||
increment();
|
||||
expect(count()).toBe(2);
|
||||
dispose();
|
||||
});
|
||||
});
|
||||
|
||||
it("calls a named setter with custom args", () => {
|
||||
createRoot((dispose) => {
|
||||
const [count, { addBy }] = createSignalWithSetters(0)({
|
||||
addBy: (set, amount: number) => set((n) => n + amount),
|
||||
});
|
||||
addBy(5);
|
||||
expect(count()).toBe(5);
|
||||
addBy(3);
|
||||
expect(count()).toBe(8);
|
||||
dispose();
|
||||
});
|
||||
});
|
||||
|
||||
it("supports multiple named setters independently", () => {
|
||||
createRoot((dispose) => {
|
||||
const [count, { increment, decrement, reset }] = createSignalWithSetters(
|
||||
10,
|
||||
)({
|
||||
increment: (set) => set((n) => n + 1),
|
||||
decrement: (set) => set((n) => n - 1),
|
||||
reset: (set) => set(0),
|
||||
});
|
||||
increment();
|
||||
expect(count()).toBe(11);
|
||||
decrement();
|
||||
decrement();
|
||||
expect(count()).toBe(9);
|
||||
reset();
|
||||
expect(count()).toBe(0);
|
||||
dispose();
|
||||
});
|
||||
});
|
||||
|
||||
it("works with non-primitive default values", () => {
|
||||
createRoot((dispose) => {
|
||||
const [state, { setName }] = createSignalWithSetters({ name: "Alice" })({
|
||||
setName: (set, name: string) => set((prev) => ({ ...prev, name })),
|
||||
});
|
||||
setName("Bob");
|
||||
expect(state().name).toBe("Bob");
|
||||
dispose();
|
||||
});
|
||||
});
|
||||
|
||||
it("raw set and named setters share the same underlying signal", () => {
|
||||
createRoot((dispose) => {
|
||||
const [count, { increment, set }] = createSignalWithSetters(0)({
|
||||
increment: (set) => set((n) => n + 1),
|
||||
});
|
||||
increment();
|
||||
set(100);
|
||||
expect(count()).toBe(100);
|
||||
increment();
|
||||
expect(count()).toBe(101);
|
||||
dispose();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user