pocak
Version:
State machine
142 lines (120 loc) • 3.91 kB
JavaScript
const stateMachine = require("./index.js");
const Turnstil = {
0: "Locked",
1: "Unlocked",
Locked: 0,
Unlocked: 1
};
const coin = (current, states) => () => {
if (Array.isArray(states)) {
if (current === states[0]) {
return states[1];
}
} else if (current === states.Locked) {
return states.Unlocked;
}
return current;
};
const push = (current, states) => () => {
if (Array.isArray(states)) {
if (current === states[1]) {
return states[0];
}
} else if (current === states.Unlocked) {
return states.Locked;
}
return current;
};
const transitions = [coin, push];
describe("StateMachine", () => {
it("should move to the correct state", () => {
const [state, coin, push] = stateMachine(Turnstil, transitions);
expect(state()).toBe(Turnstil.Locked);
coin();
expect(state()).toBe(Turnstil.Unlocked);
push();
push();
expect(state()).toBe(Turnstil.Locked);
coin();
expect(state()).toBe(Turnstil.Unlocked);
});
it("should be able to subscribe to state changes", () => {
const cb = jest.fn(id => id);
const [state, coin] = stateMachine(Turnstil, transitions);
state.subscribe(cb);
coin(200);
expect(cb.mock.calls.length).toBe(1);
coin(100);
coin(100);
expect(cb.mock.calls.length).toBe(3);
cb.mockClear();
cb.mockReset();
const [state2, coin2] = stateMachine(["Locked", "Unlocked"], transitions);
state2.subscribe(cb);
coin2(200);
expect(cb.mock.calls.length).toBe(1);
coin2(100);
coin2(100);
expect(cb.mock.calls.length).toBe(3);
cb.mockClear();
cb.mockReset();
});
it("should unsubscribe from state changes", () => {
const cb = jest.fn(id => id);
const [state, coin] = stateMachine(Turnstil, transitions);
const unsubscribe = state.subscribe(cb);
unsubscribe();
coin(100);
coin(100);
coin(100);
coin(100);
expect(cb.mock.calls.length).toBe(0);
});
it("should tell the current state", () => {
const [state] = stateMachine(Turnstil);
expect(state()).toBe(Turnstil.Locked);
const [state2] = stateMachine(["FirstState"]);
expect(state2()).toBe("FirstState");
});
it("should move to other state when appropriate action is called", () => {
const [state, coin] = stateMachine(Turnstil, transitions);
const _state = coin();
expect(state()).toBe(Turnstil.Unlocked);
expect(_state).toBe(Turnstil.Unlocked);
const [state2, coin2] = stateMachine(["Locked", "Unlocked"], transitions);
const _state2 = coin2();
expect(state2()).toBe("Unlocked");
expect(_state2).toBe("Unlocked");
});
it("should not move to other state when unappropriate action is called", () => {
const [state, coin, push] = stateMachine(Turnstil, transitions);
push();
expect(state()).toBe(Turnstil.Locked);
});
it("should let the state to be define", () => {
const [state] = stateMachine(Turnstil, transitions, Turnstil.Unlocked);
expect(state()).toBe(Turnstil.Unlocked);
const [state2] = stateMachine(["Locked", "Unlocked"], transitions, 1);
expect(state2()).toBe("Unlocked");
});
it("should pass arguments to transition functions", () => {
const myTransitionFn = jest.fn(id => id);
const [state, myTransition] = stateMachine(Turnstil, [
() => myTransitionFn
]);
myTransition("1", 2, 3.2);
expect(myTransitionFn.mock.calls.length).toBe(1);
expect(myTransitionFn).toHaveBeenCalledWith("1", 2, 3.2);
myTransitionFn.mockClear();
myTransitionFn.mockReset();
const [state2, myTransition2] = stateMachine(
["Locked", "Unlocked"],
[() => myTransitionFn]
);
myTransition2("1", 2, 3.2);
expect(myTransitionFn.mock.calls.length).toBe(1);
expect(myTransitionFn).toHaveBeenCalledWith("1", 2, 3.2);
myTransitionFn.mockClear();
myTransitionFn.mockReset();
});
});