Skip to content

Commit 07acf6f

Browse files
committed
Get compound states working!
1 parent 34b0fd8 commit 07acf6f

2 files changed

Lines changed: 351 additions & 200 deletions

File tree

src/index.test.ts

Lines changed: 72 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1-
import { always, call, cond, entry, exit, on, start } from "./index";
1+
import { always, compound, cond, entry, exit, on, start } from "./index";
22

33
const fetch = jest.fn();
44
beforeEach(fetch.mockClear);
55

66
const finishedLoading = jest.fn();
77
beforeEach(finishedLoading.mockClear);
88

9+
const succeeded = jest.fn();
10+
beforeEach(succeeded.mockClear);
11+
912
describe("Machine with entry and exit actions", () => {
1013
const someURL = new URL("https://example.org/");
1114
function fetchData() {
@@ -22,7 +25,9 @@ describe("Machine with entry and exit actions", () => {
2225
yield on("SUCCESS", success);
2326
yield on("FAILURE", failure);
2427
}
25-
function* success() {}
28+
function* success() {
29+
yield entry(succeeded);
30+
}
2631
function* failure() {
2732
yield on("RETRY", loading);
2833
}
@@ -63,11 +68,13 @@ describe("Machine with entry and exit actions", () => {
6368
expect(finishedLoading).toHaveBeenCalledTimes(1);
6469
expect(loader.changeCount).toEqual(2);
6570
expect(loader.value).toEqual("success");
71+
expect(succeeded).toHaveBeenCalledTimes(1);
6672

6773
const transitionResult2 = loader.next("FETCH");
68-
expect(transitionResult2.actions).toEqual([]);
74+
// expect(transitionResult2.actions).toEqual([]);
6975
expect(loader.changeCount).toEqual(2);
7076
expect(loader.value).toEqual("success");
77+
expect(succeeded).toHaveBeenCalledTimes(1);
7178

7279
await loader.resolved;
7380
});
@@ -79,7 +86,7 @@ describe("Machine with entry and exit actions", () => {
7986
});
8087

8188
test("sending events", async () => {
82-
const loader = start(Loader, [{ url: someURL }]);
89+
const loader = start(Loader);
8390
expect(loader.value).toEqual("idle");
8491

8592
const transitionResult = loader.next("FETCH");
@@ -116,9 +123,7 @@ describe("Machine with entry and exit actions", () => {
116123
});
117124
});
118125

119-
describe("Form Field Machine with entry and exit actions", () => {
120-
// const validate = jest.fn();
121-
// beforeEach(validate.mockClear);
126+
describe("Form Field Machine with always()", () => {
122127
const isValid = jest.fn();
123128
beforeEach(isValid.mockClear);
124129

@@ -127,20 +132,12 @@ describe("Form Field Machine with entry and exit actions", () => {
127132
yield on("CHANGE", editing);
128133
}
129134
function* editing() {
130-
// yield exit(validate);
131135
yield on("CHANGE", editing);
132136
yield on("BLUR", validating);
133137
}
134138
function* validating() {
135139
yield always(cond(isValid, valid));
136140
yield always(invalid);
137-
138-
// yield on(null, cond(isValid, valid));
139-
// yield on(null, invalid);
140-
141-
// yield always([cond(isValid, valid), invalid]);
142-
// return [cond(isValid, valid), invalid];
143-
// return conds([[isValid, valid], [true, invalid]]);
144141
}
145142
function* invalid() {
146143
yield on("CHANGE", editing);
@@ -152,11 +149,6 @@ describe("Form Field Machine with entry and exit actions", () => {
152149
return initial;
153150
}
154151

155-
test("creating", () => {
156-
const formField = start(FormField);
157-
expect(formField).toBeDefined();
158-
});
159-
160152
describe("when is valid", () => {
161153
beforeEach(() => {
162154
isValid.mockReturnValue(true);
@@ -173,11 +165,11 @@ describe("Form Field Machine with entry and exit actions", () => {
173165

174166
formField.next("CHANGE");
175167
expect(formField.value).toEqual("editing");
176-
expect(formField.changeCount).toEqual(2);
168+
expect(formField.changeCount).toEqual(1);
177169

178170
formField.next("BLUR");
179171
expect(formField.value).toEqual("valid");
180-
expect(formField.changeCount).toEqual(4);
172+
expect(formField.changeCount).toEqual(3);
181173
});
182174
});
183175

@@ -197,106 +189,79 @@ describe("Form Field Machine with entry and exit actions", () => {
197189

198190
formField.next("CHANGE");
199191
expect(formField.value).toEqual("editing");
200-
expect(formField.changeCount).toEqual(2);
192+
expect(formField.changeCount).toEqual(1);
201193

202194
formField.next("BLUR");
203195
expect(formField.value).toEqual("invalid");
204-
expect(formField.changeCount).toEqual(4);
196+
expect(formField.changeCount).toEqual(3);
205197
});
206198
});
207199
});
208200

209-
describe("Machine with call", () => {
210-
function Loader({ url }: { url: URL }) {
211-
function* idle() {
212-
yield on("FETCH", loading);
213-
}
214-
function* loading() {
215-
yield call(fetch, [url.toString()]);
216-
yield on("SUCCESS", success);
217-
yield on("FAILURE", failure);
201+
describe("Hierarchical Traffic Lights Machine", () => {
202+
// const validate = jest.fn();
203+
// beforeEach(validate.mockClear);
204+
const isValid = jest.fn();
205+
beforeEach(isValid.mockClear);
206+
207+
function PedestrianFactory() {
208+
function* walk() {
209+
yield on("PED_COUNTDOWN", wait);
218210
}
219-
function* success() {}
220-
function* failure() {
221-
yield on("RETRY", loading);
211+
function* wait() {
212+
yield on("PED_COUNTDOWN", stop);
222213
}
214+
function* stop() {}
215+
function* blinking() {}
223216

224-
return idle;
217+
return { walk, blinking };
225218
}
219+
function* TrafficLights() {
220+
const { walk, blinking } = PedestrianFactory();
226221

227-
const someURL = new URL("https://example.org/");
228-
229-
test("creating", () => {
230-
const loader = start(Loader, [{ url: someURL }]);
231-
expect(loader).toBeDefined();
232-
});
233-
234-
describe("when fetch succeeds", () => {
235-
beforeEach(() => {
236-
fetch.mockResolvedValue(42);
237-
});
238-
239-
test("sending events", async () => {
240-
const loader = start(Loader, [{ url: someURL }]);
241-
expect(loader.value).toEqual("idle");
242-
await expect(loader.resolved).resolves.toEqual([]);
243-
244-
loader.next("NOOP");
245-
expect(loader.value).toEqual("idle");
246-
expect(loader.changeCount).toEqual(0);
247-
await expect(loader.resolved).resolves.toEqual([]);
248-
249-
loader.next("FETCH");
250-
expect(loader.value).toEqual("loading");
251-
expect(loader.changeCount).toEqual(1);
252-
253-
expect(fetch).toHaveBeenCalledWith("https://example.org/");
254-
255-
await expect(loader.resolved).resolves.toEqual([42]);
256-
expect(loader.changeCount).toEqual(2);
257-
expect(loader.value).toEqual("success");
258-
259-
loader.next("FETCH");
260-
expect(loader.changeCount).toEqual(2);
261-
262-
await loader.resolved;
263-
});
264-
});
265-
266-
describe("when fetch fails", () => {
267-
beforeEach(() => {
268-
fetch.mockRejectedValueOnce(new Error("Failed!")).mockResolvedValue(42);
269-
});
270-
271-
test("sending events", async () => {
272-
const loader = start(Loader, [{ url: someURL }]);
273-
expect(loader.value).toEqual("idle");
274-
275-
loader.next("FETCH");
276-
expect(loader.value).toEqual("loading");
277-
expect(loader.changeCount).toEqual(1);
278-
279-
expect(fetch).toHaveBeenCalledTimes(1);
280-
expect(fetch).toHaveBeenLastCalledWith("https://example.org/");
281-
282-
await expect(loader.resolved).rejects.toEqual(new Error("Failed!"));
283-
expect(loader.changeCount).toEqual(2);
284-
expect(loader.value).toEqual("failure");
222+
function* green() {
223+
yield on("TIMER", yellow);
224+
}
225+
function* yellow() {
226+
yield on("TIMER", red);
227+
}
228+
function* red() {
229+
yield on("TIMER", green);
285230

286-
loader.next("FETCH");
287-
expect(fetch).toHaveBeenCalledTimes(1);
288-
expect(loader.changeCount).toEqual(2);
231+
return walk;
232+
}
289233

290-
loader.next("RETRY");
291-
expect(loader.value).toEqual("loading");
292-
expect(loader.changeCount).toEqual(3);
234+
yield on("POWER_OUTAGE", compound(red, blinking));
235+
yield on("POWER_RESTORED", compound(red));
293236

294-
expect(fetch).toHaveBeenCalledTimes(2);
295-
expect(fetch).toHaveBeenLastCalledWith("https://example.org/");
237+
return green;
238+
}
296239

297-
await expect(loader.resolved).resolves.toEqual([42]);
298-
expect(loader.changeCount).toEqual(4);
299-
expect(loader.value).toEqual("success");
300-
});
240+
test("sending events", () => {
241+
const machine = start(TrafficLights);
242+
expect(machine).toBeDefined();
243+
expect(machine.value).toEqual("green");
244+
245+
machine.next("TIMER");
246+
expect(machine.value).toEqual("yellow");
247+
expect(machine.changeCount).toEqual(1);
248+
249+
machine.next("TIMER");
250+
// expect(machine.value).toEqual("red");
251+
// expect(machine.value).toEqual(["red", "walk"]);
252+
expect(machine.value).toEqual({ "red": "walk" });
253+
expect(machine.changeCount).toEqual(3);
254+
255+
machine.next("TIMER");
256+
expect(machine.value).toEqual("green");
257+
expect(machine.changeCount).toEqual(4);
258+
259+
machine.next("POWER_RESTORED");
260+
expect(machine.value).toEqual({ "red": "walk" });
261+
expect(machine.changeCount).toEqual(6);
262+
263+
machine.next("POWER_OUTAGE");
264+
expect(machine.value).toEqual({ "red": "blinking" });
265+
expect(machine.changeCount).toEqual(7);
301266
});
302267
});

0 commit comments

Comments
 (0)