Skip to content

Commit

Permalink
Add extra test cases related to error reporting (#4517)
Browse files Browse the repository at this point in the history
  • Loading branch information
Andarist authored Nov 29, 2023
1 parent a533ecc commit d648de0
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 11 deletions.
5 changes: 1 addition & 4 deletions packages/core/test/actor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,7 @@ import {
stopChild
} from '../src/index.ts';
import { setup } from '../src/setup.ts';

function sleep(ms: number) {
return new Promise((res) => setTimeout(res, ms));
}
import { sleep } from '@xstate-repo/jest-utils';

describe('spawning machines', () => {
const context = {
Expand Down
110 changes: 107 additions & 3 deletions packages/core/test/errors.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { createMachine, fromCallback, fromPromise, createActor } from '../src';
import { sleep } from '@xstate-repo/jest-utils';

const cleanups: (() => void)[] = [];
function installGlobalOnErrorHandler(handler: (ev: ErrorEvent) => void) {
Expand Down Expand Up @@ -165,14 +166,18 @@ describe('error handling', () => {
});
});

it('unhandled rejections when starting a child actor should be reported globally', (done) => {
it('unhandled rejection of a promise actor should be reported globally in absence of error listener', (done) => {
const machine = createMachine({
initial: 'pending',
states: {
pending: {
invoke: {
src: fromPromise(() =>
Promise.reject(new Error('unhandled_rejection_in_actor_start'))
Promise.reject(
new Error(
'unhandled_rejection_in_promise_actor_without_error_listener'
)
)
),
onDone: 'success'
}
Expand All @@ -187,11 +192,110 @@ describe('error handling', () => {

installGlobalOnErrorHandler((ev) => {
ev.preventDefault();
expect(ev.error.message).toEqual('unhandled_rejection_in_actor_start');
expect(ev.error.message).toEqual(
'unhandled_rejection_in_promise_actor_without_error_listener'
);
done();
});
});

it('unhandled rejection of a promise actor should be reported to the existing error listener of its parent', async () => {
const errorSpy = jest.fn();

const machine = createMachine({
initial: 'pending',
states: {
pending: {
invoke: {
src: fromPromise(() =>
Promise.reject(
new Error(
'unhandled_rejection_in_promise_actor_with_parent_listener'
)
)
),
onDone: 'success'
}
},
success: {
type: 'final'
}
}
});

const actorRef = createActor(machine);
actorRef.subscribe({
error: errorSpy
});
actorRef.start();

await sleep(0);

expect(errorSpy).toMatchMockCallsInlineSnapshot(`
[
[
[Error: unhandled_rejection_in_promise_actor_with_parent_listener],
],
]
`);
});

it('unhandled rejection of a promise actor should be reported to the existing error listener of its grandparent', async () => {
const errorSpy = jest.fn();

const child = createMachine({
initial: 'pending',
states: {
pending: {
invoke: {
src: fromPromise(() =>
Promise.reject(
new Error(
'unhandled_rejection_in_promise_actor_with_grandparent_listener'
)
)
),
onDone: 'success'
}
},
success: {
type: 'final'
}
}
});

const machine = createMachine({
initial: 'pending',
states: {
pending: {
invoke: {
src: child,
onDone: 'success'
}
},
success: {
type: 'final'
}
}
});

const actorRef = createActor(machine);
actorRef.subscribe({
error: errorSpy
});
actorRef.start();

await sleep(0);

expect(errorSpy).toMatchMockCallsInlineSnapshot(`
[
[
[Error: unhandled_rejection_in_promise_actor_with_grandparent_listener],
],
]
`);
});

it('handled sync errors thrown when starting a child actor should not be reported globally', (done) => {
const machine = createMachine({
initial: 'pending',
Expand Down
5 changes: 1 addition & 4 deletions packages/core/test/invoke.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,7 @@ import {
Snapshot,
ActorRef
} from '../src/index.ts';

function sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
import { sleep } from '@xstate-repo/jest-utils';

const user = { name: 'David' };

Expand Down
1 change: 1 addition & 0 deletions scripts/jest-utils/index.d.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export declare function sleep(ms: number): Promise<undefined>;
export declare function clearConsoleMocks(): void;
2 changes: 2 additions & 0 deletions scripts/jest-utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ const { consoleSpies } = require('./console-spies');

module.exports.clearConsoleMocks = () =>
Object.values(consoleSpies).forEach((spy) => spy.mockClear());

module.exports.sleep = (ms) => new Promise((res) => setTimeout(res, ms));

0 comments on commit d648de0

Please sign in to comment.