veza/apps/web/src/utils/timeoutHandler.test.ts

135 lines
3.8 KiB
TypeScript
Raw Normal View History

/**
* Tests for Timeout Handler Utility
* FE-TEST-004: Test timeout handler utility functions
*/
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import {
TIMEOUT_CONFIG,
TIMEOUT_MESSAGES,
createTimeoutPromise,
withTimeout,
getTimeoutForRequestType,
isTimeoutError,
getTimeoutMessage,
withRequestTimeout,
} from './timeoutHandler';
// Mock react-hot-toast
vi.mock('react-hot-toast', () => ({
default: {
loading: vi.fn(() => 'toast-id'),
dismiss: vi.fn(),
},
}));
describe('timeoutHandler utilities', () => {
beforeEach(() => {
vi.useFakeTimers();
vi.clearAllMocks();
});
afterEach(() => {
vi.useRealTimers();
});
describe('TIMEOUT_CONFIG', () => {
it('should have timeout configs', () => {
expect(TIMEOUT_CONFIG.default).toBe(10000);
expect(TIMEOUT_CONFIG.fast).toBe(5000);
expect(TIMEOUT_CONFIG.slow).toBe(30000);
});
});
describe('createTimeoutPromise', () => {
it('should reject after timeout', async () => {
const promise = createTimeoutPromise(1000, 'Timeout');
vi.advanceTimersByTime(1000);
await expect(promise).rejects.toThrow('Timeout');
});
});
describe('withTimeout', () => {
it('should resolve if promise completes before timeout', async () => {
const promise = Promise.resolve('success');
const result = withTimeout(promise, { timeout: 1000 });
vi.advanceTimersByTime(500);
await expect(result).resolves.toBe('success');
});
it('should reject on timeout', async () => {
const promise = new Promise((resolve) => {
setTimeout(() => resolve('success'), 2000);
});
const result = withTimeout(promise, { timeout: 1000 });
vi.advanceTimersByTime(1000);
await expect(result).rejects.toThrow();
});
it('should call onTimeout callback', async () => {
const onTimeout = vi.fn();
const promise = new Promise((resolve) => {
setTimeout(() => resolve('success'), 2000);
});
const result = withTimeout(promise, { timeout: 1000, onTimeout });
vi.advanceTimersByTime(1000);
try {
await result;
} catch {
// Expected
}
expect(onTimeout).toHaveBeenCalled();
});
});
describe('getTimeoutForRequestType', () => {
it('should return timeout for request type', () => {
expect(getTimeoutForRequestType('fast')).toBe(5000);
expect(getTimeoutForRequestType('normal')).toBe(10000);
expect(getTimeoutForRequestType('slow')).toBe(30000);
});
it('should default to normal', () => {
expect(getTimeoutForRequestType()).toBe(10000);
});
});
describe('isTimeoutError', () => {
it('should return true for timeout error', () => {
const error = new Error('Request timeout');
expect(isTimeoutError(error)).toBe(true);
});
it('should return true for timeout in message', () => {
const error = new Error('Request expired');
expect(isTimeoutError(error)).toBe(true);
});
it('should return true for ECONNABORTED', () => {
expect(isTimeoutError({ code: 'ECONNABORTED' })).toBe(true);
});
it('should return false for other errors', () => {
const error = new Error('Other error');
expect(isTimeoutError(error)).toBe(false);
});
});
describe('getTimeoutMessage', () => {
it('should return message for request type', () => {
expect(getTimeoutMessage('fast')).toBeTruthy();
expect(getTimeoutMessage('normal')).toBeTruthy();
expect(getTimeoutMessage('slow')).toBeTruthy();
});
});
describe('withRequestTimeout', () => {
it('should wrap API call with timeout', async () => {
const apiCall = vi.fn().mockResolvedValue('success');
const result = withRequestTimeout(apiCall, 'normal');
vi.advanceTimersByTime(500);
await expect(result).resolves.toBe('success');
});
});
});