import { describe, it, expect, vi, beforeEach } from 'vitest' import { useAPI } from '../use-api' import { useRuntimeConfig, useFetch, useNuxtApp } from 'nuxt/app' import { useUser } from '#lib/composables' import { getDeviceHeaders } from '#lib/utils' vi.mock('#lib/composables', () => ({ useUser: vi.fn(), })) vi.mock('#lib/utils', () => ({ getDeviceHeaders: vi.fn(), getTokenCookie: vi.fn(), setTokenCookie: vi.fn(), })) vi.mock('nuxt/app', () => ({ useRuntimeConfig: vi.fn(() => ({ public: { SITE_DOMAIN: 'https://your-site.com/', FLAT_CURRENCY_UNIT: 'VNĐ', MAIN_CREDIT_UNIT: 'K', AGENCY_CREDIT_UNIT: 'Đ', }, })), useNuxtApp: vi.fn().mockReturnValue({ $appCookies: vi.mocked({ token: { value: 'mock-token' }, }), }), useFetch: vi.fn(), createError: vi.fn(), })) describe('useAPI', () => { let mockConfig: any let mockUser: any let $appCookies: any beforeEach(() => { mockConfig = { public: { API_URL: 'https://api.example.com', }, } mockUser = { gpToken: { value: 'mock-gp-token' }, } $appCookies = useNuxtApp().$appCookies vi.mocked(useRuntimeConfig).mockReturnValue(mockConfig) vi.mocked(useUser).mockReturnValue(mockUser) }) it('should call useFetch with correct default options', async () => { const mockResponse = { data: 'mock-data' } vi.mocked(useFetch).mockResolvedValue(mockResponse) vi.mocked(getDeviceHeaders).mockReturnValue({ os: 'mock-os', device: 'mock-device', browser: 'mock-browser', }) const url = '/test-endpoint' const result = await useAPI(url) expect(useFetch).toHaveBeenCalledWith(url, { baseURL: mockConfig.public.API_URL, method: 'GET', retry: 3, signal: undefined, key: url, onRequest: expect.any(Function), onResponse: expect.any(Function), onResponseError: expect.any(Function), }) expect(result).toEqual(mockResponse) }) it('should set headers correctly in onRequest', async () => { const mockRequestOptions: any = {} const onRequest = (useFetch as any).mock.calls[0][1].onRequest vi.mocked(getDeviceHeaders).mockReturnValue({ os: 'mock-os', device: 'mock-device', browser: 'mock-browser', }) onRequest({ options: mockRequestOptions }) expect(mockRequestOptions.headers).toEqual({ Token: 'mock-token', Accept: 'application/json', 'Content-type': 'application/json', gpToken: 'mock-gp-token', os: 'mock-os', device: 'mock-device', browser: 'mock-browser', }) }) it('should handle response correctly in onResponse', async () => { const mockResponse = { _data: { user: { token: 'new-token' }, status: 'success', }, status: 200, } const onResponse = (useFetch as any).mock.calls[0][1].onResponse onResponse({ response: mockResponse }) expect($appCookies.token.value).toEqual('new-token') }) it('should throw error correctly if status is not 2xx in onResponse', async () => { const mockResponse = { _data: { user: { token: 'new-token' }, status: 'error', }, status: 400, } const onResponse = (useFetch as any).mock.calls[0][1].onResponse try { onResponse({ response: mockResponse }) } catch (e: any) { expect(e).toBe(undefined) } }) it('should throw error correctly in onResponseError', async () => { const mockResponse = { status: 400, statusText: 'Bad Request', _data: { errors: { username: ['is required'], password: ['is too short'], }, }, } const onResponseError = (useFetch as any).mock.calls[0][1].onResponseError try { onResponseError({ response: mockResponse }) } catch (e: any) { expect(e).toBe(undefined) } }) it('should call useFetch with no signal if no timeout is provided', async () => { const mockResponse = { data: 'mock-data' } vi.mocked(useFetch).mockResolvedValue(mockResponse) const url = '/test-endpoint' const result = await useAPI(url, { timeout: 0 }) // No timeout expect(useFetch).toHaveBeenCalledWith( url, expect.objectContaining({ signal: undefined, // No signal should be set }), ) expect(result).toEqual(mockResponse) }) it('should clear timeout after request is completed', async () => { vi.useFakeTimers() const clearTimeoutSpy = vi.spyOn(global, 'clearTimeout') const url = '/test-endpoint' const fetchPromise = useAPI(url, { timeout: 1000 }) vi.advanceTimersByTime(500) // Before timeout await fetchPromise // Assume request completes expect(clearTimeoutSpy).toHaveBeenCalled() vi.useRealTimers() }) })