repos / starfx

a micro-mvc framework for react apps
git clone https://github.com/neurosnap/starfx.git

starfx / src / test
Eric Bower  ·  2025-06-06

store.test.ts

  1import {
  2  type Operation,
  3  type Result,
  4  createScope,
  5  parallel,
  6  put,
  7  take,
  8} from "../index.js";
  9import {
 10  StoreContext,
 11  StoreUpdateContext,
 12  createStore,
 13  updateStore,
 14} from "../store/index.js";
 15import { expect, test } from "../test.js";
 16
 17interface User {
 18  id: string;
 19  name: string;
 20}
 21
 22interface State {
 23  users: { [key: string]: User };
 24  theme: string;
 25  token: string;
 26  dev: boolean;
 27}
 28
 29function findUserById(state: State, { id }: { id: string }) {
 30  return state.users[id];
 31}
 32
 33function findUsers(state: State) {
 34  return state.users;
 35}
 36
 37interface UpdateUserProps {
 38  id: string;
 39  name: string;
 40}
 41
 42const updateUser =
 43  ({ id, name }: UpdateUserProps) =>
 44  (state: State) => {
 45    // use selectors to find the data you want to mutate
 46    const user = findUserById(state, { id });
 47    user.name = name;
 48
 49    // different ways to update a `zod` record
 50    const users = findUsers(state);
 51    users[id].name = name;
 52
 53    (users as any)[2] = undefined;
 54    users[3] = { id: "", name: "" };
 55
 56    // or mutate state directly without selectors
 57    state.dev = true;
 58  };
 59
 60test("update store and receives update from channel `StoreUpdateContext`", async () => {
 61  expect.assertions(1);
 62  const [scope] = createScope();
 63  const initialState: Partial<State> = {
 64    users: { 1: { id: "1", name: "testing" }, 2: { id: "2", name: "wow" } },
 65    dev: false,
 66  };
 67  createStore({ scope, initialState });
 68  let store;
 69  await scope.run(function* (): Operation<Result<void>[]> {
 70    const result = yield* parallel([
 71      function* () {
 72        store = yield* StoreContext.expect();
 73        const chan = yield* StoreUpdateContext.expect();
 74        const msgList = yield* chan;
 75        yield* msgList.next();
 76      },
 77      function* () {
 78        yield* updateStore(updateUser({ id: "1", name: "eric" }));
 79      },
 80    ]);
 81    return yield* result;
 82  });
 83  expect((store as any)?.getState()).toEqual({
 84    users: { 1: { id: "1", name: "eric" }, 3: { id: "", name: "" } },
 85    dev: true,
 86  });
 87});
 88
 89test("update store and receives update from `subscribe()`", async () => {
 90  expect.assertions(1);
 91  const initialState: Partial<State> = {
 92    users: { 1: { id: "1", name: "testing" }, 2: { id: "2", name: "wow" } },
 93    dev: false,
 94    theme: "",
 95    token: "",
 96  };
 97  const store = createStore({ initialState });
 98
 99  store.subscribe(() => {
100    expect(store.getState()).toEqual({
101      users: { 1: { id: "1", name: "eric" }, 3: { id: "", name: "" } },
102      dev: true,
103      theme: "",
104      token: "",
105    });
106  });
107
108  await store.run(function* () {
109    yield* updateStore(updateUser({ id: "1", name: "eric" }));
110  });
111});
112
113test("emit Action and update store", async () => {
114  expect.assertions(1);
115  const initialState: Partial<State> = {
116    users: { 1: { id: "1", name: "testing" }, 2: { id: "2", name: "wow" } },
117    dev: false,
118    theme: "",
119    token: "",
120  };
121  const store = createStore({ initialState });
122
123  await store.run(function* (): Operation<void> {
124    const result = yield* parallel([
125      function* (): Operation<void> {
126        const action = yield* take<UpdateUserProps>("UPDATE_USER");
127        yield* updateStore(updateUser(action.payload));
128      },
129      function* () {
130        yield* put({ type: "UPDATE_USER", payload: { id: "1", name: "eric" } });
131      },
132    ]);
133    yield* result;
134  });
135
136  expect(store.getState()).toEqual({
137    users: { 1: { id: "1", name: "eric" }, 3: { id: "", name: "" } },
138    theme: "",
139    token: "",
140    dev: true,
141  });
142});
143
144test("resets store", async () => {
145  expect.assertions(2);
146  const initialState: Partial<State> = {
147    users: { 1: { id: "1", name: "testing" }, 2: { id: "2", name: "wow" } },
148    dev: false,
149    theme: "",
150    token: "",
151  };
152  const store = createStore({ initialState });
153
154  await store.run(function* () {
155    yield* store.update((s) => {
156      s.users = { 3: { id: "3", name: "hehe" } };
157      s.dev = true;
158      s.theme = "darkness";
159    });
160  });
161
162  expect(store.getState()).toEqual({
163    users: { 3: { id: "3", name: "hehe" } },
164    theme: "darkness",
165    token: "",
166    dev: true,
167  });
168
169  await store.run(store.reset(["users"]));
170
171  expect(store.getState()).toEqual({
172    users: { 3: { id: "3", name: "hehe" } },
173    dev: false,
174    theme: "",
175    token: "",
176  });
177});