Skip to content

Commit

Permalink
Fixed an issue that caused sending the same event multiple times to t…
Browse files Browse the repository at this point in the history
…he inspector for restarted services (#3199)
  • Loading branch information
Andarist committed Apr 8, 2022
1 parent 2e6a361 commit f3d6314
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 18 deletions.
5 changes: 5 additions & 0 deletions .changeset/dry-carpets-dream.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@xstate/inspect': patch
---

Fixed an issue that caused sending the same event multiple times to the inspector for restarted services.
43 changes: 25 additions & 18 deletions packages/xstate-inspect/src/browser.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
ActorRef,
AnyInterpreter,
EventData,
EventObject,
interpret,
Expand Down Expand Up @@ -86,6 +87,8 @@ const getFinalOptions = (options?: Partial<InspectorOptions>) => {
};
};

const patchedInterpreters = new Set<AnyInterpreter>();

export function inspect(options?: InspectorOptions): Inspector | undefined {
const { iframe, url, devTools } = getFinalOptions(options);

Expand Down Expand Up @@ -161,25 +164,29 @@ export function inspect(options?: InspectorOptions): Inspector | undefined {
sessionId: service.sessionId
});

// monkey-patch service.send so that we know when an event was sent
// to a service *before* it is processed, since other events might occur
// while the sent one is being processed, which throws the order off
const originalSend = service.send.bind(service);

service.send = function inspectSend(
event: EventObject,
payload?: EventData
) {
inspectService.send({
type: 'service.event',
event: stringifyWithSerializer(
toSCXMLEvent(toEventObject(event as EventObject, payload))
),
sessionId: service.sessionId
});
if (!patchedInterpreters.has(service)) {
patchedInterpreters.add(service);

// monkey-patch service.send so that we know when an event was sent
// to a service *before* it is processed, since other events might occur
// while the sent one is being processed, which throws the order off
const originalSend = service.send.bind(service);

service.send = function inspectSend(
event: EventObject,
payload?: EventData
) {
inspectService.send({
type: 'service.event',
event: stringifyWithSerializer(
toSCXMLEvent(toEventObject(event as EventObject, payload))
),
sessionId: service.sessionId
});

return originalSend(event, payload);
};
return originalSend(event, payload);
};
}

service.subscribe((state) => {
// filter out synchronous notification from within `.start()` call
Expand Down
31 changes: 31 additions & 0 deletions packages/xstate-inspect/test/inspect.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -309,4 +309,35 @@ describe('@xstate/inspect', () => {
]
`);
});

it('should only send events once to the inspector after restarting a service', () => {
const machine = createMachine({});

const devTools = createDevTools();
const iframeMock = createIframeMock();

inspect({
iframe: iframeMock.iframe,
devTools
});

iframeMock.initConnection();

const service = interpret(machine).start();
devTools.register(service);

service.stop();
service.start();
devTools.register(service);

iframeMock.flushMessages();

service.send({ type: 'EV' });

expect(
iframeMock
.flushMessages()
.filter((message: any) => message.type === 'service.event')
).toHaveLength(1);
});
});

0 comments on commit f3d6314

Please sign in to comment.