repos / starfx

supercharged async flow control library.
git clone https://github.com/neurosnap/starfx.git

starfx / test
Eric Bower · 04 Mar 24

store.test.ts

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