163 lines
4.5 KiB
TypeScript
163 lines
4.5 KiB
TypeScript
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
import { renderHook, waitFor, act } from '@testing-library/react';
|
|
import { useUsernameAvailability } from './useUsernameAvailability';
|
|
import { checkUsernameAvailability } from '../services/authService';
|
|
|
|
vi.mock('../services/authService', () => ({
|
|
checkUsernameAvailability: vi.fn(),
|
|
}));
|
|
|
|
describe('useUsernameAvailability', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
it('should return null when username is empty', () => {
|
|
const { result } = renderHook(() => useUsernameAvailability(''));
|
|
|
|
expect(result.current.available).toBeNull();
|
|
expect(result.current.checking).toBe(false);
|
|
});
|
|
|
|
it('should return null when username is too short', () => {
|
|
const { result } = renderHook(() => useUsernameAvailability('ab'));
|
|
|
|
expect(result.current.available).toBeNull();
|
|
expect(result.current.checking).toBe(false);
|
|
});
|
|
|
|
it('should check availability when username is long enough', async () => {
|
|
vi.mocked(checkUsernameAvailability).mockResolvedValue(true);
|
|
|
|
const { result } = renderHook(() => useUsernameAvailability('testuser'));
|
|
|
|
// Wait for debounce (500ms) and API call
|
|
await waitFor(
|
|
() => {
|
|
expect(checkUsernameAvailability).toHaveBeenCalledWith('testuser');
|
|
},
|
|
{ timeout: 2000 },
|
|
);
|
|
|
|
await waitFor(
|
|
() => {
|
|
expect(result.current.available).toBe(true);
|
|
expect(result.current.checking).toBe(false);
|
|
},
|
|
{ timeout: 2000 },
|
|
);
|
|
});
|
|
|
|
it('should return false when username is not available', async () => {
|
|
vi.mocked(checkUsernameAvailability).mockResolvedValue(false);
|
|
|
|
const { result } = renderHook(() => useUsernameAvailability('takenuser'));
|
|
|
|
// Wait for debounce (500ms) and API call
|
|
await waitFor(
|
|
() => {
|
|
expect(checkUsernameAvailability).toHaveBeenCalledWith('takenuser');
|
|
},
|
|
{ timeout: 2000 },
|
|
);
|
|
|
|
await waitFor(
|
|
() => {
|
|
expect(result.current.available).toBe(false);
|
|
expect(result.current.checking).toBe(false);
|
|
},
|
|
{ timeout: 2000 },
|
|
);
|
|
});
|
|
|
|
it('should debounce the API call', async () => {
|
|
vi.mocked(checkUsernameAvailability).mockResolvedValue(true);
|
|
|
|
const { result, rerender } = renderHook(
|
|
({ username }) => useUsernameAvailability(username),
|
|
{ initialProps: { username: 'test' } },
|
|
);
|
|
|
|
// Change username multiple times quickly
|
|
await act(async () => {
|
|
rerender({ username: 'test1' });
|
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
rerender({ username: 'test12' });
|
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
rerender({ username: 'test123' });
|
|
});
|
|
|
|
// Wait for debounce to complete and API call
|
|
await waitFor(
|
|
() => {
|
|
expect(checkUsernameAvailability).toHaveBeenCalled();
|
|
},
|
|
{ timeout: 2000 },
|
|
);
|
|
|
|
await waitFor(
|
|
() => {
|
|
expect(result.current.available).toBe(true);
|
|
},
|
|
{ timeout: 2000 },
|
|
);
|
|
|
|
// Should only be called once for the last value
|
|
expect(checkUsernameAvailability).toHaveBeenCalledTimes(1);
|
|
expect(checkUsernameAvailability).toHaveBeenCalledWith('test123');
|
|
});
|
|
|
|
it('should handle errors gracefully', async () => {
|
|
vi.mocked(checkUsernameAvailability).mockRejectedValue(
|
|
new Error('API Error'),
|
|
);
|
|
|
|
const { result } = renderHook(() => useUsernameAvailability('testuser'));
|
|
|
|
// Wait for debounce (500ms) and API call
|
|
await waitFor(
|
|
() => {
|
|
expect(checkUsernameAvailability).toHaveBeenCalledWith('testuser');
|
|
},
|
|
{ timeout: 2000 },
|
|
);
|
|
|
|
// Wait for promise to reject and state to update
|
|
await waitFor(
|
|
() => {
|
|
expect(result.current.available).toBeNull();
|
|
expect(result.current.checking).toBe(false);
|
|
},
|
|
{ timeout: 2000 },
|
|
);
|
|
});
|
|
|
|
it('should reset when username changes to empty', async () => {
|
|
vi.mocked(checkUsernameAvailability).mockResolvedValue(true);
|
|
|
|
const { result, rerender } = renderHook(
|
|
({ username }) => useUsernameAvailability(username),
|
|
{ initialProps: { username: 'testuser' } },
|
|
);
|
|
|
|
// Wait for debounce and API call
|
|
await waitFor(
|
|
() => {
|
|
expect(result.current.available).toBe(true);
|
|
},
|
|
{ timeout: 2000 },
|
|
);
|
|
|
|
// Change to empty
|
|
await act(async () => {
|
|
rerender({ username: '' });
|
|
});
|
|
|
|
expect(result.current.available).toBeNull();
|
|
expect(result.current.checking).toBe(false);
|
|
});
|
|
});
|