/** * 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'); }); }); });