Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions .github/workflows/biome.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: Biome CI

on:
pull_request:
branches: [main]
push:
branches: [main]

jobs:
biome:
name: Code Quality Check
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"

- name: Install dependencies
run: npm ci

- name: Run Biome checks
run: npm run lint

- name: Check formatting
run: npm run format
7 changes: 0 additions & 7 deletions .prettierrc.json

This file was deleted.

26 changes: 13 additions & 13 deletions __tests__/TryX.test.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,38 @@
import { TryX } from '../src';
import { TryX } from "../src";

describe('TryX configuration', () => {
it('Should be of the correct instance', () => {
describe("TryX configuration", () => {
it("Should be of the correct instance", () => {
const tx = new TryX({
timeout: 5000
timeout: 5000,
});
expect(tx).toBeInstanceOf(TryX);
});

it('Should set the timeout correctly', () => {
it("Should set the timeout correctly", () => {
const tx = new TryX({
timeout: 5000
timeout: 5000,
});

expect(tx.getConfig()).toEqual({
timeout: 5000
timeout: 5000,
});
})
});

it('Should set the logErrors correctly', () => {
it("Should set the logErrors correctly", () => {
// @ts-expect-error - No timeout will result in a type error. For testing purposes this is fine.
const tx = new TryX({
logErrors: 'always'
logErrors: "always",
});
expect(tx.getConfig()).toEqual({
logErrors: 'always'
logErrors: "always",
});
});

it('Should set the onError callback correctly', () => {
it("Should set the onError callback correctly", () => {
const mockCallback = jest.fn();
const tx = new TryX({
timeout: 5000,
onError: mockCallback
onError: mockCallback,
});

expect(tx.getConfig().onError).toBe(mockCallback);
Expand Down
37 changes: 18 additions & 19 deletions __tests__/executeAsyncHandler.test.ts
Original file line number Diff line number Diff line change
@@ -1,48 +1,47 @@
import { TryX } from "../src";
import { tx } from "../src/lib/tx";

describe('executeAsyncHandler', () => {

it('Should execute an async function and return the result', async () => {
describe("executeAsyncHandler", () => {
it("Should execute an async function and return the result", async () => {
const mockFunction = jest.fn(async () => {
return 'Hello world';
return "Hello world";
});
const { data, error } = await tx.executeAsync(mockFunction);

expect(data).toEqual('Hello world');
expect(data).toEqual("Hello world");
expect(error).toBeNull();
});

it('Should return an error if the async function throws', async () => {
it("Should return an error if the async function throws", async () => {
const mockFunction = jest.fn(async () => {
throw new Error('Oops, something went wrong');
throw new Error("Oops, something went wrong");
});
const { data, error } = await tx.executeAsync(mockFunction);

expect(data).toBeNull();
expect(error).toEqual(new Error('Oops, something went wrong'));
expect(error).toEqual(new Error("Oops, something went wrong"));
});

it('Should return an error if the async function times out', async () => {
it("Should return an error if the async function times out", async () => {
jest.useFakeTimers();

const txShort = new TryX({ timeout: 100 });

const mockFunction = jest.fn(async () => {
await new Promise(resolve => setTimeout(resolve, 200));
await new Promise((resolve) => setTimeout(resolve, 200));
});

const promise = txShort.executeAsync(mockFunction);

// Fast-forward time to trigger the timeout before awaiting
jest.advanceTimersByTime(150);

const { data, error } = await promise;

expect(data).toBeNull();
expect(error).toBeInstanceOf(DOMException);
expect(error!.name).toEqual('AbortError');
expect(error!.name).toEqual("AbortError");

jest.useRealTimers();
});
});
});
34 changes: 17 additions & 17 deletions __tests__/executeHandler.test.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
import { tx } from "../src/lib/tx"
import { tx } from "../src/lib/tx";

describe('executeHandler', () => {
it('Should execute a function and return the result', () => {
describe("executeHandler", () => {
it("Should execute a function and return the result", () => {
const mockFunction = jest.fn(() => {
return 'Hello world'
})
const {data, error } = tx.execute(mockFunction);
return "Hello world";
});
const { data, error } = tx.execute(mockFunction);

expect(data).toEqual('Hello world')
expect(error).toBeNull()
})
expect(data).toEqual("Hello world");
expect(error).toBeNull();
});

it('Should return an error if the function throws', () => {
it("Should return an error if the function throws", () => {
const mockFunction = jest.fn(() => {
throw new Error('Oops, something went wrong')
})
const {data, error } = tx.execute(mockFunction);
throw new Error("Oops, something went wrong");
});
const { data, error } = tx.execute(mockFunction);

expect(data).toBeNull()
expect(error).toEqual(new Error('Oops, something went wrong'))
})
})
expect(data).toBeNull();
expect(error).toEqual(new Error("Oops, something went wrong"));
});
});
66 changes: 35 additions & 31 deletions __tests__/fetchHandler.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { TryX } from "../src";
import { tx } from "../src/lib/tx";

describe('fetchHandler', () => {
describe("fetchHandler", () => {
beforeEach(() => {
jest.useFakeTimers();
global.fetch = jest.fn();
Expand All @@ -12,41 +12,46 @@ describe('fetchHandler', () => {
jest.resetAllMocks();
});

it('should return JSON data on success', async () => {
const mockJson = { data: {message: 'Hello world'} };
it("should return JSON data on success", async () => {
const mockJson = { data: { message: "Hello world" } };
(fetch as jest.Mock).mockResolvedValue({
ok: true,
json: async () => mockJson,
});

const result = await tx.fetch('https://api.example.com/test');
const result = await tx.fetch("https://api.example.com/test");

expect(result.data).toEqual(mockJson);
});

it('should return an error on failure', async () => {
const mockError = new Error('Network error');
it("should return an error on failure", async () => {
const mockError = new Error("Network error");
(fetch as jest.Mock).mockRejectedValue(mockError);

const result = await tx.fetch('https://api.example.com/test');
const result = await tx.fetch("https://api.example.com/test");

expect(result.error).toEqual(mockError);
});

it('should handle HTTP errors', async () => {
it("should handle HTTP errors", async () => {
(fetch as jest.Mock).mockResolvedValue({
ok: false,
status: 404,
statusText: 'Not Found',
statusText: "Not Found",
});

const result = await tx.fetch('https://api.example.com/test');
const result = await tx.fetch("https://api.example.com/test");

expect(result.error).toEqual(new Error('HTTP error! Status: 404 Reason: Not Found'));
expect(result.error).toEqual(
new Error("HTTP error! Status: 404 Reason: Not Found"),
);
});

it('should abort and catch timeout errors', async () => {
const abortError = new DOMException('The user aborted a request.', 'AbortError');
it("should abort and catch timeout errors", async () => {
const abortError = new DOMException(
"The user aborted a request.",
"AbortError",
);

(global.fetch as jest.Mock).mockImplementation((_url, options) => {
const signal = options?.signal;
Expand All @@ -55,45 +60,44 @@ describe('fetchHandler', () => {
if (signal?.aborted) {
reject(abortError);
} else {
signal?.addEventListener?.('abort', () => {
signal?.addEventListener?.("abort", () => {
reject(abortError);
});
}
});
});

const txShort = new TryX({ timeout: 100 });
const resultPromise = txShort.fetch('https://api.example.com/slow');
const resultPromise = txShort.fetch("https://api.example.com/slow");

// Advance time to trigger the abort
jest.advanceTimersByTime(150);

const result = await resultPromise;

expect(result.error).toBeInstanceOf(DOMException);
expect(result.error!.name).toBe('AbortError');
expect(result.error!.name).toBe("AbortError");
});

it('should forward custom fetch options', async () => {
it("should forward custom fetch options", async () => {
(fetch as jest.Mock).mockResolvedValue({
ok: true,
json: async () => ({ foo: 'bar' }),
json: async () => ({ foo: "bar" }),
});
await tx.fetch('https://api.example.com', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ foo: 'bar' }),

await tx.fetch("https://api.example.com", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ foo: "bar" }),
});

expect(fetch).toHaveBeenCalledWith(
'https://api.example.com',
"https://api.example.com",
expect.objectContaining({
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ foo: 'bar' }),
})
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ foo: "bar" }),
}),
);
});

})
});
12 changes: 6 additions & 6 deletions __tests__/onError.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { TryX } from "../src";

describe('onError callback', () => {
describe("onError callback", () => {
beforeEach(() => {
jest.useFakeTimers();
global.fetch = jest.fn();
Expand All @@ -11,18 +11,18 @@ describe('onError callback', () => {
jest.resetAllMocks();
});

it('should call onError callback when an error occurs', async () => {
it("should call onError callback when an error occurs", async () => {
const mockCallback = jest.fn();
const tx = new TryX({
timeout: 5000,
onError: mockCallback
onError: mockCallback,
});

const mockError = new Error('Network error');
const mockError = new Error("Network error");
(fetch as jest.Mock).mockRejectedValue(mockError);

await tx.fetch('https://api.example.com/test');
await tx.fetch("https://api.example.com/test");

expect(mockCallback).toHaveBeenCalledWith(mockError);
});
});
});
Loading