Skip to content

Instantly share code, notes, and snippets.

@jfhector
Created February 19, 2022 20:30
Show Gist options
  • Save jfhector/290c7e77828eb1c60b6cbe09e1ba374a to your computer and use it in GitHub Desktop.
Save jfhector/290c7e77828eb1c60b6cbe09e1ba374a to your computer and use it in GitHub Desktop.
wrapInAsyncTryCatch
Feature: wrapInAsyncTryCatch
Scenario: The function returned by wrapInAsyncTryCatch calls the function provided to wrapInAsyncTryCatch with the same arguments
Given wrapInAsyncTryCatch is called with an asynchronous function that does not throw
When the function returned by wrapInAsyncTryCatch is called with some arguments
Then the function provided to wrapInAsyncTryCatch gets called with the same arguments
And the function returned by wrapInAsyncTryCatch returns the same thing as the provided function
Scenario: It catches any error that the provided function throws
Given wrapInAsyncTryCatch is called with an asynchronous function that throws when called with some arguments
When the function returned by wrapInAsyncTryCatch is called with the same arguments
Then the error gets logged to the console
And the function returned by wrapInAsyncTryCatch does not throw
/* eslint-disable no-console */
type WrapInAsyncTryCatch = {
<Args extends any[], ReturnType>(fn: (...args: Args) => ReturnType): (
...args: Args
) => Promise<ReturnType | undefined>;
};
/**
* Use this if you have a function that may throw an error, and you want that error handled so it doesn't propagate further.
*
* `wrapInAsyncTryCatch` is not async itself, but it expects an async function, and returns an async function.
* If the function you're providing is synchronous (i.e. does not return a promise), use `wrapInSynchronousTryCatch` instead.
*
* @param asyncFnToWrapInTryCatch an asynchronous function that you want called inside a try/catch block.
* @returns an asynchronous function which, when called, calls `fn` with the arguments it's given. `fn` will be called inside `try { ... } catch(err) { logs the err and returns }`
*/
const wrapInAsyncTryCatch: WrapInAsyncTryCatch = asyncFnToWrapInTryCatch => {
return async function todoName(...args) {
try {
return await asyncFnToWrapInTryCatch(...args);
} catch (err) {
console.error(err);
return;
}
};
};
export { wrapInAsyncTryCatch };
/* eslint-disable @typescript-eslint/no-empty-function */
/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable no-console */
import { defineFeature, loadFeature } from 'jest-cucumber';
import { wrapInAsyncTryCatch } from '..';
// Syncing with .feature Cucumber file
const feature = loadFeature('../definition.feature', {
loadRelativePath: true,
});
defineFeature(feature, test => {
afterEach(() => {
jest.useRealTimers();
jest.clearAllMocks();
});
test('The function returned by wrapInAsyncTryCatch calls the function provided to wrapInAsyncTryCatch with the same arguments', async ({
given,
when,
then,
and,
}) => {
jest.useFakeTimers();
const MOCK_RESOLVED_VALUE = 'mock_resolved_value';
const functionToProvideToWithErrorBoundary = jest
.fn()
.mockResolvedValue(MOCK_RESOLVED_VALUE);
let functionReturnedByWithErrorBoundary: any;
let valueReturnedByTheFunctionReturnedByWithErrorBoundary: any;
given(
'wrapInAsyncTryCatch is called with an asynchronous function that does not throw',
() => {
functionReturnedByWithErrorBoundary = wrapInAsyncTryCatch(
functionToProvideToWithErrorBoundary
);
}
);
when(
'the function returned by wrapInAsyncTryCatch is called with some arguments',
async () => {
valueReturnedByTheFunctionReturnedByWithErrorBoundary = await functionReturnedByWithErrorBoundary(
'arg1',
'arg2'
);
}
);
then(
'the function provided to wrapInAsyncTryCatch gets called with the same arguments',
() => {
expect(functionToProvideToWithErrorBoundary).toHaveBeenCalledWith(
'arg1',
'arg2'
);
}
);
and(
'the function returned by wrapInAsyncTryCatch returns the same thing as the provided function',
async () => {
expect(valueReturnedByTheFunctionReturnedByWithErrorBoundary).toBe(
await functionToProvideToWithErrorBoundary('arg1', 'arg2')
);
}
);
});
test('It catches any error that the provided function throws', async ({
given,
when,
then,
and,
}) => {
const MOCK_REJECTION_REASON = 'mock_rejection_reason';
const functionToProvideToWithErrorBoundary = jest
.fn()
.mockRejectedValue(MOCK_REJECTION_REASON);
let functionReturnedByWithErrorBoundary: any;
const consoleErrorSpy = jest.spyOn(console, 'error');
given(
'wrapInAsyncTryCatch is called with an asynchronous function that throws when called with some arguments',
() => {
functionReturnedByWithErrorBoundary = wrapInAsyncTryCatch(
functionToProvideToWithErrorBoundary
);
}
);
when(
'the function returned by wrapInAsyncTryCatch is called with the same arguments',
async () => {
await functionReturnedByWithErrorBoundary();
}
);
then('the error gets logged to the console', () => {
expect(consoleErrorSpy).toHaveBeenCalledWith(MOCK_REJECTION_REASON);
});
and('the function returned by wrapInAsyncTryCatch does not throw', () => {
console.log(
'If this gets executed, it means there has been no unhandled exception, and this test is successful'
);
});
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment