test(frontend): raise overall statement coverage to 30.42% for Phase 1 MVP
This commit is contained in:
@@ -0,0 +1,209 @@
|
||||
// File: frontend/hooks/__tests__/use-ai-prompts.test.ts
|
||||
// Change Log:
|
||||
// - 2026-06-13: Initial creation - test coverage for useAiPrompts and useSandboxRun hooks
|
||||
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
import { renderHook, waitFor, act } from '@testing-library/react';
|
||||
import { createTestQueryClient } from '@/lib/test-utils';
|
||||
import { useAiPrompts, useSandboxRun } from '../use-ai-prompts';
|
||||
import { aiPromptsService } from '@/lib/services/ai-prompts.service';
|
||||
import { adminAiService } from '@/lib/services/admin-ai.service';
|
||||
|
||||
// Mock services
|
||||
vi.mock('@/lib/services/ai-prompts.service', () => ({
|
||||
aiPromptsService: {
|
||||
listVersions: vi.fn(),
|
||||
createVersion: vi.fn(),
|
||||
activateVersion: vi.fn(),
|
||||
deleteVersion: vi.fn(),
|
||||
updateNote: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('@/lib/services/admin-ai.service', () => ({
|
||||
adminAiService: {
|
||||
getSandboxJobStatus: vi.fn(),
|
||||
submitSandboxExtract: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('useAiPrompts hooks', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('useAiPrompts', () => {
|
||||
it('ควรดึงข้อมูล prompt versions สำเร็จ', async () => {
|
||||
const mockData = [{ versionNumber: 1, template: 'test', isActive: true }];
|
||||
vi.mocked(aiPromptsService.listVersions).mockResolvedValue(mockData as any);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useAiPrompts('RFA'), { wrapper });
|
||||
await waitFor(() => {
|
||||
expect(result.current.versionsQuery.isSuccess).toBe(true);
|
||||
});
|
||||
expect(result.current.versionsQuery.data).toEqual(mockData);
|
||||
expect(aiPromptsService.listVersions).toHaveBeenCalledWith('RFA');
|
||||
});
|
||||
|
||||
it('ควรเรียก createVersion สำเร็จ', async () => {
|
||||
vi.mocked(aiPromptsService.createVersion).mockResolvedValue({ success: true } as any);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useAiPrompts('RFA'), { wrapper });
|
||||
await act(async () => {
|
||||
await result.current.createMutation.mutateAsync('new template');
|
||||
});
|
||||
expect(aiPromptsService.createVersion).toHaveBeenCalledWith('RFA', 'new template');
|
||||
});
|
||||
|
||||
it('ควรเรียก activateVersion สำเร็จ', async () => {
|
||||
vi.mocked(aiPromptsService.activateVersion).mockResolvedValue({ success: true } as any);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useAiPrompts('RFA'), { wrapper });
|
||||
await act(async () => {
|
||||
await result.current.activateMutation.mutateAsync(2);
|
||||
});
|
||||
expect(aiPromptsService.activateVersion).toHaveBeenCalledWith('RFA', 2);
|
||||
});
|
||||
|
||||
it('ควรเรียก deleteVersion สำเร็จ', async () => {
|
||||
vi.mocked(aiPromptsService.deleteVersion).mockResolvedValue({ success: true } as any);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useAiPrompts('RFA'), { wrapper });
|
||||
await act(async () => {
|
||||
await result.current.deleteMutation.mutateAsync(3);
|
||||
});
|
||||
expect(aiPromptsService.deleteVersion).toHaveBeenCalledWith('RFA', 3);
|
||||
});
|
||||
|
||||
it('ควรเรียก updateNote สำเร็จ', async () => {
|
||||
vi.mocked(aiPromptsService.updateNote).mockResolvedValue({ success: true } as any);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useAiPrompts('RFA'), { wrapper });
|
||||
await act(async () => {
|
||||
await result.current.updateNoteMutation.mutateAsync({ versionNumber: 1, note: 'New Note' });
|
||||
});
|
||||
expect(aiPromptsService.updateNote).toHaveBeenCalledWith('RFA', 1, 'New Note');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('useSandboxRun hook', () => {
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it('ควรเริ่มต้นด้วยสถานะว่างเปล่า', () => {
|
||||
const { result } = renderHook(() => useSandboxRun());
|
||||
expect(result.current.state).toEqual({
|
||||
isRunning: false,
|
||||
progress: 0,
|
||||
statusText: '',
|
||||
result: null,
|
||||
});
|
||||
expect(result.current.jobId).toBeNull();
|
||||
});
|
||||
|
||||
it('ควรสามารถส่ง job และจำลอง polling จนกระทั่งเสร็จสิ้น (completed)', async () => {
|
||||
const mockFile = new File(['pdf-content'], 'test.pdf', { type: 'application/pdf' });
|
||||
vi.mocked(adminAiService.submitSandboxExtract).mockResolvedValue({ requestPublicId: 'job-123' } as any);
|
||||
let pollCount = 0;
|
||||
vi.mocked(adminAiService.getSandboxJobStatus).mockImplementation(async () => {
|
||||
pollCount += 1;
|
||||
if (pollCount === 1) return { status: 'pending' } as any;
|
||||
if (pollCount === 2) return { status: 'processing' } as any;
|
||||
return { status: 'completed', metadata: { test: 1 } } as any;
|
||||
});
|
||||
const onCompletedMock = vi.fn();
|
||||
const { result } = renderHook(() => useSandboxRun(onCompletedMock));
|
||||
let jobIdPromise: Promise<string> | undefined;
|
||||
act(() => {
|
||||
jobIdPromise = result.current.submit(mockFile, 'project-1', 'contract-1');
|
||||
});
|
||||
await act(async () => {
|
||||
await jobIdPromise;
|
||||
});
|
||||
expect(result.current.jobId).toBe('job-123');
|
||||
expect(result.current.state.isRunning).toBe(true);
|
||||
expect(result.current.state.progress).toBe(30);
|
||||
expect(result.current.state.statusText).toBe('ai.prompt.statusPending');
|
||||
await act(async () => {
|
||||
await vi.advanceTimersByTimeAsync(4000);
|
||||
});
|
||||
expect(result.current.state.progress).toBe(70);
|
||||
expect(result.current.state.statusText).toBe('ai.prompt.statusProcessing');
|
||||
await act(async () => {
|
||||
await vi.advanceTimersByTimeAsync(4000);
|
||||
});
|
||||
expect(result.current.state.isRunning).toBe(false);
|
||||
expect(result.current.state.progress).toBe(100);
|
||||
expect(result.current.state.statusText).toBe('ai.prompt.statusCompleted');
|
||||
expect(onCompletedMock).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('ควรหยุดการ polling เมื่อสถานะเป็น failed', async () => {
|
||||
const mockFile = new File(['pdf-content'], 'test.pdf', { type: 'application/pdf' });
|
||||
vi.mocked(adminAiService.submitSandboxExtract).mockResolvedValue({ requestPublicId: 'job-failed' } as any);
|
||||
vi.mocked(adminAiService.getSandboxJobStatus).mockResolvedValue({ status: 'failed' } as any);
|
||||
const { result } = renderHook(() => useSandboxRun());
|
||||
let jobIdPromise: Promise<string> | undefined;
|
||||
act(() => {
|
||||
jobIdPromise = result.current.submit(mockFile, 'project-1');
|
||||
});
|
||||
await act(async () => {
|
||||
await jobIdPromise;
|
||||
});
|
||||
await act(async () => {
|
||||
await vi.advanceTimersByTimeAsync(0);
|
||||
});
|
||||
expect(result.current.state.isRunning).toBe(false);
|
||||
expect(result.current.state.progress).toBe(100);
|
||||
expect(result.current.state.statusText).toBe('ai.prompt.statusFailed');
|
||||
expect(result.current.jobId).toBeNull();
|
||||
});
|
||||
|
||||
it('ควรหยุดการ polling เมื่อสถานะเป็น cancelled', async () => {
|
||||
vi.mocked(adminAiService.getSandboxJobStatus).mockResolvedValue({ status: 'cancelled' } as any);
|
||||
const { result } = renderHook(() => useSandboxRun());
|
||||
act(() => {
|
||||
result.current.startPolling('job-cancelled');
|
||||
});
|
||||
expect(result.current.jobId).toBe('job-cancelled');
|
||||
await act(async () => {
|
||||
await vi.advanceTimersByTimeAsync(0);
|
||||
});
|
||||
expect(result.current.state.isRunning).toBe(false);
|
||||
expect(result.current.state.progress).toBe(100);
|
||||
expect(result.current.state.statusText).toBe('ai.prompt.statusCancelled');
|
||||
expect(result.current.jobId).toBeNull();
|
||||
});
|
||||
|
||||
it('ควรจะทำงานต่อเงียบๆ และพยายามต่อเมื่อเกิด network error ระหว่าง polling', async () => {
|
||||
vi.mocked(adminAiService.getSandboxJobStatus).mockRejectedValue(new Error('Network error'));
|
||||
const { result } = renderHook(() => useSandboxRun());
|
||||
act(() => {
|
||||
result.current.startPolling('job-error');
|
||||
});
|
||||
await act(async () => {
|
||||
await vi.advanceTimersByTimeAsync(0);
|
||||
});
|
||||
expect(result.current.jobId).toBe('job-error');
|
||||
});
|
||||
|
||||
it('ควรสามารถรีเซ็ตสถานะกลับสู่ค่าเริ่มต้นได้', () => {
|
||||
const { result } = renderHook(() => useSandboxRun());
|
||||
act(() => {
|
||||
result.current.startPolling('job-to-reset');
|
||||
});
|
||||
expect(result.current.jobId).toBe('job-to-reset');
|
||||
act(() => {
|
||||
result.current.reset();
|
||||
});
|
||||
expect(result.current.jobId).toBeNull();
|
||||
expect(result.current.state.isRunning).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,75 @@
|
||||
// File: frontend/hooks/__tests__/use-dashboard.test.ts
|
||||
// Change Log:
|
||||
// - 2026-06-13: Initial creation - test coverage for use-dashboard hooks
|
||||
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import { createTestQueryClient } from '@/lib/test-utils';
|
||||
import { useDashboardStats, useRecentActivity, usePendingTasks, dashboardKeys } from '../use-dashboard';
|
||||
import { dashboardService } from '@/lib/services/dashboard.service';
|
||||
|
||||
// Mock services
|
||||
vi.mock('@/lib/services/dashboard.service', () => ({
|
||||
dashboardService: {
|
||||
getStats: vi.fn(),
|
||||
getRecentActivity: vi.fn(),
|
||||
getPendingTasks: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('use-dashboard hooks', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('dashboardKeys', () => {
|
||||
it('ควรสร้าง cache keys ที่ถูกต้อง', () => {
|
||||
expect(dashboardKeys.all).toEqual(['dashboard']);
|
||||
expect(dashboardKeys.stats('proj-1')).toEqual(['dashboard', 'stats', 'proj-1']);
|
||||
expect(dashboardKeys.activity('proj-2')).toEqual(['dashboard', 'activity', 'proj-2']);
|
||||
expect(dashboardKeys.pending('proj-3')).toEqual(['dashboard', 'pending', 'proj-3']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('useDashboardStats', () => {
|
||||
it('ควรดึงข้อมูล stats สำเร็จ', async () => {
|
||||
const mockData = { totalDocuments: 10, pendingApprovals: 2 };
|
||||
vi.mocked(dashboardService.getStats).mockResolvedValue(mockData as any);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useDashboardStats('proj-1'), { wrapper });
|
||||
await waitFor(() => {
|
||||
expect(result.current.isSuccess).toBe(true);
|
||||
});
|
||||
expect(result.current.data).toEqual(mockData);
|
||||
expect(dashboardService.getStats).toHaveBeenCalledWith('proj-1');
|
||||
});
|
||||
});
|
||||
|
||||
describe('useRecentActivity', () => {
|
||||
it('ควรดึงข้อมูล recent activity สำเร็จ', async () => {
|
||||
const mockData = [{ id: 'act-1', action: 'CREATE' }];
|
||||
vi.mocked(dashboardService.getRecentActivity).mockResolvedValue(mockData as any);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useRecentActivity('proj-1'), { wrapper });
|
||||
await waitFor(() => {
|
||||
expect(result.current.isSuccess).toBe(true);
|
||||
});
|
||||
expect(result.current.data).toEqual(mockData);
|
||||
expect(dashboardService.getRecentActivity).toHaveBeenCalledWith('proj-1');
|
||||
});
|
||||
});
|
||||
|
||||
describe('usePendingTasks', () => {
|
||||
it('ควรดึงข้อมูล pending tasks สำเร็จ', async () => {
|
||||
const mockData = [{ publicId: 'task-1', title: 'Task 1' }];
|
||||
vi.mocked(dashboardService.getPendingTasks).mockResolvedValue(mockData as any);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => usePendingTasks('proj-1'), { wrapper });
|
||||
await waitFor(() => {
|
||||
expect(result.current.isSuccess).toBe(true);
|
||||
});
|
||||
expect(result.current.data).toEqual(mockData);
|
||||
expect(dashboardService.getPendingTasks).toHaveBeenCalledWith('proj-1');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,118 @@
|
||||
// File: frontend/hooks/__tests__/use-delegation.test.ts
|
||||
// Change Log:
|
||||
// - 2026-06-13: Initial creation - test coverage for use-delegation hooks
|
||||
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { renderHook, waitFor, act } from '@testing-library/react';
|
||||
import { createTestQueryClient } from '@/lib/test-utils';
|
||||
import { useMyDelegations, useCreateDelegation, useRevokeDelegation, delegationKeys } from '../use-delegation';
|
||||
import apiClient from '@/lib/api/client';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
// Mock apiClient
|
||||
vi.mock('@/lib/api/client', () => ({
|
||||
default: {
|
||||
get: vi.fn(),
|
||||
post: vi.fn(),
|
||||
delete: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('use-delegation hooks', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('delegationKeys', () => {
|
||||
it('ควรสร้าง cache keys ที่ถูกต้อง', () => {
|
||||
expect(delegationKeys.all).toEqual(['delegations']);
|
||||
expect(delegationKeys.mine()).toEqual(['delegations', 'mine']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('useMyDelegations', () => {
|
||||
it('ควรดึงข้อมูล delegations ของฉันสำเร็จ', async () => {
|
||||
const mockData = [{ publicId: 'uuid-deleg-1', scope: 'PROJECT' }];
|
||||
vi.mocked(apiClient.get).mockResolvedValue({ data: mockData });
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useMyDelegations(), { wrapper });
|
||||
await waitFor(() => {
|
||||
expect(result.current.isSuccess).toBe(true);
|
||||
});
|
||||
expect(result.current.data).toEqual(mockData);
|
||||
expect(apiClient.get).toHaveBeenCalledWith('/delegations');
|
||||
});
|
||||
});
|
||||
|
||||
describe('useCreateDelegation', () => {
|
||||
it('ควรสร้าง delegation สำเร็จและแสดง toast success', async () => {
|
||||
vi.mocked(apiClient.post).mockResolvedValue({ data: { success: true } });
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useCreateDelegation(), { wrapper });
|
||||
const mockDto = {
|
||||
delegateUserPublicId: 'uuid-user-1',
|
||||
scope: 'PROJECT' as const,
|
||||
startDate: '2026-01-01',
|
||||
endDate: '2026-01-10',
|
||||
};
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync(mockDto);
|
||||
});
|
||||
expect(apiClient.post).toHaveBeenCalledWith('/delegations', mockDto);
|
||||
expect(toast.success).toHaveBeenCalledWith('Delegation created successfully');
|
||||
});
|
||||
|
||||
it('ควรแสดง toast error เมื่อสร้าง delegation ล้มเหลว', async () => {
|
||||
const mockError = new Error('API Error');
|
||||
vi.mocked(apiClient.post).mockRejectedValue(mockError);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useCreateDelegation(), { wrapper });
|
||||
const mockDto = {
|
||||
delegateUserPublicId: 'uuid-user-1',
|
||||
scope: 'PROJECT' as const,
|
||||
startDate: '2026-01-01',
|
||||
endDate: '2026-01-10',
|
||||
};
|
||||
await act(async () => {
|
||||
try {
|
||||
await result.current.mutateAsync(mockDto);
|
||||
} catch {
|
||||
// Expected
|
||||
}
|
||||
});
|
||||
expect(toast.error).toHaveBeenCalledWith('Failed to create delegation', {
|
||||
description: 'API Error',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('useRevokeDelegation', () => {
|
||||
it('ควรลบ delegation (revoke) สำเร็จและแสดง toast success', async () => {
|
||||
vi.mocked(apiClient.delete).mockResolvedValue({ data: { success: true } });
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useRevokeDelegation(), { wrapper });
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync('uuid-deleg-1');
|
||||
});
|
||||
expect(apiClient.delete).toHaveBeenCalledWith('/delegations/uuid-deleg-1');
|
||||
expect(toast.success).toHaveBeenCalledWith('Delegation revoked');
|
||||
});
|
||||
|
||||
it('ควรแสดง toast error เมื่อยกเลิก delegation ล้มเหลว', async () => {
|
||||
const mockError = new Error('API Error');
|
||||
vi.mocked(apiClient.delete).mockRejectedValue(mockError);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useRevokeDelegation(), { wrapper });
|
||||
await act(async () => {
|
||||
try {
|
||||
await result.current.mutateAsync('uuid-deleg-1');
|
||||
} catch {
|
||||
// Expected
|
||||
}
|
||||
});
|
||||
expect(toast.error).toHaveBeenCalledWith('Failed to revoke delegation', {
|
||||
description: 'API Error',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,286 @@
|
||||
// File: frontend/hooks/__tests__/use-master-data.test.ts
|
||||
// Change Log:
|
||||
// - 2026-06-13: Initial creation - test coverage for use-master-data hooks
|
||||
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { renderHook, waitFor, act } from '@testing-library/react';
|
||||
import { createTestQueryClient } from '@/lib/test-utils';
|
||||
import {
|
||||
useOrganizations,
|
||||
useCreateOrganization,
|
||||
useUpdateOrganization,
|
||||
useDeleteOrganization,
|
||||
useDisciplines,
|
||||
useProjects,
|
||||
useContracts,
|
||||
useCorrespondenceTypes,
|
||||
useContractDrawingCategories,
|
||||
useShopMainCategories,
|
||||
useShopSubCategories,
|
||||
masterDataKeys,
|
||||
} from '../use-master-data';
|
||||
import { masterDataService } from '@/lib/services/master-data.service';
|
||||
import { organizationService } from '@/lib/services/organization.service';
|
||||
import { projectService } from '@/lib/services/project.service';
|
||||
import { contractService } from '@/lib/services/contract.service';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
// Mock services
|
||||
vi.mock('@/lib/services/master-data.service', () => ({
|
||||
masterDataService: {
|
||||
createOrganization: vi.fn(),
|
||||
updateOrganization: vi.fn(),
|
||||
deleteOrganization: vi.fn(),
|
||||
getDisciplines: vi.fn(),
|
||||
getCorrespondenceTypes: vi.fn(),
|
||||
getContractDrawingCategories: vi.fn(),
|
||||
getShopMainCategories: vi.fn(),
|
||||
getShopSubCategories: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('@/lib/services/organization.service', () => ({
|
||||
organizationService: {
|
||||
getAll: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('@/lib/services/project.service', () => ({
|
||||
projectService: {
|
||||
getAll: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('@/lib/services/contract.service', () => ({
|
||||
contractService: {
|
||||
getAll: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('use-master-data hooks', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('masterDataKeys', () => {
|
||||
it('ควรสร้าง cache keys ที่ถูกต้อง', () => {
|
||||
expect(masterDataKeys.all).toEqual(['masterData']);
|
||||
expect(masterDataKeys.organizations()).toEqual(['masterData', 'organizations']);
|
||||
expect(masterDataKeys.correspondenceTypes()).toEqual(['masterData', 'correspondenceTypes']);
|
||||
expect(masterDataKeys.disciplines('uuid-1')).toEqual(['masterData', 'disciplines', 'uuid-1']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('useOrganizations', () => {
|
||||
it('ควรดึงข้อมูลองค์กรสำเร็จ', async () => {
|
||||
const mockData = [{ publicId: 'uuid-org-1', organizationName: 'Org A' }];
|
||||
vi.mocked(organizationService.getAll).mockResolvedValue(mockData);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useOrganizations({ isActive: true }), { wrapper });
|
||||
await waitFor(() => {
|
||||
expect(result.current.isSuccess).toBe(true);
|
||||
});
|
||||
expect(result.current.data).toEqual(mockData);
|
||||
expect(organizationService.getAll).toHaveBeenCalledWith({ isActive: true });
|
||||
});
|
||||
});
|
||||
|
||||
describe('useCreateOrganization', () => {
|
||||
it('ควรสร้างองค์กรสำเร็จและแสดง toast success', async () => {
|
||||
const mockResponse = { publicId: 'uuid-org-1', organizationName: 'New Org' };
|
||||
vi.mocked(masterDataService.createOrganization).mockResolvedValue(mockResponse);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useCreateOrganization(), { wrapper });
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync({ organizationName: 'New Org', organizationCode: 'ORG' });
|
||||
});
|
||||
expect(masterDataService.createOrganization).toHaveBeenCalledWith({ organizationName: 'New Org', organizationCode: 'ORG' });
|
||||
expect(toast.success).toHaveBeenCalledWith('Organization created successfully');
|
||||
});
|
||||
|
||||
it('ควรแสดง toast error เมื่อสร้างไม่สำเร็จ', async () => {
|
||||
const mockError = {
|
||||
message: 'Error',
|
||||
response: { data: { message: 'Duplicate code' } },
|
||||
};
|
||||
vi.mocked(masterDataService.createOrganization).mockRejectedValue(mockError);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useCreateOrganization(), { wrapper });
|
||||
await act(async () => {
|
||||
try {
|
||||
await result.current.mutateAsync({ organizationName: 'New Org', organizationCode: 'ORG' });
|
||||
} catch {
|
||||
// คาดหวังว่าจะเกิด error
|
||||
}
|
||||
});
|
||||
expect(toast.error).toHaveBeenCalledWith('Failed to create organization', {
|
||||
description: 'Duplicate code',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('useUpdateOrganization', () => {
|
||||
it('ควรแก้ไของค์กรสำเร็จและแสดง toast success', async () => {
|
||||
const mockResponse = { publicId: 'uuid-org-1', organizationName: 'Updated Org' };
|
||||
vi.mocked(masterDataService.updateOrganization).mockResolvedValue(mockResponse);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useUpdateOrganization(), { wrapper });
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync({ uuid: 'uuid-org-1', data: { organizationName: 'Updated Org' } });
|
||||
});
|
||||
expect(masterDataService.updateOrganization).toHaveBeenCalledWith('uuid-org-1', { organizationName: 'Updated Org' });
|
||||
expect(toast.success).toHaveBeenCalledWith('Organization updated successfully');
|
||||
});
|
||||
|
||||
it('ควรแสดง toast error เมื่อแก้ไขไม่สำเร็จ', async () => {
|
||||
const mockError = {
|
||||
message: 'Error',
|
||||
response: { data: { message: 'Not found' } },
|
||||
};
|
||||
vi.mocked(masterDataService.updateOrganization).mockRejectedValue(mockError);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useUpdateOrganization(), { wrapper });
|
||||
await act(async () => {
|
||||
try {
|
||||
await result.current.mutateAsync({ uuid: 'uuid-org-1', data: { organizationName: 'Updated Org' } });
|
||||
} catch {
|
||||
// คาดหวังว่าจะเกิด error
|
||||
}
|
||||
});
|
||||
expect(toast.error).toHaveBeenCalledWith('Failed to update organization', {
|
||||
description: 'Not found',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('useDeleteOrganization', () => {
|
||||
it('ควรลบองค์กรสำเร็จและแสดง toast success', async () => {
|
||||
vi.mocked(masterDataService.deleteOrganization).mockResolvedValue({});
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useDeleteOrganization(), { wrapper });
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync('uuid-org-1');
|
||||
});
|
||||
expect(masterDataService.deleteOrganization).toHaveBeenCalledWith('uuid-org-1');
|
||||
expect(toast.success).toHaveBeenCalledWith('Organization deleted successfully');
|
||||
});
|
||||
|
||||
it('ควรแสดง toast error เมื่อลบไม่สำเร็จ', async () => {
|
||||
const mockError = {
|
||||
message: 'Error',
|
||||
response: { data: { message: 'Constraint violation' } },
|
||||
};
|
||||
vi.mocked(masterDataService.deleteOrganization).mockRejectedValue(mockError);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useDeleteOrganization(), { wrapper });
|
||||
await act(async () => {
|
||||
try {
|
||||
await result.current.mutateAsync('uuid-org-1');
|
||||
} catch {
|
||||
// คาดหวังว่าจะเกิด error
|
||||
}
|
||||
});
|
||||
expect(toast.error).toHaveBeenCalledWith('Failed to delete organization', {
|
||||
description: 'Constraint violation',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('useDisciplines', () => {
|
||||
it('ควรดึงข้อมูลสาขา (Disciplines) สำเร็จ', async () => {
|
||||
const mockData = [{ publicId: 'uuid-disp-1', disciplineCode: 'CIV' }];
|
||||
vi.mocked(masterDataService.getDisciplines).mockResolvedValue(mockData);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useDisciplines('uuid-contract-1'), { wrapper });
|
||||
await waitFor(() => {
|
||||
expect(result.current.isSuccess).toBe(true);
|
||||
});
|
||||
expect(result.current.data).toEqual(mockData);
|
||||
expect(masterDataService.getDisciplines).toHaveBeenCalledWith('uuid-contract-1');
|
||||
});
|
||||
});
|
||||
|
||||
describe('useProjects', () => {
|
||||
it('ควรดึงข้อมูลโครงการ (Projects) สำเร็จ', async () => {
|
||||
const mockData = [{ publicId: 'uuid-proj-1', projectName: 'Project A' }];
|
||||
vi.mocked(projectService.getAll).mockResolvedValue(mockData);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useProjects(true), { wrapper });
|
||||
await waitFor(() => {
|
||||
expect(result.current.isSuccess).toBe(true);
|
||||
});
|
||||
expect(result.current.data).toEqual(mockData);
|
||||
expect(projectService.getAll).toHaveBeenCalledWith({ isActive: true });
|
||||
});
|
||||
});
|
||||
|
||||
describe('useContracts', () => {
|
||||
it('ควรดึงข้อมูลสัญญา (Contracts) สำเร็จ', async () => {
|
||||
const mockData = [{ publicId: 'uuid-cont-1', name: 'Contract A' }];
|
||||
vi.mocked(contractService.getAll).mockResolvedValue(mockData);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useContracts('uuid-proj-1'), { wrapper });
|
||||
await waitFor(() => {
|
||||
expect(result.current.isSuccess).toBe(true);
|
||||
});
|
||||
expect(result.current.data).toEqual(mockData);
|
||||
expect(contractService.getAll).toHaveBeenCalledWith({ projectId: 'uuid-proj-1' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('useCorrespondenceTypes', () => {
|
||||
it('ควรดึงข้อมูลชนิดจดหมายนำส่งสำเร็จ', async () => {
|
||||
const mockData = [{ publicId: 'uuid-corr-1', typeCode: 'RFA' }];
|
||||
vi.mocked(masterDataService.getCorrespondenceTypes).mockResolvedValue(mockData);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useCorrespondenceTypes(), { wrapper });
|
||||
await waitFor(() => {
|
||||
expect(result.current.isSuccess).toBe(true);
|
||||
});
|
||||
expect(result.current.data).toEqual(mockData);
|
||||
expect(masterDataService.getCorrespondenceTypes).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('useContractDrawingCategories', () => {
|
||||
it('ควรดึงข้อมูลหมวดหมู่แบบคู่สัญญาสำเร็จ', async () => {
|
||||
const mockData = [{ id: 1, categoryName: 'Design Drawing' }];
|
||||
vi.mocked(masterDataService.getContractDrawingCategories).mockResolvedValue(mockData);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useContractDrawingCategories('uuid-proj-1'), { wrapper });
|
||||
await waitFor(() => {
|
||||
expect(result.current.isSuccess).toBe(true);
|
||||
});
|
||||
expect(result.current.data).toEqual(mockData);
|
||||
expect(masterDataService.getContractDrawingCategories).toHaveBeenCalledWith('uuid-proj-1');
|
||||
});
|
||||
});
|
||||
|
||||
describe('useShopMainCategories', () => {
|
||||
it('ควรดึงข้อมูลหมวดหมู่หลักแบบรายละเอียดก่อสร้างสำเร็จ', async () => {
|
||||
const mockData = [{ id: 1, mainCategoryName: 'Structural' }];
|
||||
vi.mocked(masterDataService.getShopMainCategories).mockResolvedValue(mockData);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useShopMainCategories(123), { wrapper });
|
||||
await waitFor(() => {
|
||||
expect(result.current.isSuccess).toBe(true);
|
||||
});
|
||||
expect(result.current.data).toEqual(mockData);
|
||||
expect(masterDataService.getShopMainCategories).toHaveBeenCalledWith(123);
|
||||
});
|
||||
});
|
||||
|
||||
describe('useShopSubCategories', () => {
|
||||
it('ควรดึงข้อมูลหมวดหมู่ย่อยสำเร็จ', async () => {
|
||||
const mockData = [{ id: 10, subCategoryName: 'Foundation' }];
|
||||
vi.mocked(masterDataService.getShopSubCategories).mockResolvedValue(mockData);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useShopSubCategories(123, 1), { wrapper });
|
||||
await waitFor(() => {
|
||||
expect(result.current.isSuccess).toBe(true);
|
||||
});
|
||||
expect(result.current.data).toEqual(mockData);
|
||||
expect(masterDataService.getShopSubCategories).toHaveBeenCalledWith(123, 1);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,165 @@
|
||||
// File: frontend/hooks/__tests__/use-numbering.test.ts
|
||||
// Change Log:
|
||||
// - 2026-06-13: Initial creation - test coverage for use-numbering hooks
|
||||
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { renderHook, waitFor, act } from '@testing-library/react';
|
||||
import { createTestQueryClient } from '@/lib/test-utils';
|
||||
import {
|
||||
useTemplates,
|
||||
useSaveTemplate,
|
||||
useNumberingMetrics,
|
||||
useNumberingAuditLogs,
|
||||
useManualOverrideNumbering,
|
||||
useVoidAndReplaceNumbering,
|
||||
useCancelNumbering,
|
||||
useBulkImportNumbering,
|
||||
numberingKeys,
|
||||
} from '../use-numbering';
|
||||
import { documentNumberingService } from '@/lib/services/document-numbering.service';
|
||||
import { numberingApi } from '@/lib/api/numbering';
|
||||
|
||||
// Mock services
|
||||
vi.mock('@/lib/services/document-numbering.service', () => ({
|
||||
documentNumberingService: {
|
||||
getMetrics: vi.fn(),
|
||||
getAuditLogs: vi.fn(),
|
||||
manualOverride: vi.fn(),
|
||||
voidAndReplace: vi.fn(),
|
||||
cancelNumber: vi.fn(),
|
||||
bulkImport: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('@/lib/api/numbering', () => ({
|
||||
numberingApi: {
|
||||
getTemplates: vi.fn(),
|
||||
saveTemplate: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('use-numbering hooks', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('numberingKeys', () => {
|
||||
it('ควรสร้าง cache keys ที่ถูกต้อง', () => {
|
||||
expect(numberingKeys.all).toEqual(['numbering']);
|
||||
expect(numberingKeys.templates()).toEqual(['numbering', 'templates']);
|
||||
expect(numberingKeys.metrics()).toEqual(['numbering', 'metrics']);
|
||||
expect(numberingKeys.auditLogs({ page: 1 })).toEqual(['numbering', 'auditLogs', { page: 1 }]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('useTemplates', () => {
|
||||
it('ควรดึงข้อมูล templates สำเร็จ', async () => {
|
||||
const mockData = [{ id: 1, templateName: 'Temp A' }];
|
||||
vi.mocked(numberingApi.getTemplates).mockResolvedValue(mockData as any);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useTemplates(), { wrapper });
|
||||
await waitFor(() => {
|
||||
expect(result.current.isSuccess).toBe(true);
|
||||
});
|
||||
expect(result.current.data).toEqual(mockData);
|
||||
expect(numberingApi.getTemplates).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('useSaveTemplate', () => {
|
||||
it('ควรบันทึก template สำเร็จ', async () => {
|
||||
const mockResponse = { id: 1, success: true };
|
||||
vi.mocked(numberingApi.saveTemplate).mockResolvedValue(mockResponse as any);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useSaveTemplate(), { wrapper });
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync({ templateName: 'New Temp' } as any);
|
||||
});
|
||||
expect(numberingApi.saveTemplate).toHaveBeenCalledWith({ templateName: 'New Temp' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('useNumberingMetrics', () => {
|
||||
it('ควรดึงข้อมูล metrics สำเร็จ', async () => {
|
||||
const mockData = { totalGenerated: 100 };
|
||||
vi.mocked(documentNumberingService.getMetrics).mockResolvedValue(mockData as any);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useNumberingMetrics(), { wrapper });
|
||||
await waitFor(() => {
|
||||
expect(result.current.isSuccess).toBe(true);
|
||||
});
|
||||
expect(result.current.data).toEqual(mockData);
|
||||
expect(documentNumberingService.getMetrics).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('useNumberingAuditLogs', () => {
|
||||
it('ควรดึงข้อมูล audit logs สำเร็จ', async () => {
|
||||
const mockData = [{ id: 1, action: 'OVERRIDE' }];
|
||||
vi.mocked(documentNumberingService.getAuditLogs).mockResolvedValue(mockData as any);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useNumberingAuditLogs({ page: 1 }), { wrapper });
|
||||
await waitFor(() => {
|
||||
expect(result.current.isSuccess).toBe(true);
|
||||
});
|
||||
expect(result.current.data).toEqual(mockData);
|
||||
expect(documentNumberingService.getAuditLogs).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('useManualOverrideNumbering', () => {
|
||||
it('ควรทำ manual override สำเร็จ', async () => {
|
||||
const mockResponse = { success: true };
|
||||
vi.mocked(documentNumberingService.manualOverride).mockResolvedValue(mockResponse as any);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useManualOverrideNumbering(), { wrapper });
|
||||
const mockDto = { documentNumber: 'DOC-001', reason: 'Urgent' };
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync(mockDto as any);
|
||||
});
|
||||
expect(documentNumberingService.manualOverride).toHaveBeenCalledWith(mockDto);
|
||||
});
|
||||
});
|
||||
|
||||
describe('useVoidAndReplaceNumbering', () => {
|
||||
it('ควรทำ void and replace สำเร็จ', async () => {
|
||||
const mockResponse = { success: true };
|
||||
vi.mocked(documentNumberingService.voidAndReplace).mockResolvedValue(mockResponse as any);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useVoidAndReplaceNumbering(), { wrapper });
|
||||
const mockDto = { originalNumber: 'DOC-001', newNumber: 'DOC-002' };
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync(mockDto as any);
|
||||
});
|
||||
expect(documentNumberingService.voidAndReplace).toHaveBeenCalledWith(mockDto);
|
||||
});
|
||||
});
|
||||
|
||||
describe('useCancelNumbering', () => {
|
||||
it('ควรทำ cancel numbering สำเร็จ', async () => {
|
||||
const mockResponse = { success: true };
|
||||
vi.mocked(documentNumberingService.cancelNumber).mockResolvedValue(mockResponse as any);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useCancelNumbering(), { wrapper });
|
||||
const mockDto = { documentNumber: 'DOC-001' };
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync(mockDto as any);
|
||||
});
|
||||
expect(documentNumberingService.cancelNumber).toHaveBeenCalledWith(mockDto);
|
||||
});
|
||||
});
|
||||
|
||||
describe('useBulkImportNumbering', () => {
|
||||
it('ควรทำ bulk import สำเร็จ', async () => {
|
||||
const mockResponse = { success: true };
|
||||
vi.mocked(documentNumberingService.bulkImport).mockResolvedValue(mockResponse as any);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useBulkImportNumbering(), { wrapper });
|
||||
const mockDto = [{ documentNumber: 'DOC-001', projectId: 1, sequenceNumber: 1 }];
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync(mockDto);
|
||||
});
|
||||
expect(documentNumberingService.bulkImport).toHaveBeenCalledWith(mockDto);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,195 @@
|
||||
// File: frontend/hooks/__tests__/use-review-teams.test.ts
|
||||
// Change Log:
|
||||
// - 2026-06-13: Initial creation - test coverage for use-review-teams hooks
|
||||
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { renderHook, waitFor, act } from '@testing-library/react';
|
||||
import { createTestQueryClient } from '@/lib/test-utils';
|
||||
import {
|
||||
useReviewTeams,
|
||||
useReviewTeam,
|
||||
useCreateReviewTeam,
|
||||
useUpdateReviewTeam,
|
||||
useAddTeamMember,
|
||||
useRemoveTeamMember,
|
||||
reviewTeamKeys,
|
||||
} from '../use-review-teams';
|
||||
import { reviewTeamService } from '@/lib/services/review-team.service';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
// Mock reviewTeamService
|
||||
vi.mock('@/lib/services/review-team.service', () => ({
|
||||
reviewTeamService: {
|
||||
getAll: vi.fn(),
|
||||
getByPublicId: vi.fn(),
|
||||
create: vi.fn(),
|
||||
update: vi.fn(),
|
||||
addMember: vi.fn(),
|
||||
removeMember: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('use-review-teams hooks', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('reviewTeamKeys', () => {
|
||||
it('ควรสร้าง cache keys ที่ถูกต้อง', () => {
|
||||
expect(reviewTeamKeys.all).toEqual(['reviewTeams']);
|
||||
expect(reviewTeamKeys.lists()).toEqual(['reviewTeams', 'list']);
|
||||
expect(reviewTeamKeys.list({ search: 'A' })).toEqual(['reviewTeams', 'list', { search: 'A' }]);
|
||||
expect(reviewTeamKeys.details()).toEqual(['reviewTeams', 'detail']);
|
||||
expect(reviewTeamKeys.detail('uuid-1')).toEqual(['reviewTeams', 'detail', 'uuid-1']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('useReviewTeams', () => {
|
||||
it('ควรดึงข้อมูล lists ของ review teams สำเร็จ', async () => {
|
||||
const mockData = [{ publicId: 'uuid-team-1', teamName: 'Team A' }];
|
||||
vi.mocked(reviewTeamService.getAll).mockResolvedValue(mockData as any);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useReviewTeams({ search: 'A' }), { wrapper });
|
||||
await waitFor(() => {
|
||||
expect(result.current.isSuccess).toBe(true);
|
||||
});
|
||||
expect(result.current.data).toEqual(mockData);
|
||||
expect(reviewTeamService.getAll).toHaveBeenCalledWith({ search: 'A' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('useReviewTeam', () => {
|
||||
it('ควรดึงข้อมูลรายละเอียด review team สำเร็จ', async () => {
|
||||
const mockData = { publicId: 'uuid-team-1', teamName: 'Team A' };
|
||||
vi.mocked(reviewTeamService.getByPublicId).mockResolvedValue(mockData as any);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useReviewTeam('uuid-team-1'), { wrapper });
|
||||
await waitFor(() => {
|
||||
expect(result.current.isSuccess).toBe(true);
|
||||
});
|
||||
expect(result.current.data).toEqual(mockData);
|
||||
expect(reviewTeamService.getByPublicId).toHaveBeenCalledWith('uuid-team-1');
|
||||
});
|
||||
});
|
||||
|
||||
describe('useCreateReviewTeam', () => {
|
||||
it('ควรสร้าง review team สำเร็จและแสดง toast success', async () => {
|
||||
vi.mocked(reviewTeamService.create).mockResolvedValue({ success: true } as any);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useCreateReviewTeam(), { wrapper });
|
||||
const mockDto = { teamName: 'New Team', projectId: 1 };
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync(mockDto as any);
|
||||
});
|
||||
expect(reviewTeamService.create).toHaveBeenCalledWith(mockDto);
|
||||
expect(toast.success).toHaveBeenCalledWith('Review Team created successfully');
|
||||
});
|
||||
|
||||
it('ควรแสดง toast error เมื่อสร้าง review team ล้มเหลว', async () => {
|
||||
const mockError = new Error('API Error');
|
||||
vi.mocked(reviewTeamService.create).mockRejectedValue(mockError);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useCreateReviewTeam(), { wrapper });
|
||||
await act(async () => {
|
||||
try {
|
||||
await result.current.mutateAsync({ teamName: 'New Team' } as any);
|
||||
} catch {
|
||||
// Expected
|
||||
}
|
||||
});
|
||||
expect(toast.error).toHaveBeenCalledWith('Failed to create Review Team', {
|
||||
description: 'API Error',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('useUpdateReviewTeam', () => {
|
||||
it('ควรปรับปรุงข้อมูลทีมสำเร็จและแสดง toast success', async () => {
|
||||
vi.mocked(reviewTeamService.update).mockResolvedValue({ success: true } as any);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useUpdateReviewTeam(), { wrapper });
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync({ publicId: 'uuid-team-1', data: { teamName: 'Updated Team' } });
|
||||
});
|
||||
expect(reviewTeamService.update).toHaveBeenCalledWith('uuid-team-1', { teamName: 'Updated Team' });
|
||||
expect(toast.success).toHaveBeenCalledWith('Review Team updated');
|
||||
});
|
||||
|
||||
it('ควรแสดง toast error เมื่อปรับปรุงข้อมูลทีมล้มเหลว', async () => {
|
||||
const mockError = new Error('API Error');
|
||||
vi.mocked(reviewTeamService.update).mockRejectedValue(mockError);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useUpdateReviewTeam(), { wrapper });
|
||||
await act(async () => {
|
||||
try {
|
||||
await result.current.mutateAsync({ publicId: 'uuid-team-1', data: { teamName: 'Updated Team' } });
|
||||
} catch {
|
||||
// Expected
|
||||
}
|
||||
});
|
||||
expect(toast.error).toHaveBeenCalledWith('Failed to update Review Team', {
|
||||
description: 'API Error',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('useAddTeamMember', () => {
|
||||
it('ควรเพิ่มสมาชิกเข้าทีมสำเร็จและแสดง toast success', async () => {
|
||||
vi.mocked(reviewTeamService.addMember).mockResolvedValue({ success: true } as any);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useAddTeamMember(), { wrapper });
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync({ teamPublicId: 'uuid-team-1', data: { userPublicId: 'uuid-user-1', role: 'REVIEWER' } as any });
|
||||
});
|
||||
expect(reviewTeamService.addMember).toHaveBeenCalledWith('uuid-team-1', { userPublicId: 'uuid-user-1', role: 'REVIEWER' });
|
||||
expect(toast.success).toHaveBeenCalledWith('Member added to team');
|
||||
});
|
||||
|
||||
it('ควรแสดง toast error เมื่อเพิ่มสมาชิกเข้าทีมล้มเหลว', async () => {
|
||||
const mockError = new Error('API Error');
|
||||
vi.mocked(reviewTeamService.addMember).mockRejectedValue(mockError);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useAddTeamMember(), { wrapper });
|
||||
await act(async () => {
|
||||
try {
|
||||
await result.current.mutateAsync({ teamPublicId: 'uuid-team-1', data: { userPublicId: 'uuid-user-1', role: 'REVIEWER' } as any });
|
||||
} catch {
|
||||
// Expected
|
||||
}
|
||||
});
|
||||
expect(toast.error).toHaveBeenCalledWith('Failed to add member', {
|
||||
description: 'API Error',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('useRemoveTeamMember', () => {
|
||||
it('ควรลบสมาชิกออกจากทีมสำเร็จและแสดง toast success', async () => {
|
||||
vi.mocked(reviewTeamService.removeMember).mockResolvedValue({ success: true } as any);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useRemoveTeamMember(), { wrapper });
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync({ teamPublicId: 'uuid-team-1', memberPublicId: 'uuid-member-1' });
|
||||
});
|
||||
expect(reviewTeamService.removeMember).toHaveBeenCalledWith('uuid-team-1', 'uuid-member-1');
|
||||
expect(toast.success).toHaveBeenCalledWith('Member removed from team');
|
||||
});
|
||||
|
||||
it('ควรแสดง toast error เมื่อลบสมาชิกออกจากทีมล้มเหลว', async () => {
|
||||
const mockError = new Error('API Error');
|
||||
vi.mocked(reviewTeamService.removeMember).mockRejectedValue(mockError);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useRemoveTeamMember(), { wrapper });
|
||||
await act(async () => {
|
||||
try {
|
||||
await result.current.mutateAsync({ teamPublicId: 'uuid-team-1', memberPublicId: 'uuid-member-1' });
|
||||
} catch {
|
||||
// Expected
|
||||
}
|
||||
});
|
||||
expect(toast.error).toHaveBeenCalledWith('Failed to remove member', {
|
||||
description: 'API Error',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,62 @@
|
||||
// File: frontend/hooks/__tests__/use-transmittal.test.ts
|
||||
// Change Log:
|
||||
// - 2026-06-13: Initial creation - test coverage for useTransmittal hook
|
||||
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import { createTestQueryClient } from '@/lib/test-utils';
|
||||
import { useTransmittal, transmittalKeys } from '../use-transmittal';
|
||||
import { transmittalService } from '@/lib/services/transmittal.service';
|
||||
|
||||
// Mock service
|
||||
vi.mock('@/lib/services/transmittal.service', () => ({
|
||||
transmittalService: {
|
||||
getByUuid: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('useTransmittal hook', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('transmittalKeys', () => {
|
||||
it('ควรสร้าง cache keys ที่ถูกต้อง', () => {
|
||||
expect(transmittalKeys.all).toEqual(['transmittals']);
|
||||
expect(transmittalKeys.detail('uuid-1')).toEqual(['transmittals', 'detail', 'uuid-1']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('useTransmittal', () => {
|
||||
it('ควรดึงรายละเอียด transmittal สำเร็จ', async () => {
|
||||
const mockData = { publicId: 'uuid-1', transmittalNumber: 'TR-001' };
|
||||
vi.mocked(transmittalService.getByUuid).mockResolvedValue(mockData as any);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useTransmittal('uuid-1'), { wrapper });
|
||||
await waitFor(() => {
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
});
|
||||
expect(result.current.transmittal).toEqual(mockData);
|
||||
expect(transmittalService.getByUuid).toHaveBeenCalledWith('uuid-1');
|
||||
});
|
||||
|
||||
it('ควรดึงรายละเอียด transmittal สำเร็จในแบบ wrapped response', async () => {
|
||||
const mockData = { publicId: 'uuid-2', transmittalNumber: 'TR-002' };
|
||||
vi.mocked(transmittalService.getByUuid).mockResolvedValue({ data: mockData } as any);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useTransmittal('uuid-2'), { wrapper });
|
||||
await waitFor(() => {
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
});
|
||||
expect(result.current.transmittal).toEqual(mockData);
|
||||
});
|
||||
|
||||
it('ไม่ควรทำงานเมื่อไม่ระบุ uuid', async () => {
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useTransmittal(undefined), { wrapper });
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
expect(result.current.transmittal).toBeUndefined();
|
||||
expect(transmittalService.getByUuid).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,142 @@
|
||||
// File: frontend/hooks/__tests__/use-workflow-history.test.ts
|
||||
// Change Log:
|
||||
// - 2026-06-13: Initial creation - test coverage for useWorkflowHistory hook
|
||||
// - 2026-06-13: Refactor to use static imports and createTestQueryClient helper
|
||||
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import { useWorkflowHistory, workflowHistoryKeys } from '../use-workflow-history';
|
||||
import { createTestQueryClient } from '@/lib/test-utils';
|
||||
import { workflowEngineService } from '@/lib/services/workflow-engine.service';
|
||||
|
||||
// Mock workflowEngineService
|
||||
vi.mock('@/lib/services/workflow-engine.service', () => ({
|
||||
workflowEngineService: {
|
||||
getHistory: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('useWorkflowHistory', () => {
|
||||
const mockInstanceId = '019505a1-7c3e-7000-8000-abc123def456';
|
||||
const mockHistory = [
|
||||
{
|
||||
id: 1,
|
||||
fromState: 'DFT',
|
||||
toState: 'FAP',
|
||||
action: 'SUBMIT',
|
||||
actorId: 'user-uuid',
|
||||
actorName: 'Test User',
|
||||
timestamp: '2026-01-01T00:00:00Z',
|
||||
comments: 'Submitted for approval',
|
||||
},
|
||||
];
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should return workflow history data', async () => {
|
||||
(workflowEngineService.getHistory as any).mockResolvedValue(mockHistory);
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useWorkflowHistory(mockInstanceId), { wrapper });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.isSuccess).toBe(true);
|
||||
});
|
||||
|
||||
expect(result.current.data).toEqual(mockHistory);
|
||||
expect(workflowEngineService.getHistory).toHaveBeenCalledWith(mockInstanceId);
|
||||
});
|
||||
|
||||
it('should be disabled when instanceId is undefined', () => {
|
||||
(workflowEngineService.getHistory as any).mockResolvedValue(mockHistory);
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useWorkflowHistory(undefined), { wrapper });
|
||||
|
||||
expect(result.current.fetchStatus).toBe('idle');
|
||||
expect(workflowEngineService.getHistory).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should be disabled when instanceId is empty string', () => {
|
||||
(workflowEngineService.getHistory as any).mockResolvedValue(mockHistory);
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useWorkflowHistory(''), { wrapper });
|
||||
|
||||
expect(result.current.fetchStatus).toBe('idle');
|
||||
expect(workflowEngineService.getHistory).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should handle fetch error', async () => {
|
||||
(workflowEngineService.getHistory as any).mockRejectedValue(new Error('API Error'));
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useWorkflowHistory(mockInstanceId), { wrapper });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.isError).toBe(true);
|
||||
});
|
||||
|
||||
expect(result.current.error).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should not retry on failure (retry: false)', async () => {
|
||||
(workflowEngineService.getHistory as any).mockRejectedValue(new Error('API Error'));
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useWorkflowHistory(mockInstanceId), { wrapper });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.isError).toBe(true);
|
||||
});
|
||||
|
||||
// Should only be called once (no retry)
|
||||
expect(workflowEngineService.getHistory).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should use correct query key', async () => {
|
||||
(workflowEngineService.getHistory as any).mockResolvedValue(mockHistory);
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
renderHook(() => useWorkflowHistory(mockInstanceId), { wrapper });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(workflowEngineService.getHistory).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
// Verify query key structure
|
||||
const expectedKey = workflowHistoryKeys.instance(mockInstanceId);
|
||||
expect(expectedKey).toEqual(['workflow-history', mockInstanceId]);
|
||||
});
|
||||
|
||||
it('should return empty array when no history exists', async () => {
|
||||
(workflowEngineService.getHistory as any).mockResolvedValue([]);
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useWorkflowHistory(mockInstanceId), { wrapper });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.isSuccess).toBe(true);
|
||||
});
|
||||
|
||||
expect(result.current.data).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle multiple instance IDs correctly', async () => {
|
||||
(workflowEngineService.getHistory as any).mockResolvedValue(mockHistory);
|
||||
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result: result1 } = renderHook(() => useWorkflowHistory('instance-1'), { wrapper });
|
||||
const { result: result2 } = renderHook(() => useWorkflowHistory('instance-2'), { wrapper });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result1.current.isSuccess).toBe(true);
|
||||
expect(result2.current.isSuccess).toBe(true);
|
||||
});
|
||||
|
||||
expect(workflowEngineService.getHistory).toHaveBeenCalledWith('instance-1');
|
||||
expect(workflowEngineService.getHistory).toHaveBeenCalledWith('instance-2');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,152 @@
|
||||
// File: frontend/hooks/__tests__/use-workflows.test.ts
|
||||
// Change Log:
|
||||
// - 2026-06-13: Initial creation - test coverage for use-workflows hooks
|
||||
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { renderHook, waitFor, act } from '@testing-library/react';
|
||||
import { createTestQueryClient } from '@/lib/test-utils';
|
||||
import {
|
||||
useWorkflowDefinitions,
|
||||
useWorkflowDefinition,
|
||||
useCreateWorkflowDefinition,
|
||||
useUpdateWorkflowDefinition,
|
||||
useDeleteWorkflowDefinition,
|
||||
useEvaluateWorkflow,
|
||||
useGetAvailableActions,
|
||||
useValidateDsl,
|
||||
workflowKeys,
|
||||
} from '../use-workflows';
|
||||
import { workflowEngineService } from '@/lib/services/workflow-engine.service';
|
||||
|
||||
// Mock services
|
||||
vi.mock('@/lib/services/workflow-engine.service', () => ({
|
||||
workflowEngineService: {
|
||||
getDefinitions: vi.fn(),
|
||||
getDefinitionById: vi.fn(),
|
||||
createDefinition: vi.fn(),
|
||||
updateDefinition: vi.fn(),
|
||||
deleteDefinition: vi.fn(),
|
||||
evaluate: vi.fn(),
|
||||
getAvailableActions: vi.fn(),
|
||||
validateDsl: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('use-workflows hooks', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('workflowKeys', () => {
|
||||
it('ควรสร้าง cache keys ที่ถูกต้อง', () => {
|
||||
expect(workflowKeys.all).toEqual(['workflows']);
|
||||
expect(workflowKeys.definitions()).toEqual(['workflows', 'definitions']);
|
||||
expect(workflowKeys.definition('uuid-1')).toEqual(['workflows', 'definitions', 'uuid-1']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('useWorkflowDefinitions', () => {
|
||||
it('ควรดึงข้อมูล definitions สำเร็จ', async () => {
|
||||
const mockData = [{ publicId: 'uuid-1', workflowName: 'Workflow A' }];
|
||||
vi.mocked(workflowEngineService.getDefinitions).mockResolvedValue(mockData as any);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useWorkflowDefinitions(), { wrapper });
|
||||
await waitFor(() => {
|
||||
expect(result.current.isSuccess).toBe(true);
|
||||
});
|
||||
expect(result.current.data).toEqual(mockData);
|
||||
expect(workflowEngineService.getDefinitions).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('useWorkflowDefinition', () => {
|
||||
it('ควรดึงข้อมูล definition ตาม id สำเร็จ', async () => {
|
||||
const mockData = { publicId: 'uuid-1', workflowName: 'Workflow A' };
|
||||
vi.mocked(workflowEngineService.getDefinitionById).mockResolvedValue(mockData as any);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useWorkflowDefinition('uuid-1'), { wrapper });
|
||||
await waitFor(() => {
|
||||
expect(result.current.isSuccess).toBe(true);
|
||||
});
|
||||
expect(result.current.data).toEqual(mockData);
|
||||
expect(workflowEngineService.getDefinitionById).toHaveBeenCalledWith('uuid-1');
|
||||
});
|
||||
});
|
||||
|
||||
describe('useCreateWorkflowDefinition', () => {
|
||||
it('ควรสร้าง workflow definition สำเร็จ', async () => {
|
||||
const mockResponse = { publicId: 'uuid-1', workflowName: 'Workflow A' };
|
||||
vi.mocked(workflowEngineService.createDefinition).mockResolvedValue(mockResponse as any);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useCreateWorkflowDefinition(), { wrapper });
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync({ workflowName: 'Workflow A', dslDefinition: {} } as any);
|
||||
});
|
||||
expect(workflowEngineService.createDefinition).toHaveBeenCalledWith({ workflowName: 'Workflow A', dslDefinition: {} });
|
||||
});
|
||||
});
|
||||
|
||||
describe('useUpdateWorkflowDefinition', () => {
|
||||
it('ควรแก้ไข workflow definition สำเร็จ', async () => {
|
||||
const mockResponse = { publicId: 'uuid-1', workflowName: 'Workflow A Updated' };
|
||||
vi.mocked(workflowEngineService.updateDefinition).mockResolvedValue(mockResponse as any);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useUpdateWorkflowDefinition(), { wrapper });
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync({ id: 'uuid-1', data: { workflowName: 'Workflow A Updated' } as any });
|
||||
});
|
||||
expect(workflowEngineService.updateDefinition).toHaveBeenCalledWith('uuid-1', { workflowName: 'Workflow A Updated' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('useDeleteWorkflowDefinition', () => {
|
||||
it('ควรลบ workflow definition สำเร็จ', async () => {
|
||||
vi.mocked(workflowEngineService.deleteDefinition).mockResolvedValue({} as any);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useDeleteWorkflowDefinition(), { wrapper });
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync('uuid-1');
|
||||
});
|
||||
expect(workflowEngineService.deleteDefinition).toHaveBeenCalledWith('uuid-1');
|
||||
});
|
||||
});
|
||||
|
||||
describe('useEvaluateWorkflow', () => {
|
||||
it('ควรประเมิน workflow สำเร็จ', async () => {
|
||||
const mockResponse = { status: 'APPROVED' };
|
||||
vi.mocked(workflowEngineService.evaluate).mockResolvedValue(mockResponse as any);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useEvaluateWorkflow(), { wrapper });
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync({ workflowUuid: 'uuid-1', action: 'APPROVE' } as any);
|
||||
});
|
||||
expect(workflowEngineService.evaluate).toHaveBeenCalledWith({ workflowUuid: 'uuid-1', action: 'APPROVE' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('useGetAvailableActions', () => {
|
||||
it('ควรดึง actions ที่ใช้งานได้สำเร็จ', async () => {
|
||||
const mockResponse = ['APPROVE', 'REJECT'];
|
||||
vi.mocked(workflowEngineService.getAvailableActions).mockResolvedValue(mockResponse as any);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useGetAvailableActions(), { wrapper });
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync({ workflowUuid: 'uuid-1' } as any);
|
||||
});
|
||||
expect(workflowEngineService.getAvailableActions).toHaveBeenCalledWith({ workflowUuid: 'uuid-1' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('useValidateDsl', () => {
|
||||
it('ควรตรวจสอบความถูกต้องของ DSL สำเร็จ', async () => {
|
||||
const mockResponse = { valid: true };
|
||||
vi.mocked(workflowEngineService.validateDsl).mockResolvedValue(mockResponse as any);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useValidateDsl(), { wrapper });
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync({ steps: [] });
|
||||
});
|
||||
expect(workflowEngineService.validateDsl).toHaveBeenCalledWith({ steps: [] });
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user