251211:1314 Frontend: reeactor Admin panel
This commit is contained in:
270
frontend/hooks/__tests__/use-correspondence.test.ts
Normal file
270
frontend/hooks/__tests__/use-correspondence.test.ts
Normal file
@@ -0,0 +1,270 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { renderHook, waitFor, act } from '@testing-library/react';
|
||||
import { createTestQueryClient } from '@/lib/test-utils';
|
||||
import {
|
||||
useCorrespondences,
|
||||
useCorrespondence,
|
||||
useCreateCorrespondence,
|
||||
useUpdateCorrespondence,
|
||||
useDeleteCorrespondence,
|
||||
useSubmitCorrespondence,
|
||||
useProcessWorkflow,
|
||||
correspondenceKeys,
|
||||
} from '../use-correspondence';
|
||||
import { correspondenceService } from '@/lib/services/correspondence.service';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
// Mock the service
|
||||
vi.mock('@/lib/services/correspondence.service', () => ({
|
||||
correspondenceService: {
|
||||
getAll: vi.fn(),
|
||||
getById: vi.fn(),
|
||||
create: vi.fn(),
|
||||
update: vi.fn(),
|
||||
delete: vi.fn(),
|
||||
submit: vi.fn(),
|
||||
processWorkflow: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('use-correspondence hooks', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('correspondenceKeys', () => {
|
||||
it('should generate correct cache keys', () => {
|
||||
expect(correspondenceKeys.all).toEqual(['correspondences']);
|
||||
expect(correspondenceKeys.lists()).toEqual(['correspondences', 'list']);
|
||||
expect(correspondenceKeys.list({ projectId: 1 })).toEqual([
|
||||
'correspondences',
|
||||
'list',
|
||||
{ projectId: 1 },
|
||||
]);
|
||||
expect(correspondenceKeys.details()).toEqual(['correspondences', 'detail']);
|
||||
expect(correspondenceKeys.detail(1)).toEqual(['correspondences', 'detail', 1]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('useCorrespondences', () => {
|
||||
it('should fetch correspondences successfully', async () => {
|
||||
const mockData = {
|
||||
data: [
|
||||
{ id: 1, title: 'Test Correspondence 1' },
|
||||
{ id: 2, title: 'Test Correspondence 2' },
|
||||
],
|
||||
meta: { total: 2, page: 1, limit: 10 },
|
||||
};
|
||||
|
||||
vi.mocked(correspondenceService.getAll).mockResolvedValue(mockData);
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useCorrespondences({ projectId: 1 }), { wrapper });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.isSuccess).toBe(true);
|
||||
});
|
||||
|
||||
expect(result.current.data).toEqual(mockData);
|
||||
expect(correspondenceService.getAll).toHaveBeenCalledWith({ projectId: 1 });
|
||||
});
|
||||
|
||||
it('should handle error state', async () => {
|
||||
const mockError = new Error('API Error');
|
||||
vi.mocked(correspondenceService.getAll).mockRejectedValue(mockError);
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useCorrespondences({ projectId: 1 }), { wrapper });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.isError).toBe(true);
|
||||
});
|
||||
|
||||
expect(result.current.error).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('useCorrespondence', () => {
|
||||
it('should fetch single correspondence by id', async () => {
|
||||
const mockData = { id: 1, title: 'Test Correspondence' };
|
||||
vi.mocked(correspondenceService.getById).mockResolvedValue(mockData);
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useCorrespondence(1), { wrapper });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.isSuccess).toBe(true);
|
||||
});
|
||||
|
||||
expect(result.current.data).toEqual(mockData);
|
||||
expect(correspondenceService.getById).toHaveBeenCalledWith(1);
|
||||
});
|
||||
|
||||
it('should not fetch when id is falsy', () => {
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useCorrespondence(0), { wrapper });
|
||||
|
||||
expect(result.current.isFetching).toBe(false);
|
||||
expect(correspondenceService.getById).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('useCreateCorrespondence', () => {
|
||||
it('should create correspondence and show success toast', async () => {
|
||||
const mockResponse = { id: 1, title: 'New Correspondence' };
|
||||
vi.mocked(correspondenceService.create).mockResolvedValue(mockResponse);
|
||||
|
||||
const { wrapper, queryClient } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useCreateCorrespondence(), { wrapper });
|
||||
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync({
|
||||
title: 'New Correspondence',
|
||||
projectId: 1,
|
||||
correspondenceTypeId: 1,
|
||||
});
|
||||
});
|
||||
|
||||
expect(correspondenceService.create).toHaveBeenCalledWith({
|
||||
title: 'New Correspondence',
|
||||
projectId: 1,
|
||||
correspondenceTypeId: 1,
|
||||
});
|
||||
expect(toast.success).toHaveBeenCalledWith('Correspondence created successfully');
|
||||
});
|
||||
|
||||
it('should show error toast on failure', async () => {
|
||||
const mockError = {
|
||||
message: 'API Error',
|
||||
response: { data: { message: 'Validation failed' } },
|
||||
};
|
||||
vi.mocked(correspondenceService.create).mockRejectedValue(mockError);
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useCreateCorrespondence(), { wrapper });
|
||||
|
||||
await act(async () => {
|
||||
try {
|
||||
await result.current.mutateAsync({
|
||||
title: '',
|
||||
projectId: 1,
|
||||
correspondenceTypeId: 1,
|
||||
});
|
||||
} catch {
|
||||
// Expected to throw
|
||||
}
|
||||
});
|
||||
|
||||
expect(toast.error).toHaveBeenCalledWith('Failed to create correspondence', {
|
||||
description: 'Validation failed',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('useUpdateCorrespondence', () => {
|
||||
it('should update correspondence and invalidate cache', async () => {
|
||||
const mockResponse = { id: 1, title: 'Updated Correspondence' };
|
||||
vi.mocked(correspondenceService.update).mockResolvedValue(mockResponse);
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useUpdateCorrespondence(), { wrapper });
|
||||
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync({
|
||||
id: 1,
|
||||
data: { title: 'Updated Correspondence' },
|
||||
});
|
||||
});
|
||||
|
||||
expect(correspondenceService.update).toHaveBeenCalledWith(1, {
|
||||
title: 'Updated Correspondence',
|
||||
});
|
||||
expect(toast.success).toHaveBeenCalledWith('Correspondence updated successfully');
|
||||
});
|
||||
});
|
||||
|
||||
describe('useDeleteCorrespondence', () => {
|
||||
it('should delete correspondence and show success toast', async () => {
|
||||
vi.mocked(correspondenceService.delete).mockResolvedValue({});
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useDeleteCorrespondence(), { wrapper });
|
||||
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync(1);
|
||||
});
|
||||
|
||||
expect(correspondenceService.delete).toHaveBeenCalledWith(1);
|
||||
expect(toast.success).toHaveBeenCalledWith('Correspondence deleted successfully');
|
||||
});
|
||||
});
|
||||
|
||||
describe('useSubmitCorrespondence', () => {
|
||||
it('should submit correspondence for workflow', async () => {
|
||||
const mockResponse = { id: 1, status: 'submitted' };
|
||||
vi.mocked(correspondenceService.submit).mockResolvedValue(mockResponse);
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useSubmitCorrespondence(), { wrapper });
|
||||
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync({
|
||||
id: 1,
|
||||
data: { recipientIds: [2, 3] },
|
||||
});
|
||||
});
|
||||
|
||||
expect(correspondenceService.submit).toHaveBeenCalledWith(1, { recipientIds: [2, 3] });
|
||||
expect(toast.success).toHaveBeenCalledWith('Correspondence submitted successfully');
|
||||
});
|
||||
});
|
||||
|
||||
describe('useProcessWorkflow', () => {
|
||||
it('should process workflow action', async () => {
|
||||
const mockResponse = { id: 1, status: 'approved' };
|
||||
vi.mocked(correspondenceService.processWorkflow).mockResolvedValue(mockResponse);
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useProcessWorkflow(), { wrapper });
|
||||
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync({
|
||||
id: 1,
|
||||
data: { action: 'approve', comment: 'LGTM' },
|
||||
});
|
||||
});
|
||||
|
||||
expect(correspondenceService.processWorkflow).toHaveBeenCalledWith(1, {
|
||||
action: 'approve',
|
||||
comment: 'LGTM',
|
||||
});
|
||||
expect(toast.success).toHaveBeenCalledWith('Action completed successfully');
|
||||
});
|
||||
|
||||
it('should handle workflow action error', async () => {
|
||||
const mockError = {
|
||||
message: 'Error',
|
||||
response: { data: { message: 'Permission denied' } },
|
||||
};
|
||||
vi.mocked(correspondenceService.processWorkflow).mockRejectedValue(mockError);
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useProcessWorkflow(), { wrapper });
|
||||
|
||||
await act(async () => {
|
||||
try {
|
||||
await result.current.mutateAsync({
|
||||
id: 1,
|
||||
data: { action: 'approve' },
|
||||
});
|
||||
} catch {
|
||||
// Expected to throw
|
||||
}
|
||||
});
|
||||
|
||||
expect(toast.error).toHaveBeenCalledWith('Failed to process action', {
|
||||
description: 'Permission denied',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
212
frontend/hooks/__tests__/use-drawing.test.ts
Normal file
212
frontend/hooks/__tests__/use-drawing.test.ts
Normal file
@@ -0,0 +1,212 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { renderHook, waitFor, act } from '@testing-library/react';
|
||||
import { createTestQueryClient } from '@/lib/test-utils';
|
||||
import {
|
||||
useDrawings,
|
||||
useDrawing,
|
||||
useCreateDrawing,
|
||||
drawingKeys,
|
||||
} from '../use-drawing';
|
||||
import { contractDrawingService } from '@/lib/services/contract-drawing.service';
|
||||
import { shopDrawingService } from '@/lib/services/shop-drawing.service';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
// Mock services
|
||||
vi.mock('@/lib/services/contract-drawing.service', () => ({
|
||||
contractDrawingService: {
|
||||
getAll: vi.fn(),
|
||||
getById: vi.fn(),
|
||||
create: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('@/lib/services/shop-drawing.service', () => ({
|
||||
shopDrawingService: {
|
||||
getAll: vi.fn(),
|
||||
getById: vi.fn(),
|
||||
create: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('use-drawing hooks', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('drawingKeys', () => {
|
||||
it('should generate correct cache keys', () => {
|
||||
expect(drawingKeys.all).toEqual(['drawings']);
|
||||
expect(drawingKeys.lists()).toEqual(['drawings', 'list']);
|
||||
expect(drawingKeys.list('CONTRACT', { projectId: 1 })).toEqual([
|
||||
'drawings',
|
||||
'list',
|
||||
'CONTRACT',
|
||||
{ projectId: 1 },
|
||||
]);
|
||||
expect(drawingKeys.detail('SHOP', 1)).toEqual(['drawings', 'detail', 'SHOP', 1]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('useDrawings', () => {
|
||||
it('should fetch CONTRACT drawings successfully', async () => {
|
||||
const mockData = {
|
||||
data: [
|
||||
{ id: 1, drawingNumber: 'CD-001' },
|
||||
{ id: 2, drawingNumber: 'CD-002' },
|
||||
],
|
||||
meta: { total: 2, page: 1, limit: 10 },
|
||||
};
|
||||
|
||||
vi.mocked(contractDrawingService.getAll).mockResolvedValue(mockData);
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useDrawings('CONTRACT', { projectId: 1 }), { wrapper });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.isSuccess).toBe(true);
|
||||
});
|
||||
|
||||
expect(result.current.data).toEqual(mockData);
|
||||
expect(contractDrawingService.getAll).toHaveBeenCalledWith({ projectId: 1 });
|
||||
expect(shopDrawingService.getAll).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should fetch SHOP drawings successfully', async () => {
|
||||
const mockData = {
|
||||
data: [{ id: 1, drawingNumber: 'SD-001' }],
|
||||
meta: { total: 1, page: 1, limit: 10 },
|
||||
};
|
||||
|
||||
vi.mocked(shopDrawingService.getAll).mockResolvedValue(mockData);
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useDrawings('SHOP', { projectId: 1 }), { wrapper });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.isSuccess).toBe(true);
|
||||
});
|
||||
|
||||
expect(result.current.data).toEqual(mockData);
|
||||
expect(shopDrawingService.getAll).toHaveBeenCalledWith({ projectId: 1 });
|
||||
expect(contractDrawingService.getAll).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should handle error state', async () => {
|
||||
const mockError = new Error('API Error');
|
||||
vi.mocked(contractDrawingService.getAll).mockRejectedValue(mockError);
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useDrawings('CONTRACT', { projectId: 1 }), { wrapper });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.isError).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('useDrawing', () => {
|
||||
it('should fetch single CONTRACT drawing by id', async () => {
|
||||
const mockData = { id: 1, drawingNumber: 'CD-001' };
|
||||
vi.mocked(contractDrawingService.getById).mockResolvedValue(mockData);
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useDrawing('CONTRACT', 1), { wrapper });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.isSuccess).toBe(true);
|
||||
});
|
||||
|
||||
expect(result.current.data).toEqual(mockData);
|
||||
expect(contractDrawingService.getById).toHaveBeenCalledWith(1);
|
||||
});
|
||||
|
||||
it('should fetch single SHOP drawing by id', async () => {
|
||||
const mockData = { id: 1, drawingNumber: 'SD-001' };
|
||||
vi.mocked(shopDrawingService.getById).mockResolvedValue(mockData);
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useDrawing('SHOP', 1), { wrapper });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.isSuccess).toBe(true);
|
||||
});
|
||||
|
||||
expect(result.current.data).toEqual(mockData);
|
||||
expect(shopDrawingService.getById).toHaveBeenCalledWith(1);
|
||||
});
|
||||
|
||||
it('should not fetch when id is falsy', () => {
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useDrawing('CONTRACT', 0), { wrapper });
|
||||
|
||||
expect(result.current.isFetching).toBe(false);
|
||||
expect(contractDrawingService.getById).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('useCreateDrawing', () => {
|
||||
it('should create CONTRACT drawing and show success toast', async () => {
|
||||
const mockResponse = { id: 1, drawingNumber: 'CD-001' };
|
||||
vi.mocked(contractDrawingService.create).mockResolvedValue(mockResponse);
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useCreateDrawing('CONTRACT'), { wrapper });
|
||||
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync({
|
||||
projectId: 1,
|
||||
drawingNumber: 'CD-001',
|
||||
title: 'Test Drawing',
|
||||
});
|
||||
});
|
||||
|
||||
expect(contractDrawingService.create).toHaveBeenCalled();
|
||||
expect(toast.success).toHaveBeenCalledWith('Contract Drawing uploaded successfully');
|
||||
});
|
||||
|
||||
it('should create SHOP drawing and show success toast', async () => {
|
||||
const mockResponse = { id: 1, drawingNumber: 'SD-001' };
|
||||
vi.mocked(shopDrawingService.create).mockResolvedValue(mockResponse);
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useCreateDrawing('SHOP'), { wrapper });
|
||||
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync({
|
||||
contractDrawingId: 1,
|
||||
title: 'Shop Drawing',
|
||||
});
|
||||
});
|
||||
|
||||
expect(shopDrawingService.create).toHaveBeenCalled();
|
||||
expect(toast.success).toHaveBeenCalledWith('Shop Drawing uploaded successfully');
|
||||
});
|
||||
|
||||
it('should show error toast on failure', async () => {
|
||||
const mockError = {
|
||||
message: 'API Error',
|
||||
response: { data: { message: 'File too large' } },
|
||||
};
|
||||
vi.mocked(contractDrawingService.create).mockRejectedValue(mockError);
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useCreateDrawing('CONTRACT'), { wrapper });
|
||||
|
||||
await act(async () => {
|
||||
try {
|
||||
await result.current.mutateAsync({
|
||||
projectId: 1,
|
||||
drawingNumber: 'CD-001',
|
||||
title: 'Test',
|
||||
});
|
||||
} catch {
|
||||
// Expected to throw
|
||||
}
|
||||
});
|
||||
|
||||
expect(toast.error).toHaveBeenCalledWith('Failed to upload drawing', {
|
||||
description: 'File too large',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
223
frontend/hooks/__tests__/use-projects.test.ts
Normal file
223
frontend/hooks/__tests__/use-projects.test.ts
Normal file
@@ -0,0 +1,223 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { renderHook, waitFor, act } from '@testing-library/react';
|
||||
import { createTestQueryClient } from '@/lib/test-utils';
|
||||
import {
|
||||
useProjects,
|
||||
useCreateProject,
|
||||
useUpdateProject,
|
||||
useDeleteProject,
|
||||
projectKeys,
|
||||
} from '../use-projects';
|
||||
import { projectService } from '@/lib/services/project.service';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
// Mock the service
|
||||
vi.mock('@/lib/services/project.service', () => ({
|
||||
projectService: {
|
||||
getAll: vi.fn(),
|
||||
getById: vi.fn(),
|
||||
create: vi.fn(),
|
||||
update: vi.fn(),
|
||||
delete: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('use-projects hooks', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('projectKeys', () => {
|
||||
it('should generate correct cache keys', () => {
|
||||
expect(projectKeys.all).toEqual(['projects']);
|
||||
expect(projectKeys.list({ search: 'test' })).toEqual([
|
||||
'projects',
|
||||
'list',
|
||||
{ search: 'test' },
|
||||
]);
|
||||
expect(projectKeys.detail(1)).toEqual(['projects', 'detail', 1]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('useProjects', () => {
|
||||
it('should fetch projects successfully', async () => {
|
||||
const mockData = [
|
||||
{ id: 1, name: 'Project Alpha', code: 'P-001' },
|
||||
{ id: 2, name: 'Project Beta', code: 'P-002' },
|
||||
];
|
||||
|
||||
vi.mocked(projectService.getAll).mockResolvedValue(mockData);
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useProjects({ search: 'test' }), { wrapper });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.isSuccess).toBe(true);
|
||||
});
|
||||
|
||||
expect(result.current.data).toEqual(mockData);
|
||||
expect(projectService.getAll).toHaveBeenCalledWith({ search: 'test' });
|
||||
});
|
||||
|
||||
it('should fetch projects without params', async () => {
|
||||
const mockData = [{ id: 1, name: 'Project Alpha' }];
|
||||
vi.mocked(projectService.getAll).mockResolvedValue(mockData);
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useProjects(), { wrapper });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.isSuccess).toBe(true);
|
||||
});
|
||||
|
||||
expect(projectService.getAll).toHaveBeenCalledWith(undefined);
|
||||
});
|
||||
|
||||
it('should handle error state', async () => {
|
||||
vi.mocked(projectService.getAll).mockRejectedValue(new Error('API Error'));
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useProjects({}), { wrapper });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.isError).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('useCreateProject', () => {
|
||||
it('should create project and show success toast', async () => {
|
||||
const mockResponse = { id: 1, name: 'New Project' };
|
||||
vi.mocked(projectService.create).mockResolvedValue(mockResponse);
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useCreateProject(), { wrapper });
|
||||
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync({
|
||||
name: 'New Project',
|
||||
code: 'P-003',
|
||||
contractId: 1,
|
||||
});
|
||||
});
|
||||
|
||||
expect(projectService.create).toHaveBeenCalledWith({
|
||||
name: 'New Project',
|
||||
code: 'P-003',
|
||||
contractId: 1,
|
||||
});
|
||||
expect(toast.success).toHaveBeenCalledWith('Project created successfully');
|
||||
});
|
||||
|
||||
it('should show error toast on failure', async () => {
|
||||
const mockError = {
|
||||
message: 'Error',
|
||||
response: { data: { message: 'Duplicate code' } },
|
||||
};
|
||||
vi.mocked(projectService.create).mockRejectedValue(mockError);
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useCreateProject(), { wrapper });
|
||||
|
||||
await act(async () => {
|
||||
try {
|
||||
await result.current.mutateAsync({
|
||||
name: 'Test',
|
||||
code: 'P-001',
|
||||
});
|
||||
} catch {
|
||||
// Expected
|
||||
}
|
||||
});
|
||||
|
||||
expect(toast.error).toHaveBeenCalledWith('Failed to create project', {
|
||||
description: 'Duplicate code',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('useUpdateProject', () => {
|
||||
it('should update project and show success toast', async () => {
|
||||
const mockResponse = { id: 1, name: 'Updated Project' };
|
||||
vi.mocked(projectService.update).mockResolvedValue(mockResponse);
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useUpdateProject(), { wrapper });
|
||||
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync({
|
||||
id: 1,
|
||||
data: { name: 'Updated Project' },
|
||||
});
|
||||
});
|
||||
|
||||
expect(projectService.update).toHaveBeenCalledWith(1, { name: 'Updated Project' });
|
||||
expect(toast.success).toHaveBeenCalledWith('Project updated successfully');
|
||||
});
|
||||
|
||||
it('should show error toast on failure', async () => {
|
||||
const mockError = {
|
||||
message: 'Error',
|
||||
response: { data: { message: 'Not found' } },
|
||||
};
|
||||
vi.mocked(projectService.update).mockRejectedValue(mockError);
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useUpdateProject(), { wrapper });
|
||||
|
||||
await act(async () => {
|
||||
try {
|
||||
await result.current.mutateAsync({
|
||||
id: 999,
|
||||
data: { name: 'Test' },
|
||||
});
|
||||
} catch {
|
||||
// Expected
|
||||
}
|
||||
});
|
||||
|
||||
expect(toast.error).toHaveBeenCalledWith('Failed to update project', {
|
||||
description: 'Not found',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('useDeleteProject', () => {
|
||||
it('should delete project and show success toast', async () => {
|
||||
vi.mocked(projectService.delete).mockResolvedValue({});
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useDeleteProject(), { wrapper });
|
||||
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync(1);
|
||||
});
|
||||
|
||||
expect(projectService.delete).toHaveBeenCalledWith(1);
|
||||
expect(toast.success).toHaveBeenCalledWith('Project deleted successfully');
|
||||
});
|
||||
|
||||
it('should show error toast on delete failure', async () => {
|
||||
const mockError = {
|
||||
message: 'Error',
|
||||
response: { data: { message: 'Cannot delete' } },
|
||||
};
|
||||
vi.mocked(projectService.delete).mockRejectedValue(mockError);
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useDeleteProject(), { wrapper });
|
||||
|
||||
await act(async () => {
|
||||
try {
|
||||
await result.current.mutateAsync(1);
|
||||
} catch {
|
||||
// Expected
|
||||
}
|
||||
});
|
||||
|
||||
expect(toast.error).toHaveBeenCalledWith('Failed to delete project', {
|
||||
description: 'Cannot delete',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
215
frontend/hooks/__tests__/use-rfa.test.ts
Normal file
215
frontend/hooks/__tests__/use-rfa.test.ts
Normal file
@@ -0,0 +1,215 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { renderHook, waitFor, act } from '@testing-library/react';
|
||||
import { createTestQueryClient } from '@/lib/test-utils';
|
||||
import {
|
||||
useRFAs,
|
||||
useRFA,
|
||||
useCreateRFA,
|
||||
useUpdateRFA,
|
||||
useProcessRFA,
|
||||
rfaKeys,
|
||||
} from '../use-rfa';
|
||||
import { rfaService } from '@/lib/services/rfa.service';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
// Mock service
|
||||
vi.mock('@/lib/services/rfa.service', () => ({
|
||||
rfaService: {
|
||||
getAll: vi.fn(),
|
||||
getById: vi.fn(),
|
||||
create: vi.fn(),
|
||||
update: vi.fn(),
|
||||
processWorkflow: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('use-rfa hooks', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('rfaKeys', () => {
|
||||
it('should generate correct cache keys', () => {
|
||||
expect(rfaKeys.all).toEqual(['rfas']);
|
||||
expect(rfaKeys.lists()).toEqual(['rfas', 'list']);
|
||||
expect(rfaKeys.list({ projectId: 1 })).toEqual(['rfas', 'list', { projectId: 1 }]);
|
||||
expect(rfaKeys.details()).toEqual(['rfas', 'detail']);
|
||||
expect(rfaKeys.detail(1)).toEqual(['rfas', 'detail', 1]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('useRFAs', () => {
|
||||
it('should fetch RFAs successfully', async () => {
|
||||
const mockData = {
|
||||
data: [
|
||||
{ id: 1, rfaNumber: 'RFA-001' },
|
||||
{ id: 2, rfaNumber: 'RFA-002' },
|
||||
],
|
||||
meta: { total: 2, page: 1, limit: 10 },
|
||||
};
|
||||
|
||||
vi.mocked(rfaService.getAll).mockResolvedValue(mockData);
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useRFAs({ projectId: 1 }), { wrapper });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.isSuccess).toBe(true);
|
||||
});
|
||||
|
||||
expect(result.current.data).toEqual(mockData);
|
||||
expect(rfaService.getAll).toHaveBeenCalledWith({ projectId: 1 });
|
||||
});
|
||||
|
||||
it('should handle error state', async () => {
|
||||
vi.mocked(rfaService.getAll).mockRejectedValue(new Error('API Error'));
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useRFAs({ projectId: 1 }), { wrapper });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.isError).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('useRFA', () => {
|
||||
it('should fetch single RFA by id', async () => {
|
||||
const mockData = { id: 1, rfaNumber: 'RFA-001', status: 'pending' };
|
||||
vi.mocked(rfaService.getById).mockResolvedValue(mockData);
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useRFA(1), { wrapper });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.isSuccess).toBe(true);
|
||||
});
|
||||
|
||||
expect(result.current.data).toEqual(mockData);
|
||||
expect(rfaService.getById).toHaveBeenCalledWith(1);
|
||||
});
|
||||
|
||||
it('should not fetch when id is falsy', () => {
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useRFA(0), { wrapper });
|
||||
|
||||
expect(result.current.isFetching).toBe(false);
|
||||
expect(rfaService.getById).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('useCreateRFA', () => {
|
||||
it('should create RFA and show success toast', async () => {
|
||||
const mockResponse = { id: 1, rfaNumber: 'RFA-001' };
|
||||
vi.mocked(rfaService.create).mockResolvedValue(mockResponse);
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useCreateRFA(), { wrapper });
|
||||
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync({
|
||||
projectId: 1,
|
||||
subject: 'Test RFA',
|
||||
});
|
||||
});
|
||||
|
||||
expect(rfaService.create).toHaveBeenCalled();
|
||||
expect(toast.success).toHaveBeenCalledWith('RFA created successfully');
|
||||
});
|
||||
|
||||
it('should show error toast on failure', async () => {
|
||||
const mockError = {
|
||||
message: 'Error',
|
||||
response: { data: { message: 'Validation failed' } },
|
||||
};
|
||||
vi.mocked(rfaService.create).mockRejectedValue(mockError);
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useCreateRFA(), { wrapper });
|
||||
|
||||
await act(async () => {
|
||||
try {
|
||||
await result.current.mutateAsync({
|
||||
projectId: 1,
|
||||
subject: '',
|
||||
});
|
||||
} catch {
|
||||
// Expected
|
||||
}
|
||||
});
|
||||
|
||||
expect(toast.error).toHaveBeenCalledWith('Failed to create RFA', {
|
||||
description: 'Validation failed',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('useUpdateRFA', () => {
|
||||
it('should update RFA and invalidate cache', async () => {
|
||||
const mockResponse = { id: 1, subject: 'Updated RFA' };
|
||||
vi.mocked(rfaService.update).mockResolvedValue(mockResponse);
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useUpdateRFA(), { wrapper });
|
||||
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync({
|
||||
id: 1,
|
||||
data: { subject: 'Updated RFA' },
|
||||
});
|
||||
});
|
||||
|
||||
expect(rfaService.update).toHaveBeenCalledWith(1, { subject: 'Updated RFA' });
|
||||
expect(toast.success).toHaveBeenCalledWith('RFA updated successfully');
|
||||
});
|
||||
});
|
||||
|
||||
describe('useProcessRFA', () => {
|
||||
it('should process workflow action and show toast', async () => {
|
||||
const mockResponse = { id: 1, status: 'approved' };
|
||||
vi.mocked(rfaService.processWorkflow).mockResolvedValue(mockResponse);
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useProcessRFA(), { wrapper });
|
||||
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync({
|
||||
id: 1,
|
||||
data: { action: 'approve', comment: 'Approved' },
|
||||
});
|
||||
});
|
||||
|
||||
expect(rfaService.processWorkflow).toHaveBeenCalledWith(1, {
|
||||
action: 'approve',
|
||||
comment: 'Approved',
|
||||
});
|
||||
expect(toast.success).toHaveBeenCalledWith('Workflow status updated successfully');
|
||||
});
|
||||
|
||||
it('should handle workflow error', async () => {
|
||||
const mockError = {
|
||||
message: 'Error',
|
||||
response: { data: { message: 'Permission denied' } },
|
||||
};
|
||||
vi.mocked(rfaService.processWorkflow).mockRejectedValue(mockError);
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useProcessRFA(), { wrapper });
|
||||
|
||||
await act(async () => {
|
||||
try {
|
||||
await result.current.mutateAsync({
|
||||
id: 1,
|
||||
data: { action: 'reject' },
|
||||
});
|
||||
} catch {
|
||||
// Expected
|
||||
}
|
||||
});
|
||||
|
||||
expect(toast.error).toHaveBeenCalledWith('Failed to process workflow', {
|
||||
description: 'Permission denied',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
234
frontend/hooks/__tests__/use-users.test.ts
Normal file
234
frontend/hooks/__tests__/use-users.test.ts
Normal file
@@ -0,0 +1,234 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { renderHook, waitFor, act } from '@testing-library/react';
|
||||
import { createTestQueryClient } from '@/lib/test-utils';
|
||||
import {
|
||||
useUsers,
|
||||
useRoles,
|
||||
useCreateUser,
|
||||
useUpdateUser,
|
||||
useDeleteUser,
|
||||
userKeys,
|
||||
} from '../use-users';
|
||||
import { userService } from '@/lib/services/user.service';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
// Mock the service
|
||||
vi.mock('@/lib/services/user.service', () => ({
|
||||
userService: {
|
||||
getAll: vi.fn(),
|
||||
getById: vi.fn(),
|
||||
create: vi.fn(),
|
||||
update: vi.fn(),
|
||||
delete: vi.fn(),
|
||||
getRoles: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('use-users hooks', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('userKeys', () => {
|
||||
it('should generate correct cache keys', () => {
|
||||
expect(userKeys.all).toEqual(['users']);
|
||||
expect(userKeys.list({ search: 'john' })).toEqual([
|
||||
'users',
|
||||
'list',
|
||||
{ search: 'john' },
|
||||
]);
|
||||
expect(userKeys.detail(1)).toEqual(['users', 'detail', 1]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('useUsers', () => {
|
||||
it('should fetch users successfully', async () => {
|
||||
const mockData = {
|
||||
data: [
|
||||
{ userId: 1, username: 'john', email: 'john@example.com' },
|
||||
{ userId: 2, username: 'jane', email: 'jane@example.com' },
|
||||
],
|
||||
meta: { total: 2, page: 1, limit: 10 },
|
||||
};
|
||||
|
||||
vi.mocked(userService.getAll).mockResolvedValue(mockData);
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useUsers({ search: 'test' }), { wrapper });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.isSuccess).toBe(true);
|
||||
});
|
||||
|
||||
expect(result.current.data).toEqual(mockData);
|
||||
expect(userService.getAll).toHaveBeenCalledWith({ search: 'test' });
|
||||
});
|
||||
|
||||
it('should handle error state', async () => {
|
||||
vi.mocked(userService.getAll).mockRejectedValue(new Error('API Error'));
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useUsers(), { wrapper });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.isError).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('useRoles', () => {
|
||||
it('should fetch roles successfully', async () => {
|
||||
const mockRoles = [
|
||||
{ roleId: 1, name: 'Admin' },
|
||||
{ roleId: 2, name: 'Editor' },
|
||||
{ roleId: 3, name: 'Viewer' },
|
||||
];
|
||||
|
||||
vi.mocked(userService.getRoles).mockResolvedValue(mockRoles);
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useRoles(), { wrapper });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.isSuccess).toBe(true);
|
||||
});
|
||||
|
||||
expect(result.current.data).toEqual(mockRoles);
|
||||
expect(userService.getRoles).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('useCreateUser', () => {
|
||||
it('should create user and show success toast', async () => {
|
||||
const mockResponse = { userId: 1, username: 'newuser' };
|
||||
vi.mocked(userService.create).mockResolvedValue(mockResponse);
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useCreateUser(), { wrapper });
|
||||
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync({
|
||||
username: 'newuser',
|
||||
email: 'newuser@example.com',
|
||||
password: 'password123',
|
||||
roleIds: [2],
|
||||
});
|
||||
});
|
||||
|
||||
expect(userService.create).toHaveBeenCalled();
|
||||
expect(toast.success).toHaveBeenCalledWith('User created successfully');
|
||||
});
|
||||
|
||||
it('should show error toast on failure', async () => {
|
||||
const mockError = {
|
||||
message: 'Error',
|
||||
response: { data: { message: 'Username already exists' } },
|
||||
};
|
||||
vi.mocked(userService.create).mockRejectedValue(mockError);
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useCreateUser(), { wrapper });
|
||||
|
||||
await act(async () => {
|
||||
try {
|
||||
await result.current.mutateAsync({
|
||||
username: 'existinguser',
|
||||
email: 'test@example.com',
|
||||
password: 'password',
|
||||
});
|
||||
} catch {
|
||||
// Expected
|
||||
}
|
||||
});
|
||||
|
||||
expect(toast.error).toHaveBeenCalledWith('Failed to create user', {
|
||||
description: 'Username already exists',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('useUpdateUser', () => {
|
||||
it('should update user and show success toast', async () => {
|
||||
const mockResponse = { userId: 1, email: 'updated@example.com' };
|
||||
vi.mocked(userService.update).mockResolvedValue(mockResponse);
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useUpdateUser(), { wrapper });
|
||||
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync({
|
||||
id: 1,
|
||||
data: { email: 'updated@example.com' },
|
||||
});
|
||||
});
|
||||
|
||||
expect(userService.update).toHaveBeenCalledWith(1, { email: 'updated@example.com' });
|
||||
expect(toast.success).toHaveBeenCalledWith('User updated successfully');
|
||||
});
|
||||
|
||||
it('should show error toast on failure', async () => {
|
||||
const mockError = {
|
||||
message: 'Error',
|
||||
response: { data: { message: 'User not found' } },
|
||||
};
|
||||
vi.mocked(userService.update).mockRejectedValue(mockError);
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useUpdateUser(), { wrapper });
|
||||
|
||||
await act(async () => {
|
||||
try {
|
||||
await result.current.mutateAsync({
|
||||
id: 999,
|
||||
data: { email: 'test@example.com' },
|
||||
});
|
||||
} catch {
|
||||
// Expected
|
||||
}
|
||||
});
|
||||
|
||||
expect(toast.error).toHaveBeenCalledWith('Failed to update user', {
|
||||
description: 'User not found',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('useDeleteUser', () => {
|
||||
it('should delete user and show success toast', async () => {
|
||||
vi.mocked(userService.delete).mockResolvedValue({});
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useDeleteUser(), { wrapper });
|
||||
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync(1);
|
||||
});
|
||||
|
||||
expect(userService.delete).toHaveBeenCalledWith(1);
|
||||
expect(toast.success).toHaveBeenCalledWith('User deleted successfully');
|
||||
});
|
||||
|
||||
it('should show error toast on delete failure', async () => {
|
||||
const mockError = {
|
||||
message: 'Error',
|
||||
response: { data: { message: 'Cannot delete yourself' } },
|
||||
};
|
||||
vi.mocked(userService.delete).mockRejectedValue(mockError);
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useDeleteUser(), { wrapper });
|
||||
|
||||
await act(async () => {
|
||||
try {
|
||||
await result.current.mutateAsync(1);
|
||||
} catch {
|
||||
// Expected
|
||||
}
|
||||
});
|
||||
|
||||
expect(toast.error).toHaveBeenCalledWith('Failed to delete user', {
|
||||
description: 'Cannot delete yourself',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user