Bug?
When model-testing a machine with a Promise service and providing the test model with all explicit events, the tests fail with the message Missing config for event "done.invoke.fetch". Providing a blank event implementation allows the test to continue, but they never get past the state that invokes that service.
Tests don't fail.
Tests fail.
I would anticipate that internal service invocation events aren't required to be supplied to the test model. There is no meaningful action I can think of that will map to that event.
https://codesandbox.io/s/xstate-missing-config-repro-i4xls
You'll have to click the server rack icon on the left side and run the yarn test script to see this in action.
I've experienced exactly the same issue.
The repo is: https://github.com/metamn/react-best-practices/tree/master/src/components/State
The message is:
FAIL src/components/State/State.test.js
StateWithMachine
โ coverage (1ms)
reaches state: "closed"
โ via (32ms)
reaches state: "opening"
โ via OPEN (17ms)
reaches state: "closing"
โ via OPEN โ CLOSE (10ms)
โ via OPEN โ done.invoke.openMenu โ CLOSE (53ms)
reaches state: "open"
โ via OPEN โ done.invoke.openMenu (9ms)
โ StateWithMachine โบ reaches state: "closing" โบ via OPEN โ done.invoke.openMenu โ CLOSE
Missing config for event "done.invoke.openMenu".
Path:
State: "closed"
Event: {"type":"OPEN"}
State: "opening"
Event: {"type":"done.invoke.openMenu"}
State: "closing"
at TestModel.Object.<anonymous>.TestModel.getEventExecutor (node_modules/@xstate/test/src/index.ts:259:13)
at TestModel.<anonymous> (node_modules/@xstate/test/src/index.ts:270:27)
at step (node_modules/@xstate/test/lib/index.js:43:23)
at Object.next (node_modules/@xstate/test/lib/index.js:24:53)
at node_modules/@xstate/test/lib/index.js:18:71
at Object.<anonymous>.__awaiter (node_modules/@xstate/test/lib/index.js:14:12)
at TestModel.Object.<anonymous>.TestModel.executeEvent (node_modules/@xstate/test/lib/index.js:342:16)
at Object.exec (node_modules/@xstate/test/src/index.ts:108:39)
at TestModel.<anonymous> (node_modules/@xstate/test/src/index.ts:157:33)
at step (node_modules/@xstate/test/lib/index.js:43:23)
at Object.next (node_modules/@xstate/test/lib/index.js:24:53)
at fulfilled (node_modules/@xstate/test/lib/index.js:15:58)
โ StateWithMachine โบ reaches state: "open" โบ via OPEN โ done.invoke.openMenu
Missing config for event "done.invoke.openMenu".
Path:
State: "closed"
Event: {"type":"OPEN"}
State: "opening"
Event: {"type":"done.invoke.openMenu"}
State: "open"
at TestModel.Object.<anonymous>.TestModel.getEventExecutor (node_modules/@xstate/test/src/index.ts:259:13)
at TestModel.<anonymous> (node_modules/@xstate/test/src/index.ts:270:27)
at step (node_modules/@xstate/test/lib/index.js:43:23)
at Object.next (node_modules/@xstate/test/lib/index.js:24:53)
at node_modules/@xstate/test/lib/index.js:18:71
at Object.<anonymous>.__awaiter (node_modules/@xstate/test/lib/index.js:14:12)
at TestModel.Object.<anonymous>.TestModel.executeEvent (node_modules/@xstate/test/lib/index.js:342:16)
at Object.exec (node_modules/@xstate/test/src/index.ts:108:39)
at TestModel.<anonymous> (node_modules/@xstate/test/src/index.ts:157:33)
at step (node_modules/@xstate/test/lib/index.js:43:23)
at Object.next (node_modules/@xstate/test/lib/index.js:24:53)
at fulfilled (node_modules/@xstate/test/lib/index.js:15:58)
โ StateWithMachine โบ coverage
Missing coverage for state nodes:
(machine).open
123 | */
124 | it("coverage", () => {
> 125 | testModel.testCoverage();
| ^
126 | });
127 | });
128 |
at TestModel.Object.<anonymous>.TestModel.testCoverage (node_modules/@xstate/test/src/index.ts:300:13)
at Object.testCoverage (src/components/State/State.test.js:125:15)
Test Suites: 1 failed, 1 total
Tests: 3 failed, 3 passed, 6 total
Snapshots: 0 total
Time: 1.762s, estimated 2s
Ran all test suites related to changed files.
Watch Usage: Press w to show more.
Thanks a lot!
Thanks for the repro, gonna look into this.
By the way, for testing, you probably do not need to invoke services since instead you should model the services being invoked as states, where an event that transitions to a "done" state would be the resolution of that imaginary "service" (e.g., polling to wait that a loading indicator is no longer showing), so this has workarounds.
But I think the best solution would be to _not_ error when event configuration for built-in events is missing.
you probably do not need to invoke services since instead you should model the services being invoked as states
I'm having trouble wrapping my head around that. It seems like I'd have to change the machine definition to make that work, but only in test environments. Can you provide a small example of what you mean?
A "test machine" could use such a strategy to implement "event executor".
The problem here (as per @metamn repro) is slightly different though. It tests an "implementation machine" which invokes external~ service and from what I understand in such case you don't want to provide an executor for that done event. But rather test model should be able to await that service "as normal".
It fails here:
https://github.com/davidkpiano/xstate/blob/6f883a1f9d5fe001d244e804d9c8b46b0fab0f5e/packages/xstate-test/src/index.ts#L177
because it's just a proxy to executeEvent:
https://github.com/davidkpiano/xstate/blob/6f883a1f9d5fe001d244e804d9c8b46b0fab0f5e/packages/xstate-test/src/index.ts#L128
So I think a solution (which would have to be implemented) would be to recognize those special events and await for service to be completed - and that if needed could be mocked externally to avoid real API requests in tests etc.
@davidkpiano is my understanding correct?
It tests an "implementation machine"
Yes, this is the important line. By using services and (assumedly) the real machine, you're testing the machine that actually implements the app, not the abstract machine that represents the behavior as a user would experience it. Big difference.
Yes, that's correct. From what we've discussed testing "implementation" machines might be OK in certain situations, right? And thus a created test model could support that.
I'm not sure though how to properly await invoked service right now though. Seems like we would have to recognize which ID has been invoked and await it through children/activities property of the machine?