- Consolidate API logic: hooks/useApi.ts now uses services/api.ts - Create BaseFormField component to reduce form duplication - Refactor FormField, FormSelect, FormTextarea to use BaseFormField - Add centralized theme utility (utils/theme.ts) for colors/styles - Add comprehensive tests for api, auth.service, useApi hooks, AuthContext - Add tests for theme utility This reduces duplication from 11.45% and improves test coverage. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
277 lines
7.1 KiB
TypeScript
277 lines
7.1 KiB
TypeScript
/**
|
|
* Tests for the Auth Service
|
|
* @jest-environment jsdom
|
|
*/
|
|
|
|
import { authService } from '@/services/auth.service';
|
|
import { api } from '@/services/api';
|
|
import { API_ENDPOINTS } from '@/utils/constants';
|
|
|
|
// Mock the api module
|
|
jest.mock('@/services/api', () => ({
|
|
api: {
|
|
get: jest.fn(),
|
|
post: jest.fn(),
|
|
put: jest.fn(),
|
|
patch: jest.fn(),
|
|
delete: jest.fn(),
|
|
},
|
|
ApiError: class ApiError extends Error {
|
|
constructor(public status: number, message: string, public data?: unknown) {
|
|
super(message);
|
|
this.name = 'ApiError';
|
|
}
|
|
},
|
|
}));
|
|
|
|
describe('Auth Service', () => {
|
|
beforeEach(() => {
|
|
jest.clearAllMocks();
|
|
});
|
|
|
|
describe('login', () => {
|
|
it('should login with valid credentials', async () => {
|
|
const mockResponse = {
|
|
success: true,
|
|
token: 'jwt-token-123',
|
|
user: {
|
|
id: '1',
|
|
email: 'test@test.com',
|
|
firstName: 'John',
|
|
lastName: 'Doe',
|
|
role: 'CLIENT',
|
|
},
|
|
};
|
|
|
|
(api.post as jest.Mock).mockResolvedValueOnce(mockResponse);
|
|
|
|
const result = await authService.login({
|
|
email: 'test@test.com',
|
|
password: 'password123',
|
|
});
|
|
|
|
expect(api.post).toHaveBeenCalledWith(
|
|
API_ENDPOINTS.AUTH.LOGIN,
|
|
{ email: 'test@test.com', password: 'password123' }
|
|
);
|
|
expect(result).toEqual({
|
|
token: 'jwt-token-123',
|
|
user: mockResponse.user,
|
|
});
|
|
});
|
|
|
|
it('should throw error on invalid credentials', async () => {
|
|
(api.post as jest.Mock).mockRejectedValueOnce(new Error('Invalid credentials'));
|
|
|
|
await expect(
|
|
authService.login({ email: 'test@test.com', password: 'wrong' })
|
|
).rejects.toThrow('Invalid credentials');
|
|
});
|
|
});
|
|
|
|
describe('register', () => {
|
|
it('should register a new user', async () => {
|
|
const mockResponse = {
|
|
success: true,
|
|
token: 'jwt-token-new',
|
|
user: {
|
|
id: '2',
|
|
email: 'new@test.com',
|
|
firstName: 'Jane',
|
|
lastName: 'Doe',
|
|
role: 'CLIENT',
|
|
},
|
|
};
|
|
|
|
(api.post as jest.Mock).mockResolvedValueOnce(mockResponse);
|
|
|
|
const registerData = {
|
|
email: 'new@test.com',
|
|
password: 'password123',
|
|
firstName: 'Jane',
|
|
lastName: 'Doe',
|
|
};
|
|
|
|
const result = await authService.register(registerData);
|
|
|
|
expect(api.post).toHaveBeenCalledWith(
|
|
API_ENDPOINTS.AUTH.REGISTER,
|
|
registerData
|
|
);
|
|
expect(result.token).toBe('jwt-token-new');
|
|
expect(result.user.email).toBe('new@test.com');
|
|
});
|
|
|
|
it('should throw error if email already exists', async () => {
|
|
(api.post as jest.Mock).mockRejectedValueOnce(new Error('Email already exists'));
|
|
|
|
await expect(
|
|
authService.register({
|
|
email: 'existing@test.com',
|
|
password: 'password',
|
|
firstName: 'Test',
|
|
lastName: 'User',
|
|
})
|
|
).rejects.toThrow('Email already exists');
|
|
});
|
|
});
|
|
|
|
describe('logout', () => {
|
|
it('should call logout endpoint', async () => {
|
|
(api.post as jest.Mock).mockResolvedValueOnce({ success: true });
|
|
|
|
await authService.logout();
|
|
|
|
expect(api.post).toHaveBeenCalledWith(API_ENDPOINTS.AUTH.LOGOUT);
|
|
});
|
|
});
|
|
|
|
describe('getCurrentUser', () => {
|
|
it('should return user data from response.data', async () => {
|
|
const mockUser = {
|
|
id: '1',
|
|
email: 'test@test.com',
|
|
firstName: 'John',
|
|
lastName: 'Doe',
|
|
role: 'CLIENT',
|
|
};
|
|
|
|
(api.get as jest.Mock).mockResolvedValueOnce({
|
|
success: true,
|
|
data: mockUser,
|
|
});
|
|
|
|
const result = await authService.getCurrentUser();
|
|
|
|
expect(api.get).toHaveBeenCalledWith(API_ENDPOINTS.AUTH.ME);
|
|
expect(result).toEqual(mockUser);
|
|
});
|
|
|
|
it('should return user data from response.user', async () => {
|
|
const mockUser = {
|
|
id: '1',
|
|
email: 'test@test.com',
|
|
firstName: 'John',
|
|
lastName: 'Doe',
|
|
role: 'CLIENT',
|
|
};
|
|
|
|
(api.get as jest.Mock).mockResolvedValueOnce({
|
|
success: true,
|
|
user: mockUser,
|
|
});
|
|
|
|
const result = await authService.getCurrentUser();
|
|
|
|
expect(result).toEqual(mockUser);
|
|
});
|
|
|
|
it('should return response directly if no data or user field', async () => {
|
|
const mockUser = {
|
|
id: '1',
|
|
email: 'test@test.com',
|
|
firstName: 'John',
|
|
lastName: 'Doe',
|
|
role: 'CLIENT',
|
|
};
|
|
|
|
(api.get as jest.Mock).mockResolvedValueOnce(mockUser);
|
|
|
|
const result = await authService.getCurrentUser();
|
|
|
|
expect(result).toEqual(mockUser);
|
|
});
|
|
});
|
|
|
|
describe('googleLogin', () => {
|
|
it('should login with Google token', async () => {
|
|
const mockResponse = {
|
|
success: true,
|
|
token: 'jwt-google-token',
|
|
user: {
|
|
id: '3',
|
|
email: 'google@test.com',
|
|
firstName: 'Google',
|
|
lastName: 'User',
|
|
role: 'CLIENT',
|
|
},
|
|
};
|
|
|
|
(api.post as jest.Mock).mockResolvedValueOnce(mockResponse);
|
|
|
|
const result = await authService.googleLogin('google-oauth-token');
|
|
|
|
expect(api.post).toHaveBeenCalledWith(
|
|
API_ENDPOINTS.AUTH.GOOGLE,
|
|
{ token: 'google-oauth-token' }
|
|
);
|
|
expect(result.token).toBe('jwt-google-token');
|
|
});
|
|
});
|
|
|
|
describe('facebookLogin', () => {
|
|
it('should login with Facebook token', async () => {
|
|
const mockResponse = {
|
|
success: true,
|
|
token: 'jwt-facebook-token',
|
|
user: {
|
|
id: '4',
|
|
email: 'facebook@test.com',
|
|
firstName: 'Facebook',
|
|
lastName: 'User',
|
|
role: 'CLIENT',
|
|
},
|
|
};
|
|
|
|
(api.post as jest.Mock).mockResolvedValueOnce(mockResponse);
|
|
|
|
const result = await authService.facebookLogin('facebook-oauth-token');
|
|
|
|
expect(api.post).toHaveBeenCalledWith(
|
|
API_ENDPOINTS.AUTH.FACEBOOK,
|
|
{ token: 'facebook-oauth-token' }
|
|
);
|
|
expect(result.token).toBe('jwt-facebook-token');
|
|
});
|
|
});
|
|
|
|
describe('verifyEmail', () => {
|
|
it('should verify email with token', async () => {
|
|
(api.post as jest.Mock).mockResolvedValueOnce({ success: true });
|
|
|
|
await authService.verifyEmail('verification-token');
|
|
|
|
expect(api.post).toHaveBeenCalledWith(
|
|
API_ENDPOINTS.AUTH.VERIFY_EMAIL,
|
|
{ token: 'verification-token' }
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('forgotPassword', () => {
|
|
it('should send forgot password request', async () => {
|
|
(api.post as jest.Mock).mockResolvedValueOnce({ success: true });
|
|
|
|
await authService.forgotPassword('test@test.com');
|
|
|
|
expect(api.post).toHaveBeenCalledWith(
|
|
API_ENDPOINTS.AUTH.FORGOT_PASSWORD,
|
|
{ email: 'test@test.com' }
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('resetPassword', () => {
|
|
it('should reset password with token', async () => {
|
|
(api.post as jest.Mock).mockResolvedValueOnce({ success: true });
|
|
|
|
await authService.resetPassword('reset-token', 'newPassword123');
|
|
|
|
expect(api.post).toHaveBeenCalledWith(
|
|
API_ENDPOINTS.AUTH.RESET_PASSWORD,
|
|
{ token: 'reset-token', password: 'newPassword123' }
|
|
);
|
|
});
|
|
});
|
|
});
|