summaryrefslogtreecommitdiff
path: root/src/shapex.test.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/shapex.test.ts')
-rw-r--r--src/shapex.test.ts173
1 files changed, 171 insertions, 2 deletions
diff --git a/src/shapex.test.ts b/src/shapex.test.ts
index c57e676..00cf1e6 100644
--- a/src/shapex.test.ts
+++ b/src/shapex.test.ts
@@ -29,6 +29,38 @@ describe("subscribe", () => {
});
});
+describe("subscribe: async", () => {
+ it("subscribes to an event", () => {
+ const $ = ShapeX({ counter: 1 });
+ const id = $.subscribe("test-event", async (state) =>
+ Promise.resolve({ state }),
+ );
+
+ expect(id).toBe(1);
+ expect($.subscriptionCount("test-event")).toBe(1);
+ });
+
+ it("subscribes to an event once", () => {
+ const $ = ShapeX({ counter: 1 });
+ const id = $.subscribeOnce("test-event", async (state) =>
+ Promise.resolve({ state }),
+ );
+
+ expect(id).toBe(1);
+ expect($.subscriptionCount("test-event")).toBe(1);
+ });
+
+ it("unsubscribes from an event", () => {
+ const $ = ShapeX({ counter: 1 });
+
+ $.subscribe("test-event", async (state) => Promise.resolve({ state }));
+ expect($.subscriptionCount("test-event")).toBe(1);
+
+ $.unsubscribe("test-event");
+ expect($.subscriptionCount("test-event")).toBe(0);
+ });
+});
+
describe("dispatch", () => {
it("dispatches an event without arguments", () => {
type AppState = {
@@ -174,7 +206,7 @@ describe("dispatch", () => {
// This callback receives ChildEventData
const childEventCb: EventCallback<AppState, ChildEventData> = (
state,
- data
+ data,
) => ({
state: data ? { ...state, counter: data.message.length } : state,
});
@@ -204,7 +236,7 @@ describe("dispatch", () => {
// Child event should be called with the child event data
expect(spyChildCb).toHaveBeenCalledWith(
{ counter: 1 },
- { message: "ID 123 processed" }
+ { message: "ID 123 processed" },
);
// State should be updated based on the message length
@@ -212,6 +244,143 @@ describe("dispatch", () => {
});
});
+describe("dispatch: async", () => {
+ it("dispatches an event without arguments", () => {
+ type AppState = {
+ counter: number;
+ };
+
+ const $ = ShapeX<AppState>({ counter: 1 });
+ const cb: EventCallback<AppState> = async (state) =>
+ Promise.resolve({ state });
+ const spyCb = vi.fn(cb);
+
+ $.subscribe("test-event", spyCb);
+ $.dispatch("test-event");
+
+ expect(spyCb).toHaveBeenCalledWith({ counter: 1 });
+ });
+
+ it("dispatches an event with arguments", () => {
+ type AppState = {
+ counter: number;
+ };
+
+ const $ = ShapeX<AppState>({ counter: 1 });
+
+ const testEventCb: EventCallback<AppState, string> = async (state, _) =>
+ Promise.resolve({
+ state,
+ });
+
+ const callback = vi.fn(testEventCb);
+
+ $.subscribe("test-event", callback);
+ $.dispatch("test-event", "arg1-value");
+
+ expect(callback).toHaveBeenCalledWith({ counter: 1 }, "arg1-value");
+ });
+
+ it("updates state when event handler returns new state", async () => {
+ type AppState = {
+ counter: number;
+ };
+
+ const $ = ShapeX<AppState>({ counter: 1 });
+
+ const state = await vi.waitFor(
+ () => {
+ return new Promise((resolve) => {
+ $.subscribe("$.counter", (state) => {
+ resolve(state);
+ return { state };
+ });
+
+ $.subscribe("increment", async (state) =>
+ Promise.resolve({
+ state: { ...state, counter: state.counter + 1 },
+ }),
+ );
+
+ $.dispatch("increment");
+ });
+ },
+ {
+ timeout: 1000,
+ interval: 100,
+ },
+ );
+
+ expect(state).toStrictEqual({ counter: 2 });
+ });
+
+ it("dispatches nested events", async () => {
+ type AppState = {
+ counter: number;
+ };
+
+ const $ = ShapeX({ counter: 1 });
+
+ const state = await vi.waitFor(() => {
+ return new Promise((resolve) => {
+ $.subscribe("nested-event", (state) => {
+ resolve(true);
+ return { state };
+ });
+
+ $.subscribe("parent-event", (state) => ({
+ state,
+ dispatch: { to: "nested-event" },
+ }));
+
+ $.dispatch("parent-event");
+ });
+ });
+
+ expect(state).toBe(true);
+ });
+
+ it("dispatches multiple nested events", async () => {
+ type AppState = {
+ counter: number;
+ };
+
+ const $ = ShapeX({ counter: 1 });
+
+ const state = await vi.waitFor(() => {
+ return new Promise((resolve) => {
+ let count = 0;
+
+ $.subscribe("nested-event-1", (state) => {
+ count++;
+ return { state };
+ });
+
+ $.subscribe("nested-event-2", (state) => {
+ resolve(count + 1);
+ return { state };
+ });
+
+ $.subscribe("parent-event", async (state) =>
+ Promise.resolve({
+ state,
+ dispatch: [
+ { to: "nested-event-1" },
+ {
+ to: "nested-event-2",
+ },
+ ],
+ }),
+ );
+
+ $.dispatch("parent-event");
+ });
+ });
+
+ expect(state).toBe(2);
+ });
+});
+
describe("state change detection", () => {
it("detects value changes in state", () => {
type AppState = {