diff --git a/README.md b/README.md index ec25b2d..6685acf 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ pairing with smart people at Hashrocket. For a steady stream of TILs, [sign up for my newsletter](https://crafty-builder-6996.ck.page/e169c61186). -_1160 TILs and counting..._ +_1161 TILs and counting..._ --- @@ -1354,6 +1354,7 @@ _1160 TILs and counting..._ ### XState - [Always Use Inline Functions With Assign](xstate/always-use-inline-functions-with-assign.md) +- [Custom Jest Matcher For XState Machine States](xstate/custom-jest-matcher-for-xstate-machine-states.md) - [Define Event That Does Internal Self Transition](xstate/define-event-that-does-internal-self-transition.md) - [Events Stop Propagating Once Handled](xstate/events-stop-propagating-once-handled.md) - [Inline Actions vs Actions In Machine Options](xstate/inline-actions-vs-actions-in-machine-options.md) diff --git a/xstate/custom-jest-matcher-for-xstate-machine-states.md b/xstate/custom-jest-matcher-for-xstate-machine-states.md new file mode 100644 index 0000000..799f11b --- /dev/null +++ b/xstate/custom-jest-matcher-for-xstate-machine-states.md @@ -0,0 +1,62 @@ +# Custom Jest Matcher For XState Machine States + +Here is a custom matcher for asserting the current state of an XState machine +for Jest-based tests. I made some adaptations to it, but it was mostly +developed by [Nick +Nisi](https://discord.com/channels/795785288994652170/809564635614150686/897559009077362738). + +```typescript +import {State} from 'xstate' + +declare global { + namespace jest { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + interface Matchers { + toMatchState(state: string): CustomMatcherResult + } + } +} + +expect.extend({ + toMatchState(state: State, value: string) { + return { + pass: state.matches(value), + message: () => + `Expected + "${JSON.stringify(state.value)}" +state to match + "${JSON.stringify(value)}"`, + } + }, +}) +``` + +To make this available to your tests, place it in your `setupTests.ts` (or +`.js`) file, assuming that is configured in your `jest.config.js`. + +It can be used in your tests like so: + +```javascript +test("it transitions to open and back to closed", async () => { + const service = interpret(confirmationMachine); + + service.start(); + + service.send({ + type: "OPEN_DIALOG", + doubleConfirmText: "taco", + action: jest.fn() + }); + + expect(service.state).toMatchState({ open: "idle" }) + + service.send({ type: "CANCEL" }); + + expect(service.state).toMatchState("closed") +}); +``` + +Notice you can pass either a string or object representation of the state, if +there is nesting. + +What is nice about this custom matcher is the informative failure messaging.