Skip to content

Commit

Permalink
feat: try catch custom handler (#51)
Browse files Browse the repository at this point in the history
* v1.22.22

* feat: add catchy libs

* chore: update husky pre commit check unit test

* feat: add verbose mode

* docs: detailing the catchy module multi function test

* refactor: rename e to errType

* refactor: merge errsToCatch when exists
  • Loading branch information
Aldiwildan77 authored Oct 21, 2024
1 parent ea1231b commit 50197e1
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 1 deletion.
2 changes: 2 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@
. "$(dirname "$0")/_/husky.sh"

yarn lint-staged

yarn test
16 changes: 15 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"lint:fix": "next lint --fix",
"prepare": "husky install",
"export": "next export",
"format": "prettier --write ."
"format": "prettier --write .",
"test": "jest"
},
"dependencies": {
"@chakra-ui/icons": "2.1.1",
Expand All @@ -34,6 +35,7 @@
},
"devDependencies": {
"@ianvs/prettier-plugin-sort-imports": "3.4.2",
"@jest/globals": "^29.7.0",
"@types/node": "18.0.0",
"@types/react": "18.2.47",
"@types/react-copy-to-clipboard": "^5.0.7",
Expand All @@ -49,12 +51,24 @@
"eslint-plugin-react": "7.28.0",
"eslint-plugin-react-hooks": "4.3.0",
"husky": "7.0.4",
"jest": "^29.7.0",
"lint-staged": "12.3.2",
"prettier": "2.7.1",
"ts-jest": "^29.2.5",
"typescript": "4.7.4"
},
"lint-staged": {
"*.{js,ts,tsx}": "eslint --fix",
"*.{js,css,md,ts,tsx}": "prettier --write"
},
"jest": {
"preset": "ts-jest",
"testEnvironment": "node",
"transform": {
"node_modules/variables/.+\\.(j|t)sx?$": "ts-jest"
},
"transformIgnorePatterns": [
"node_modules/(?!variables/.*)"
]
}
}
81 changes: 81 additions & 0 deletions src/lib/catchy.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { describe, expect, jest, test } from '@jest/globals';

import { catchy } from './catchy';

async function getUser() {
throw new Error('user not found');
}

async function getMetadata() {
return Promise.resolve('metadata found!');
}

describe('Catchy Module - Single Function', () => {
test('Given a promise that throws an error, when calling catchy, then it should return the error', async () => {
const [err, user] = await catchy(getUser());
expect(err).toBeInstanceOf(Error);
expect(user).toBeUndefined();
});

test('Given a promise that resolves, when calling catchy, then it should return the value', async () => {
const [err, metadata] = await catchy(getMetadata());
expect(err).toBeUndefined();
expect(metadata).toBe('metadata found!');
});

test('Given a promise that throws an error, when calling catchy with an error to catch, then it should return the error', async () => {
const [err, user] = await catchy(getUser(), [Error]);
expect(err).toBeInstanceOf(Error);
expect(user).toBeUndefined();
});
});

describe('Catchy Module - Multiple Functions', () => {
test('Given a multiple function calls, when some of them throw an error at the end, then it should return the error', async () => {
const [errMetadata, metadata] = await catchy(getMetadata());
expect(errMetadata).toBeUndefined();
expect(metadata).toBe('metadata found!');

const [errUser, user] = await catchy(getUser());
expect(errUser).toBeInstanceOf(Error);
expect(user).toBeUndefined();
});

test('Given a multiple function calls, when some of them throw an error at the beginning, then it should return the error', async () => {
const [errUser, user] = await catchy(getUser());
expect(errUser).toBeInstanceOf(Error);
expect(user).toBeUndefined();

const [errMetadata, metadata] = await catchy(getMetadata());
expect(errMetadata).toBeUndefined();
expect(metadata).toBe('metadata found!');
});
});

describe('Catchy Module - Verbose Mode', () => {
test('Given a promise that throws an error, when calling catchy with verbose mode, then it should return the error and log it', async () => {
const consoleError = jest
.spyOn(console, 'error')
.mockImplementation(() => {});
const [err, user] = await catchy(getUser(), undefined, true);

expect(err).toBeInstanceOf(Error);
expect(user).toBeUndefined();
expect(consoleError).toHaveBeenCalled();

consoleError.mockRestore();
});

test('Given a promise that resolves, when calling catchy with verbose mode, then it should return the value', async () => {
const consoleError = jest
.spyOn(console, 'error')
.mockImplementation(() => {});

const [err, metadata] = await catchy(getMetadata(), undefined, true);
expect(err).toBeUndefined();
expect(metadata).toBe('metadata found!');
expect(consoleError).not.toHaveBeenCalled();

consoleError.mockRestore();
});
});
25 changes: 25 additions & 0 deletions src/lib/catchy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// this utility function wraps a promise and catches errors of a specific type.
// it makes your code more catch-strict and less error - prone
// since the `try-catch` must parse the errors to be caught
async function catchy<T, E extends new (message?: string) => Error>(
promise: Promise<T>,
errsToCatch?: Array<E>,
verbose?: boolean,
): Promise<[undefined, T] | [InstanceType<E>]> {
try {
const v = await promise;
return [undefined, v] as [undefined, T];
} catch (err) {
/* eslint-disable curly */
/* eslint-disable no-console */
if (verbose) console.error(err);

if (!errsToCatch || errsToCatch.some((errType) => err instanceof errType)) {
return [err as InstanceType<E>];
}

throw err;
}
}

export { catchy };

0 comments on commit 50197e1

Please sign in to comment.