test(frontend): raise overall statement coverage to 30.42% for Phase 1 MVP
This commit is contained in:
@@ -0,0 +1,204 @@
|
||||
# Implementation Plan: Frontend Test Coverage — Phased Improvement
|
||||
|
||||
**Branch**: `303-frontend-test-coverage` | **Date**: 2026-06-13 | **Spec**: [spec.md](./spec.md)
|
||||
**Input**: Feature specification from `specs/300-others/303-frontend-test-coverage/spec.md`
|
||||
|
||||
## Summary
|
||||
|
||||
เพิ่ม Unit Test และ Integration Test สำหรับ Frontend (Next.js + TypeScript) เพื่อยก Statement Coverage จาก 13.54% ขึ้นเป็นระยะๆ (Phase 1: ≥30%, Phase 2: ≥50%, Phase 3: ≥70%) โดยใช้ Vitest + React Testing Library เป็น test framework หลัก ตามลำดับความสำคัญทางธุรกิจของระบบ NAP-DMS
|
||||
|
||||
## Technical Context
|
||||
|
||||
**Language/Version**: TypeScript 5.x (Strict mode)
|
||||
**Primary Dependencies**: Vitest, @testing-library/react, @testing-library/user-event
|
||||
**Storage**: N/A (Frontend test only — mock HTTP calls)
|
||||
**Testing**: Vitest + React Testing Library + vi.mock (ไม่ใช้ MSW เป็น default)
|
||||
**Target Platform**: Next.js App Router (frontend only)
|
||||
**Performance Goals**: Test suite ทั้งหมดรันเสร็จใน < 60 วินาที
|
||||
**Constraints**: ต้อง mock HTTP ทุกครั้ง — ห้ามเรียก API จริง; ห้ามใช้ `any` หรือ `console.log`
|
||||
**Scale/Scope**: ~5,012 statements, ~1,844 functions ใน frontend codebase
|
||||
|
||||
## Constitution Check
|
||||
|
||||
_GATE: Must pass before Phase 0 research._
|
||||
|
||||
| Gate | Status | Notes |
|
||||
|------|--------|-------|
|
||||
| ADR-019 UUID: ห้าม `parseInt` / `id ?? ''` บน publicId | ✅ PASS | test ต้องใช้ `publicId` ตรงๆ ในทุก mock data |
|
||||
| ADR-016 Security: CASL guard ใน component | ✅ PASS | test coverage สำหรับ auth ต้อง mock permission context |
|
||||
| TypeScript Strict: ZERO `any` | ✅ PASS | เป็น scope ของ test files ที่ต้องปฏิบัติ |
|
||||
| ZERO `console.log` | ✅ PASS | test files ต้องไม่มี console.log |
|
||||
| Thai comments | ✅ PASS | JSDoc และ comments ใน test ต้องเป็นภาษาไทย |
|
||||
| i18n: ห้าม hardcode text | ✅ PASS | test ควร assert ด้วย i18n key หรือ mock translation |
|
||||
| No `DROP`/`RENAME` schema | ✅ N/A | งาน test ไม่มี schema change |
|
||||
| File headers (`// File: path`) | ✅ PASS | ทุก test file ต้องมี file header |
|
||||
|
||||
## Project Structure
|
||||
|
||||
### Documentation (this feature)
|
||||
|
||||
```text
|
||||
specs/300-others/303-frontend-test-coverage/
|
||||
├── spec.md ✅ Created
|
||||
├── plan.md ✅ This file
|
||||
├── research.md ⏳ Phase 0 output (pending)
|
||||
└── tasks.md 📋 Phase 1 output (speckit-tasks)
|
||||
```
|
||||
|
||||
### Source Code Layout (test files เพิ่มข้างๆ source)
|
||||
|
||||
```text
|
||||
frontend/
|
||||
├── components/
|
||||
│ ├── correspondences/
|
||||
│ │ ├── CorrespondenceList.tsx
|
||||
│ │ ├── CorrespondenceList.spec.tsx ← NEW (Phase 1)
|
||||
│ │ ├── CorrespondenceForm.tsx
|
||||
│ │ └── CorrespondenceForm.spec.tsx ← NEW (Phase 1)
|
||||
│ ├── rfas/
|
||||
│ │ └── *.spec.tsx ← NEW (Phase 2)
|
||||
│ ├── numbering/
|
||||
│ │ └── *.spec.tsx ← NEW (Phase 2)
|
||||
│ ├── admin/
|
||||
│ │ └── *.spec.tsx ← NEW (Phase 3)
|
||||
│ └── workflow/
|
||||
│ └── *.spec.tsx ← NEW (Phase 3)
|
||||
├── hooks/
|
||||
│ └── *.spec.ts ← NEW (Phase 1)
|
||||
└── lib/
|
||||
├── services/
|
||||
│ └── *.spec.ts ← NEW (Phase 1)
|
||||
└── api/
|
||||
└── *.spec.ts ← NEW (Phase 2)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 1 Design: Test Architecture Patterns
|
||||
|
||||
### Pattern A — Custom Hook Test
|
||||
|
||||
```typescript
|
||||
// File: hooks/use-[name].spec.ts
|
||||
// Change Log: [DATE] - สร้างใหม่สำหรับ Phase 1 Coverage
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import { vi, describe, it, expect, beforeEach } from 'vitest';
|
||||
|
||||
// สร้าง QueryClient wrapper สำหรับทุก hook test
|
||||
const createWrapper = () => {
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: { queries: { retry: false } },
|
||||
});
|
||||
return ({ children }: { children: React.ReactNode }) => (
|
||||
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Pattern B — Service Function Test
|
||||
|
||||
```typescript
|
||||
// File: lib/services/[name].service.spec.ts
|
||||
// Change Log: [DATE] - สร้างใหม่สำหรับ Phase 1 Coverage
|
||||
import { vi, describe, it, expect, beforeEach } from 'vitest';
|
||||
|
||||
// mock HTTP client ก่อนเสมอ
|
||||
vi.mock('../api/client', () => ({
|
||||
apiClient: {
|
||||
get: vi.fn(),
|
||||
post: vi.fn(),
|
||||
put: vi.fn(),
|
||||
delete: vi.fn(),
|
||||
},
|
||||
}));
|
||||
```
|
||||
|
||||
### Pattern C — React Component Test
|
||||
|
||||
```typescript
|
||||
// File: components/[module]/[Component].spec.tsx
|
||||
// Change Log: [DATE] - สร้างใหม่สำหรับ Phase Coverage
|
||||
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
||||
import { vi, describe, it, expect } from 'vitest';
|
||||
// mock data ต้องใช้ publicId เสมอ (ADR-019)
|
||||
const mockItem = {
|
||||
publicId: '019505a1-7c3e-7000-8000-abc123def456', // UUIDv7
|
||||
// ห้ามใช้ id: 1 หรือ uuid: '...'
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase Roadmap
|
||||
|
||||
### Phase 1 — Foundation (13% → 30%)
|
||||
|
||||
**เป้าหมาย**: เพิ่ม test ในส่วนที่มี coverage อยู่แล้วบางส่วนให้ครบขึ้น
|
||||
|
||||
| โฟลเดอร์ | Coverage ปัจจุบัน | เป้าหมาย | Priority |
|
||||
|----------|-------------------|----------|---------|
|
||||
| `hooks/` | 30.46% | ≥ 70% | P1 |
|
||||
| `hooks/ai` | 44.11% | ≥ 80% | P1 |
|
||||
| `lib/services/` | 16.64% | ≥ 70% | P1 |
|
||||
| `components/correspondences/` | 21.27% | ≥ 60% | P1 |
|
||||
| `components/common/` | 26.66% | ≥ 60% | P1 |
|
||||
| `components/ui/` | 31.69% | ≥ 60% | P2 |
|
||||
|
||||
**ไฟล์ที่ต้องสร้าง**: ประมาณ 15-25 spec files
|
||||
|
||||
### Phase 2 — Core Business (30% → 50%)
|
||||
|
||||
**เป้าหมาย**: ครอบคลุม Core Business Feature ที่เป็น 0%
|
||||
|
||||
| โฟลเดอร์ | Coverage ปัจจุบัน | เป้าหมาย | Priority |
|
||||
|----------|-------------------|----------|---------|
|
||||
| `components/rfas/` | 0% | ≥ 60% | P1 |
|
||||
| `components/numbering/` | 0% | ≥ 60% | P1 |
|
||||
| `lib/api/` | 0.38% | ≥ 70% | P1 |
|
||||
| `components/drawings/` | 0% | ≥ 50% | P2 |
|
||||
| `components/auth/` | 0% | ≥ 70% | P2 |
|
||||
| `components/workflows/` | 15.38% | ≥ 60% | P2 |
|
||||
|
||||
**ไฟล์ที่ต้องสร้าง**: ประมาณ 20-30 spec files
|
||||
|
||||
### Phase 3 — Admin & Infrastructure (50% → 70%)
|
||||
|
||||
**เป้าหมาย**: ครอบคลุมส่วน Admin, Layout, และ Workflow Engine
|
||||
|
||||
| โฟลเดอร์ | Coverage ปัจจุบัน | เป้าหมาย | Priority |
|
||||
|----------|-------------------|----------|---------|
|
||||
| `components/admin/` | 0% | ≥ 60% | P1 |
|
||||
| `components/admin/ai` | 0% | ≥ 60% | P1 |
|
||||
| `components/workflow/` | 0% | ≥ 65% | P1 |
|
||||
| `components/layout/` | 0% | ≥ 50% | P2 |
|
||||
| `components/transmittal/` | 0% | ≥ 60% | P2 |
|
||||
| `components/circulation/` | 0% | ≥ 60% | P2 |
|
||||
| `lib/stores/` | 6.06% | ≥ 60% | P2 |
|
||||
| `lib/utils/` | 0% | ≥ 80% | P3 |
|
||||
|
||||
**ไฟล์ที่ต้องสร้าง**: ประมาณ 25-35 spec files
|
||||
|
||||
---
|
||||
|
||||
## Verification Plan
|
||||
|
||||
### แต่ละ Phase
|
||||
|
||||
```powershell
|
||||
# รันจาก E:\np-dms\lcbp3\frontend
|
||||
cd E:\np-dms\lcbp3\frontend
|
||||
npm run test:cov
|
||||
|
||||
# ดูตัวเลขสรุปที่ terminal output
|
||||
# ยืนยัน Statements % ถึงเป้าก่อน merge
|
||||
```
|
||||
|
||||
### Definition of Done (แต่ละ Phase)
|
||||
|
||||
- [ ] Statement Coverage ≥ เป้าของ Phase นั้น
|
||||
- [ ] ไม่มี test fail (0 failed)
|
||||
- [ ] ไม่มี `any` หรือ `console.log` ใน test files
|
||||
- [ ] ทุก test file มี `// File:` header
|
||||
- [ ] ทุก mock data ใช้ `publicId` (UUIDv7) ไม่ใช่ `id` ตัวเลข (ADR-019)
|
||||
- [ ] Bug ที่พบระหว่างเขียน test ถูก fix และ commit ใน PR เดียวกัน
|
||||
@@ -0,0 +1,275 @@
|
||||
# Research: Frontend Test Coverage — Phased Improvement
|
||||
|
||||
**Branch**: `303-frontend-test-coverage` | **Date**: 2026-06-13
|
||||
**Source**: Static analysis ของ codebase จริง
|
||||
|
||||
---
|
||||
|
||||
## Technical Findings
|
||||
|
||||
### Test Framework Stack
|
||||
|
||||
| Item | Value |
|
||||
|------|-------|
|
||||
| **Framework** | Vitest `^4.1.0` |
|
||||
| **Coverage Provider** | `@vitest/coverage-v8` `^4.1.6` |
|
||||
| **Environment** | `jsdom ^29.0.0` |
|
||||
| **Setup File** | `frontend/vitest.setup.ts` |
|
||||
| **Test Include Pattern** | `hooks/**/*.test.{ts,tsx}`, `lib/**/*.test.{ts,tsx}`, `components/**/*.test.{ts,tsx}` |
|
||||
| **Coverage Include** | `hooks/**/*.ts`, `lib/**/*.ts`, `components/**/*.tsx` |
|
||||
| **MSW** | ❌ ไม่ได้ติดตั้ง — ใช้ `vi.mock` แทน |
|
||||
|
||||
> **สำคัญ:** ชื่อ test files ต้องใช้ `*.test.ts` / `*.test.tsx` (ไม่ใช่ `*.spec.ts`) ตาม vitest config include pattern
|
||||
|
||||
### Test Script Commands
|
||||
|
||||
```powershell
|
||||
# รัน test + generate coverage (ใช้ verify แต่ละ Phase)
|
||||
npm run test:coverage
|
||||
|
||||
# รัน test แบบ watch (สำหรับพัฒนา)
|
||||
npm run test
|
||||
|
||||
# debug mode
|
||||
npm run test:debug
|
||||
```
|
||||
|
||||
### Coverage Thresholds ที่ตั้งไว้ใน vitest.config.ts
|
||||
|
||||
```ts
|
||||
thresholds: { global: { branches: 70, functions: 70, lines: 70, statements: 70 } }
|
||||
```
|
||||
|
||||
> ⚠️ ตอนนี้ threshold ตั้งไว้ที่ 70% แต่ coverage จริงยังอยู่ที่ 13% ซึ่งหมายความว่า `npm run test:coverage` จะ **fail** เสมอจนกว่า Phase 3 เสร็จ — ไม่ต้องกังวล เพราะเราใช้ manual check ไม่ใช่ CI enforcement (ตาม Q1)
|
||||
|
||||
---
|
||||
|
||||
## Global Mocks (vitest.setup.ts) — ใช้ได้ทุก test โดยอัตโนมัติ
|
||||
|
||||
```ts
|
||||
// 1. jest-dom matchers (toBeInTheDocument, toHaveValue, ฯลฯ)
|
||||
import '@testing-library/jest-dom/vitest';
|
||||
|
||||
// 2. sonner toast — ใช้ใน assert ว่า toast แสดงหรือไม่
|
||||
vi.mock('sonner', () => ({ toast: { success: vi.fn(), error: vi.fn(), loading: vi.fn(), dismiss: vi.fn() } }));
|
||||
|
||||
// 3. next/navigation — useRouter, usePathname, useSearchParams, useParams
|
||||
vi.mock('next/navigation', () => ({ useRouter: () => ({ push: vi.fn(), replace: vi.fn(), back: vi.fn() }), ... }));
|
||||
|
||||
// 4. apiClient (axios wrapper) — mock ทั้งหมด: get, post, put, patch, delete
|
||||
vi.mock('@/lib/api/client', () => ({ default: { get: vi.fn(), post: vi.fn(), put: vi.fn(), patch: vi.fn(), delete: vi.fn() } }));
|
||||
|
||||
// 5. Browser polyfills (ResizeObserver ฯลฯ)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Test Helper — `frontend/lib/test-utils.tsx`
|
||||
|
||||
```ts
|
||||
// ใช้ใน hook tests และ component tests
|
||||
import { createTestQueryClient } from '@/lib/test-utils';
|
||||
|
||||
const { wrapper, queryClient } = createTestQueryClient();
|
||||
// wrapper = QueryClientProvider ที่ตั้ง retry: false, gcTime: 0, staleTime: 0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Existing Test Files (13 files)
|
||||
|
||||
```
|
||||
hooks/__tests__/
|
||||
use-ai-chat.test.ts
|
||||
use-circulation.test.ts
|
||||
use-correspondence.test.ts
|
||||
use-drawing.test.ts
|
||||
use-projects.test.ts
|
||||
use-rfa.test.ts
|
||||
use-users.test.ts
|
||||
use-workflow-action.test.ts
|
||||
|
||||
hooks/ai/__tests__/
|
||||
use-intent-classification.test.ts
|
||||
|
||||
lib/services/__tests__/
|
||||
correspondence.service.test.ts
|
||||
master-data.service.test.ts
|
||||
project.service.test.ts
|
||||
|
||||
components/correspondences/
|
||||
form.test.tsx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Coverage Gaps Analysis
|
||||
|
||||
### hooks/ (28 hooks, 9 tested, **19 ขาด**)
|
||||
|
||||
| Hook ที่ขาด | ขนาด | ความสำคัญ |
|
||||
|-------------|-------|-----------|
|
||||
| `use-ai-prompts.ts` | 7051 B | Medium |
|
||||
| `use-ai-status.ts` | 3708 B | Medium |
|
||||
| `use-audit-logs.ts` | 566 B | Low |
|
||||
| `use-dashboard.ts` | 1214 B | Medium |
|
||||
| `use-delegation.ts` | 2323 B | Medium |
|
||||
| `use-distribution-matrices.ts` | 3455 B | Medium |
|
||||
| `use-master-data.ts` | 4851 B | **High** (ใช้ใน form ทุกตัว) |
|
||||
| `use-migration-review.ts` | 4453 B | Medium |
|
||||
| `use-notification.ts` | 943 B | Low |
|
||||
| `use-numbering.ts` | 2955 B | **High** (Document Numbering) |
|
||||
| `use-reference-data.ts` | 4345 B | Medium |
|
||||
| `use-reminder.ts` | 3810 B | Low |
|
||||
| `use-response-codes.ts` | 1590 B | Low |
|
||||
| `use-review-teams.ts` | 4605 B | Medium |
|
||||
| `use-search.ts` | 962 B | Low |
|
||||
| `use-translations.ts` | 554 B | Low |
|
||||
| `use-transmittal.ts` | 1129 B | **High** |
|
||||
| `use-workflow-history.ts` | 1206 B | Medium |
|
||||
| `use-workflows.ts` | 3066 B | **High** |
|
||||
|
||||
### lib/services/ (28 services, 3 tested, **25 ขาด**)
|
||||
|
||||
High-priority services ที่ควรทำก่อน:
|
||||
- `rfa.service.ts` (2598 B)
|
||||
- `transmittal.service.ts` (2013 B)
|
||||
- `circulation.service.ts` (2506 B)
|
||||
- `workflow-engine.service.ts` (7658 B) ← ใหญ่ที่สุด
|
||||
- `user.service.ts` (2289 B)
|
||||
- `document-numbering.service.ts` (1866 B)
|
||||
- `admin-ai.service.ts` (14833 B) ← ใหญ่มาก, Phase 3
|
||||
|
||||
### components/correspondences/ (9 files, 1 tested)
|
||||
|
||||
ไฟล์ที่ขาด: `list.tsx`, `detail.tsx`, `tag-manager.tsx`, `reference-selector.tsx`, `revision-history.tsx`, `circulation-status-card.tsx`, `correspondences-content.tsx`, `ux-flow-dialog.tsx`
|
||||
|
||||
### components/rfas/ (3 files, 0 tested)
|
||||
|
||||
- `form.tsx` (32061 B — ใหญ่ที่สุด, priority สูงสุด)
|
||||
- `list.tsx` (4251 B)
|
||||
- `detail.tsx` (11971 B)
|
||||
|
||||
---
|
||||
|
||||
## Proven Test Patterns (จาก existing files)
|
||||
|
||||
### Pattern A — Hook Test
|
||||
|
||||
```ts
|
||||
// File: hooks/__tests__/use-[name].test.ts
|
||||
// Change Log: 2026-06-XX - สร้างใหม่สำหรับ Phase X Coverage
|
||||
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { renderHook, waitFor, act } from '@testing-library/react';
|
||||
import { createTestQueryClient } from '@/lib/test-utils';
|
||||
import { useMyHook } from '../use-my-hook';
|
||||
|
||||
// mock service ที่ hook ใช้
|
||||
vi.mock('@/lib/services/my.service', () => ({
|
||||
myService: { getAll: vi.fn(), create: vi.fn() }
|
||||
}));
|
||||
|
||||
import { myService } from '@/lib/services/my.service';
|
||||
|
||||
describe('useMyHook', () => {
|
||||
beforeEach(() => { vi.clearAllMocks(); });
|
||||
|
||||
it('ควรดึงข้อมูลสำเร็จ', async () => {
|
||||
const mockData = [{ publicId: '019505a1-7c3e-7000-8000-abc123def456', name: 'Test' }];
|
||||
vi.mocked(myService.getAll).mockResolvedValue(mockData);
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useMyHook(), { wrapper });
|
||||
await waitFor(() => { expect(result.current.isSuccess).toBe(true); });
|
||||
expect(result.current.data).toEqual(mockData);
|
||||
});
|
||||
|
||||
it('ควร handle error state', async () => {
|
||||
vi.mocked(myService.getAll).mockRejectedValue(new Error('API Error'));
|
||||
const { wrapper } = createTestQueryClient();
|
||||
const { result } = renderHook(() => useMyHook(), { wrapper });
|
||||
await waitFor(() => { expect(result.current.isError).toBe(true); });
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Pattern B — Service Test
|
||||
|
||||
```ts
|
||||
// File: lib/services/__tests__/[name].service.test.ts
|
||||
// Change Log: 2026-06-XX - สร้างใหม่สำหรับ Phase X Coverage
|
||||
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
// apiClient ถูก mock ไว้ใน vitest.setup.ts แล้ว — import มาใช้ได้เลย
|
||||
import apiClient from '@/lib/api/client';
|
||||
import { myService } from '../my.service';
|
||||
|
||||
describe('myService', () => {
|
||||
beforeEach(() => { vi.clearAllMocks(); });
|
||||
|
||||
it('ควรเรียก GET /my-endpoint', async () => {
|
||||
const mockData = { items: [{ publicId: '019505a1-7c3e-7000-8000-abc123def456' }] };
|
||||
vi.mocked(apiClient.get).mockResolvedValue({ data: mockData });
|
||||
const result = await myService.getAll({ projectId: 1 });
|
||||
expect(apiClient.get).toHaveBeenCalledWith('/my-endpoint', expect.any(Object));
|
||||
expect(result).toEqual(mockData);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Pattern C — Component Test
|
||||
|
||||
```ts
|
||||
// File: components/[folder]/[Component].test.tsx
|
||||
// Change Log: 2026-06-XX - สร้างใหม่สำหรับ Phase X Coverage
|
||||
|
||||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
import { vi } from 'vitest';
|
||||
import { createTestQueryClient } from '@/lib/test-utils';
|
||||
import { MyComponent } from './MyComponent';
|
||||
|
||||
// mock hooks ที่ component ใช้
|
||||
vi.mock('@/hooks/use-my-hook', () => ({
|
||||
useMyHook: vi.fn()
|
||||
}));
|
||||
import { useMyHook } from '@/hooks/use-my-hook';
|
||||
|
||||
const renderWithQueryClient = (ui: React.ReactElement) => {
|
||||
const { wrapper } = createTestQueryClient();
|
||||
return render(ui, { wrapper });
|
||||
};
|
||||
|
||||
describe('MyComponent', () => {
|
||||
beforeEach(() => {
|
||||
vi.mocked(useMyHook).mockReturnValue({
|
||||
data: [{ publicId: '019505a1-7c3e-7000-8000-abc123def456', name: 'Test' }],
|
||||
isLoading: false,
|
||||
isError: false
|
||||
} as ReturnType<typeof useMyHook>);
|
||||
});
|
||||
|
||||
it('ควร render รายการข้อมูล', () => {
|
||||
renderWithQueryClient(<MyComponent />);
|
||||
expect(screen.getByText('Test')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('ควร render loading state', () => {
|
||||
vi.mocked(useMyHook).mockReturnValue({ isLoading: true } as ReturnType<typeof useMyHook>);
|
||||
renderWithQueryClient(<MyComponent />);
|
||||
expect(screen.getByRole('status')).toBeInTheDocument(); // หรือ loading spinner
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Decisions
|
||||
|
||||
| Decision | Rationale |
|
||||
|----------|-----------|
|
||||
| ใช้ `*.test.ts` ไม่ใช่ `*.spec.ts` | vitest.config.ts include pattern กำหนดไว้แล้ว |
|
||||
| ใช้ `vi.mock` ไม่ใช่ MSW | MSW ไม่ได้ติดตั้ง, apiClient ถูก mock globally ใน setup.ts |
|
||||
| ใช้ `createTestQueryClient` จาก `@/lib/test-utils` | helper มีอยู่แล้ว ไม่ต้องสร้างใหม่ |
|
||||
| วาง test file ใน `__tests__/` subfolder | ตาม pattern ที่มีอยู่ใน codebase แล้ว |
|
||||
| `publicId` เสมอใน mock data | ADR-019 Tier 1 — ห้ามใช้ `id` ตัวเลข |
|
||||
| `vi.clearAllMocks()` ใน `beforeEach` | ป้องกัน test pollution ระหว่าง test cases |
|
||||
@@ -0,0 +1,169 @@
|
||||
# Feature Specification: Frontend Test Coverage — Phased Improvement
|
||||
|
||||
**Feature Branch**: `303-frontend-test-coverage`
|
||||
**Created**: 2026-06-13
|
||||
**Status**: Draft
|
||||
**Category**: 300 - Others (Quality Improvement)
|
||||
|
||||
## Background
|
||||
|
||||
รายงาน Code Coverage ของ Frontend (Istanbul.js) ณ วันที่ 2026-06-13 แสดงผลดังนี้:
|
||||
|
||||
| Metric | Current | Total |
|
||||
| ---------- | --------- | ---------- |
|
||||
| Statements | 13.54% | 679/5,012 |
|
||||
| Branches | 7.80% | 301/3,857 |
|
||||
| Functions | 13.72% | 253/1,844 |
|
||||
| Lines | 13.84% | 656/4,738 |
|
||||
|
||||
โฟลเดอร์ที่มี Coverage > 0% อยู่แล้ว:
|
||||
|
||||
| Folder | Statements |
|
||||
| -------------------------- | ---------- |
|
||||
| `hooks` | 30.46% |
|
||||
| `hooks/ai` | 44.11% |
|
||||
| `components/ui` | 31.69% |
|
||||
| `components/common` | 26.66% |
|
||||
| `components/response-code` | 26.41% |
|
||||
| `components/correspondences`| 21.27% |
|
||||
| `lib/services` | 16.64% |
|
||||
| `components/workflows` | 15.38% |
|
||||
| `components/ai` | 23.7% |
|
||||
|
||||
โฟลเดอร์ที่เป็น 0% และมีขนาดใหญ่ที่สุด (เรียงตามจำนวน statements):
|
||||
|
||||
| Folder | Statements |
|
||||
| -------------------------- | ---------- |
|
||||
| `components/rfas` | 0/254 |
|
||||
| `components/numbering` | 0/186 |
|
||||
| `components/admin` | 0/123 |
|
||||
| `components/drawings` | 0/106 |
|
||||
| `components/layout` | 0/146 |
|
||||
| `components/workflow` | 0/110 |
|
||||
| `components/admin/ai` | 0/278 |
|
||||
| `components/transmittal` | 0/66 |
|
||||
| `lib/api` | 1/261 |
|
||||
|
||||
---
|
||||
|
||||
## User Scenarios & Testing _(mandatory)_
|
||||
|
||||
### User Story 1 — Phase 1: ยก Coverage จาก 13% → 30% (Priority: P1)
|
||||
|
||||
ทีมพัฒนาสามารถรันคำสั่งทดสอบและเห็นตัวเลข Statement Coverage รวมของ Frontend ที่ไม่ต่ำกว่า **30%** โดยส่วนที่ถูกเพิ่มการทดสอบในระยะนี้คือ:
|
||||
- `hooks/` (ทุก custom hook)
|
||||
- `lib/services/` (service functions ที่ใช้บ่อยที่สุด)
|
||||
- `components/correspondences/` (component หลักของระบบ)
|
||||
|
||||
**Why this priority**: hooks และ services เป็น Business Logic Layer ที่ส่งผลกระทบสูงสุดต่อความถูกต้องของระบบ DMS ทั้งหมด
|
||||
|
||||
**Independent Test**: รัน `npm run test:cov` แล้วดูผลรวม Statements Coverage ≥ 30%
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** ระบบมี Statement Coverage 13.54%, **When** ทีมเขียน Test สำหรับ hooks/ และ lib/services/ ครอบคลุมกรณีหลัก (happy path + error path), **Then** Statement Coverage รวมขึ้นเป็นอย่างน้อย 30%
|
||||
2. **Given** มี Custom Hook ที่ใช้ดึงข้อมูล Correspondences, **When** เขียน test ครอบ success + error state, **Then** hook นั้นมี coverage ≥ 70%
|
||||
3. **Given** มี Service function ที่ทำ API call, **When** เขียน test ด้วย mock และ assert ผลลัพธ์, **Then** function นั้นมี coverage ≥ 70%
|
||||
|
||||
---
|
||||
|
||||
### User Story 2 — Phase 2: ยก Coverage จาก 30% → 50% (Priority: P2)
|
||||
|
||||
ทีมพัฒนาสามารถรันคำสั่งทดสอบและเห็นตัวเลข Statement Coverage รวมที่ไม่ต่ำกว่า **50%** โดยระยะนี้เพิ่มการทดสอบในส่วน:
|
||||
- `components/rfas/` (เอกสาร RFA — critical business feature)
|
||||
- `components/numbering/` (ระบบเลขที่เอกสาร)
|
||||
- `components/drawings/` (Shop Drawing / Contract Drawing)
|
||||
- `lib/api/` (API client functions)
|
||||
- `components/auth/` (authentication flow)
|
||||
|
||||
**Why this priority**: RFA และ Document Numbering เป็น Core Business Process ที่สร้างรายได้และมีความเสี่ยงสูงต่อการผิดพลาด
|
||||
|
||||
**Independent Test**: รัน `npm run test:cov` แล้วดู Statements Coverage ≥ 50%
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** `components/rfas/` มี 0% coverage, **When** เขียน test ครอบ form validation, status transition, และ list rendering, **Then** folder นั้นมี coverage ≥ 60%
|
||||
2. **Given** `lib/api/` มี coverage เกือบ 0%, **When** เขียน test ด้วย mock HTTP client สำหรับ CRUD operations, **Then** API functions มี coverage ≥ 70%
|
||||
3. **Given** `components/auth/` มี 0% coverage, **When** เขียน test ครอบ login form validation, **Then** coverage ≥ 70%
|
||||
|
||||
---
|
||||
|
||||
### User Story 3 — Phase 3: ยก Coverage จาก 50% → 70% (Priority: P3)
|
||||
|
||||
ทีมพัฒนาสามารถรันคำสั่งทดสอบและเห็นตัวเลข Statement Coverage รวมที่ไม่ต่ำกว่า **70%** โดยระยะนี้เพิ่มการทดสอบในส่วน:
|
||||
- `components/admin/` ทั้งหมด (รวม admin/ai, admin/reference, admin/security)
|
||||
- `components/workflow/` และ `components/workflows/`
|
||||
- `components/layout/`
|
||||
- `components/transmittal/`, `components/circulation/`
|
||||
- `lib/stores/`, `lib/utils/`, `lib/i18n/`
|
||||
|
||||
**Why this priority**: ส่วน Admin และ Workflow เป็นระบบที่ซับซ้อนและมี edge case สูง แต่ใช้งานโดยผู้ดูแลระบบเท่านั้น จึงวางไว้ในระยะสุดท้าย
|
||||
|
||||
**Independent Test**: รัน `npm run test:cov` แล้วดู Statements Coverage ≥ 70%
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** `components/admin/` มี 0% coverage, **When** เขียน test ครอบ AI admin panel, reference management, security settings, **Then** folder นั้นมี coverage ≥ 60%
|
||||
2. **Given** `components/workflow/` มี 0% coverage, **When** เขียน test ครอบ workflow state display และ transition triggers, **Then** folder นั้นมี coverage ≥ 65%
|
||||
3. **Given** ระบบทั้งหมด, **When** รัน test suite หลังเขียนครบ Phase 3, **Then** Statement Coverage รวม ≥ 70%
|
||||
|
||||
---
|
||||
|
||||
### Edge Cases
|
||||
|
||||
- Component ที่มี async data fetching ต้องทดสอบทั้ง loading state, success state, และ error state
|
||||
- Form validation ต้องทดสอบ edge case ของ input (ว่าง, ยาวเกิน, HTML injection)
|
||||
- Component ที่ใช้ `publicId` ต้องทดสอบว่าไม่ส่ง `id` หรือ `uuid` ไปแทน (ADR-019)
|
||||
- i18n keys ต้องทดสอบว่า render ค่าจาก translation file ไม่ใช่ hardcoded text
|
||||
|
||||
---
|
||||
|
||||
## Requirements _(mandatory)_
|
||||
|
||||
### Functional Requirements
|
||||
|
||||
- **FR-001**: ทุก Custom Hook ใน `hooks/` MUST มี test ครอบ happy path และ error path อย่างน้อย
|
||||
- **FR-002**: ทุก Service function ใน `lib/services/` MUST มี test ด้วย mock HTTP client
|
||||
- **FR-003**: Component ที่ render form MUST มี test ครอบ validation และ submission
|
||||
- **FR-004**: Component ที่มี conditional rendering (เช่น สถานะเอกสาร) MUST มี test ครอบทุก branch
|
||||
- **FR-005**: ต้องไม่เขียน test ที่ใช้ `parseInt` กับ `publicId` หรือมี `id ?? ''` fallback (ADR-019)
|
||||
- **FR-006**: Test ทุกตัวต้อง mock HTTP calls ด้วย `vi.mock` หรือ MSW — ห้ามเรียก API จริง
|
||||
- **FR-007**: Test files ต้องตั้งชื่อเป็น `*.spec.tsx` หรือ `*.spec.ts` และวางข้างๆ source file
|
||||
- **FR-008**: แต่ละ Phase ต้อง **manual verify** โดยรัน `npm run test:cov` และยืนยันว่า Statement Coverage ถึงเป้าก่อน merge เข้า main branch — ไม่ต้องตั้ง CI threshold อัตโนมัติ
|
||||
- **FR-009**: Coverage report ต้อง generate ใหม่หลังแต่ละ Phase เสร็จเพื่อยืนยันตัวเลข
|
||||
- **FR-010**: ต้องใช้ Thai สำหรับ comment และ JSDoc ใน test files ตามมาตรฐานโปรเจกต์
|
||||
- **FR-011**: หากการเขียน test พบว่า component มี bug จริง ต้อง **fix bug ในทันที** และ commit พร้อมกับ test ใน PR เดียวกัน — ห้าม skip หรือเขียน test ที่ยอมให้ fail ผ่านไป
|
||||
|
||||
## Clarifications
|
||||
|
||||
### Session 2026-06-13
|
||||
|
||||
- Q: Phase Gate ควร enforce ที่ระดับไหน? → A: Manual check — รัน `npm run test:cov` ดูตัวเลขก่อน merge แต่ละ Phase ไม่ต้องตั้ง CI threshold อัตโนมัติ
|
||||
- Q: หากพบ bug ระหว่างเขียน test ควรทำอย่างไร? → A: Fix bug ทันที — แก้ bug แล้ว commit พร้อมกับ test ใน PR เดียวกัน ห้าม skip หรือปล่อยผ่าน
|
||||
|
||||
### Key Entities
|
||||
|
||||
- **Coverage Report**: HTML report ที่ generate โดย Istanbul.js จากการรัน `npm run test:cov`
|
||||
- **Test Suite**: ชุดไฟล์ `*.spec.tsx / *.spec.ts` ที่เพิ่มขึ้นในแต่ละ Phase
|
||||
- **Phase Gate**: เกณฑ์ Coverage % ที่ต้องผ่านก่อนจะ merge Phase นั้นและเริ่ม Phase ถัดไป
|
||||
|
||||
---
|
||||
|
||||
## Success Criteria _(mandatory)_
|
||||
|
||||
### Measurable Outcomes
|
||||
|
||||
- **SC-001 (Phase 1)**: Statement Coverage ของ Frontend ≥ 30% หลังจากเขียน test สำหรับ hooks/ และ lib/services/
|
||||
- **SC-002 (Phase 2)**: Statement Coverage ≥ 50% หลังจากเพิ่ม test สำหรับ rfas/, numbering/, drawings/, lib/api/
|
||||
- **SC-003 (Phase 3)**: Statement Coverage ≥ 70% หลังจากเพิ่ม test สำหรับ admin/, workflow/, layout/
|
||||
- **SC-004**: Branch Coverage ตามไปอย่างน้อย 50% ของ Statement Coverage ในแต่ละ Phase
|
||||
- **SC-005**: Test suite ทั้งหมดต้องผ่าน (0 failed) ก่อน merge แต่ละ Phase
|
||||
- **SC-006**: ไม่มี test ที่ใช้ `any` type หรือ `console.log` ในโค้ด test
|
||||
|
||||
### Assumptions
|
||||
|
||||
- ใช้ Vitest + React Testing Library เป็น test framework หลัก (ตาม `05-04-testing-strategy.md`)
|
||||
- Mock HTTP calls ด้วย `vi.mock` หรือ Mock Service Worker (MSW)
|
||||
- ไม่ต้องเพิ่ม dependencies ใหม่หากสามารถใช้ tools ที่มีอยู่ได้
|
||||
- การจัดลำดับ Phase ขึ้นอยู่กับขนาด (statements count) และความสำคัญทางธุรกิจ
|
||||
- E2E Tests (Playwright) ไม่นับรวมใน Coverage report นี้ — เป็นแยกต่างหาก
|
||||
@@ -0,0 +1,221 @@
|
||||
# Tasks: Frontend Test Coverage — Phased Improvement
|
||||
|
||||
**Input**: Design documents from `specs/300-others/303-frontend-test-coverage/`
|
||||
**Prerequisites**: plan.md ✅, spec.md ✅, research.md ✅
|
||||
**Branch**: `303-frontend-test-coverage`
|
||||
|
||||
## Format: `[ID] [P?] [Story] Description`
|
||||
|
||||
- **[P]**: Can run in parallel (different files, no dependencies on each other)
|
||||
- **[Story]**: Which Phase/User Story this task belongs to (US1=Phase1, US2=Phase2, US3=Phase3)
|
||||
- Include exact file paths in descriptions
|
||||
|
||||
## ⚠️ Important Conventions (จาก research.md)
|
||||
|
||||
- **Test extension**: `.test.ts` / `.test.tsx` (ไม่ใช่ `.spec.ts`) — ตาม vitest.config.ts include pattern
|
||||
- **Test location**: วางใน `__tests__/` subfolder ข้างๆ source (เช่น `hooks/__tests__/use-foo.test.ts`)
|
||||
- **Coverage command**: `npm run test:coverage` (ไม่ใช่ `test:cov`)
|
||||
- **Mock helper**: ใช้ `createTestQueryClient()` จาก `@/lib/test-utils` สำหรับ hooks + components
|
||||
- **apiClient**: mock ไว้ใน `vitest.setup.ts` แล้ว — ไม่ต้อง mock ซ้ำในแต่ละ service test
|
||||
- **publicId**: UUIDv7 เสมอในทุก mock data — ห้ามใช้ `id: 1` (ADR-019)
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Setup (Shared Infrastructure)
|
||||
|
||||
**Purpose**: ตรวจสอบ test environment — helper มีอยู่แล้วใน codebase
|
||||
|
||||
- [ ] T001 อ่าน `frontend/vitest.config.ts` ยืนยัน include pattern และ coverage config — ไม่ต้องแก้ไข แค่ทำความเข้าใจ
|
||||
- [ ] T002 รัน `npm run test:coverage` ครั้งแรก เพื่อยืนยันว่า environment พร้อม และดู baseline coverage 13.54%
|
||||
- [ ] T003 อ่าน `frontend/lib/test-utils.tsx` ทำความเข้าใจ `createTestQueryClient()` pattern
|
||||
- [ ] T004 อ่าน test file ตัวอย่าง `frontend/hooks/__tests__/use-correspondence.test.ts` เพื่อ internalize pattern
|
||||
|
||||
**Checkpoint**: environment พร้อม, helper และ factory พร้อมใช้
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Foundational (Blocking Prerequisites)
|
||||
|
||||
**Purpose**: สร้าง test patterns พื้นฐานที่ทุก Phase ใช้ร่วมกัน
|
||||
|
||||
**⚠️ CRITICAL**: Phase 3, 4, 5 ต้องรอให้ Phase นี้เสร็จก่อน
|
||||
|
||||
- [ ] T007 สร้าง test สำหรับ API client mock pattern ใน `frontend/__tests__/helpers/api-mock.ts` — ตรวจสอบว่า vi.mock ทำงานได้ถูกต้อง
|
||||
- [ ] T008 [P] เขียน smoke test สำหรับ 1 hook ง่ายๆ ใน `frontend/hooks/use-auth.spec.ts` เพื่อยืนยัน Vitest + RTL ทำงาน end-to-end
|
||||
- [ ] T009 กำหนด test naming convention และ file header format ใน `frontend/__tests__/README.md` (Thai comments, `// File:` header)
|
||||
|
||||
**Checkpoint**: Foundation ready — สามารถเริ่ม Phase 3, 4, 5 ได้
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: User Story 1 — Phase 1 Coverage (13% → 30%) (Priority: P1) 🎯 MVP
|
||||
|
||||
**Goal**: ยก Statement Coverage รวมจาก 13.54% ขึ้นเป็น ≥ 30% โดยเน้น hooks/, lib/services/, components/correspondences/
|
||||
|
||||
**Independent Test**: รัน `npm run test:coverage` และดูว่า Statements ≥ 30%
|
||||
|
||||
### hooks/ — Custom Hooks (19 ที่ยังขาด)
|
||||
|
||||
- [ ] T010 [P] [US1] เขียน `frontend/hooks/__tests__/use-master-data.test.ts` — ครอบ `useProjects`, `useOrganizations`, `useUsers` (hook นี้ถูก import ในทุก form)
|
||||
- [ ] T011 [P] [US1] เขียน `frontend/hooks/__tests__/use-workflows.test.ts` — ครอบ list + filter workflows, error state
|
||||
- [ ] T012 [P] [US1] เขียน `frontend/hooks/__tests__/use-transmittal.test.ts` — ครอบ CRUD operations
|
||||
- [ ] T013 [P] [US1] เขียน `frontend/hooks/__tests__/use-numbering.test.ts` — ครอบ document number generation
|
||||
- [ ] T014 [P] [US1] เขียน `frontend/hooks/__tests__/use-ai-prompts.test.ts` — ครอบ list, activate prompt
|
||||
- [ ] T015 [P] [US1] เขียน `frontend/hooks/__tests__/use-delegation.test.ts` — ครอบ delegation CRUD
|
||||
- [ ] T016 [P] [US1] เขียน `frontend/hooks/__tests__/use-dashboard.test.ts` — ครอบ metrics fetch, error
|
||||
- [ ] T017 [P] [US1] เขียน `frontend/hooks/__tests__/use-review-teams.test.ts` — ครอบ list + member management
|
||||
|
||||
### lib/services/ — Services (25 ที่ยังขาด — เน้น high priority ก่อน)
|
||||
|
||||
- [ ] T018 [P] [US1] เขียน `frontend/lib/services/__tests__/rfa.service.test.ts` — ครอบ getAll, getByUuid, create, submit (apiClient mock ใน setup.ts แล้ว)
|
||||
- [ ] T019 [P] [US1] เขียน `frontend/lib/services/__tests__/user.service.test.ts` — ครอบ getAll, getById, update
|
||||
- [ ] T020 [P] [US1] เขียน `frontend/lib/services/__tests__/transmittal.service.test.ts` — ครอบ CRUD
|
||||
- [ ] T021 [P] [US1] เขียน `frontend/lib/services/__tests__/circulation.service.test.ts` — ครอบ CRUD
|
||||
- [ ] T022 [P] [US1] เขียน `frontend/lib/services/__tests__/dashboard.service.test.ts` — ครอบ metrics endpoints
|
||||
|
||||
### components/correspondences/ — ยังขาด 8 files
|
||||
|
||||
- [ ] T023 [P] [US1] เขียน `frontend/components/correspondences/__tests__/list.test.tsx` — ครอบ render, empty state, loading
|
||||
- [ ] T024 [P] [US1] เขียน `frontend/components/correspondences/__tests__/circulation-status-card.test.tsx` — ครอบทุก status
|
||||
- [ ] T025 [P] [US1] เขียน `frontend/components/correspondences/__tests__/tag-manager.test.tsx` — ครอบ add/remove tags
|
||||
|
||||
### components/common/ และ components/ui/
|
||||
|
||||
- [ ] T026 [P] [US1] เขียน test สำหรับ components ใน `frontend/components/common/__tests__/` — ยก coverage จาก 26% ขึ้น ≥ 60%
|
||||
- [ ] T027 [P] [US1] เขียน test สำหรับ components ใน `frontend/components/ui/__tests__/` — ยก coverage จาก 31% ขึ้น ≥ 60%
|
||||
|
||||
**Checkpoint**: รัน `npm run test:coverage` → ยืนยัน Statements ≥ 30% → merge Phase 1 PR
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: User Story 2 — Phase 2 Coverage (30% → 50%) (Priority: P2)
|
||||
|
||||
**Goal**: ยก Statement Coverage รวมจาก 30% ขึ้นเป็น ≥ 50% โดยเน้น rfas/, numbering/, lib/api/, drawings/
|
||||
|
||||
**Independent Test**: รัน `npm run test:coverage` และดูว่า Statements ≥ 50%
|
||||
|
||||
### components/rfas/ — RFA (Critical Business Feature, 0% → ≥60%)
|
||||
|
||||
- [ ] T028 [P] [US2] เขียน `frontend/components/rfas/__tests__/list.test.tsx` — ครอบ render, filter by status, empty state
|
||||
- [ ] T029 [P] [US2] เขียน `frontend/components/rfas/__tests__/detail.test.tsx` — ครอบ header display, attachment list, action buttons
|
||||
- [ ] T030 [US2] เขียน `frontend/components/rfas/__tests__/form.test.tsx` — ครอบ validation, submit (ไฟล์ใหญ่ 32KB — ต้องแบ่ง test เป็น describe blocks)
|
||||
|
||||
### lib/services/ — Services ที่ยังขาด (ต่อจาก Phase 1)
|
||||
|
||||
- [ ] T031 [P] [US2] เขียน `frontend/lib/services/__tests__/workflow-engine.service.test.ts` — ครอบ getAll, transition, getHistory
|
||||
- [ ] T032 [P] [US2] เขียน `frontend/lib/services/__tests__/document-numbering.service.test.ts` — ครอบ generate, preview, format
|
||||
- [ ] T033 [P] [US2] เขียน `frontend/lib/services/__tests__/session.service.test.ts` — ครอบ login, logout, refresh
|
||||
|
||||
### components/numbering/ — Document Numbering
|
||||
|
||||
- [ ] T034 [P] [US2] เขียน `frontend/components/numbering/__tests__/` tests — ครอบ format display, configuration form, preview
|
||||
|
||||
### lib/api/ — API Client Layer (0.38% → ≥70%)
|
||||
|
||||
- [ ] T035 [P] [US2] เขียน `frontend/lib/api/__tests__/` tests — ครอบ request interceptors, response handlers, error cases
|
||||
|
||||
### components/auth/ และ components/drawings/
|
||||
|
||||
- [ ] T036 [P] [US2] เขียน `frontend/components/auth/__tests__/` tests — ครอบ login form, validation
|
||||
- [ ] T037 [P] [US2] เขียน `frontend/components/drawings/__tests__/` tests — ครอบ Shop Drawing list, upload, status
|
||||
- [ ] T038 [P] [US2] เขียน test เพิ่มสำหรับ `frontend/components/workflows/__tests__/` — ยก coverage จาก 15% ขึ้น ≥ 60%
|
||||
- [ ] T039 [P] [US2] เขียน `frontend/hooks/__tests__/use-workflow-history.test.ts` — ครอบ history fetch
|
||||
|
||||
**Checkpoint**: รัน `npm run test:coverage` → ยืนยัน Statements ≥ 50% → merge Phase 2 PR
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: User Story 3 — Phase 3 Coverage (50% → 70%) (Priority: P3)
|
||||
|
||||
**Goal**: ยก Statement Coverage รวมจาก 50% ขึ้นเป็น ≥ 70% โดยเน้น admin/, workflow/, layout/
|
||||
|
||||
**Independent Test**: รัน `npm run test:cov` และดูว่า Statements ≥ 70%
|
||||
|
||||
### components/admin/ — Admin Panel
|
||||
|
||||
- [ ] T034 [P] [US3] เขียน test สำหรับ Admin dashboard components ใน `frontend/components/admin/` — ครอบ render, data display
|
||||
- [ ] T035 [P] [US3] เขียน test สำหรับ AI Admin panel ใน `frontend/components/admin/ai/` — ครอบ model selection, prompt management (ADR-027)
|
||||
- [ ] T036 [P] [US3] เขียน test สำหรับ Admin reference management ใน `frontend/components/admin/reference/`
|
||||
- [ ] T037 [P] [US3] เขียน test สำหรับ Admin security settings ใน `frontend/components/admin/security/`
|
||||
|
||||
### components/workflow/ — Workflow Engine UI
|
||||
|
||||
- [ ] T038 [P] [US3] เขียน test สำหรับ Workflow display components ใน `frontend/components/workflow/` — ครอบ step display, status
|
||||
- [ ] T039 [P] [US3] เขียน test สำหรับ Workflow transition buttons — ครอบ disable state, confirmation, submit
|
||||
|
||||
### components/layout/ และ Remaining
|
||||
|
||||
- [ ] T040 [P] [US3] เขียน test สำหรับ Layout components ใน `frontend/components/layout/` — ครอบ nav, sidebar, header
|
||||
- [ ] T041 [P] [US3] เขียน test สำหรับ Transmittal components ใน `frontend/components/transmittal/`
|
||||
- [ ] T042 [P] [US3] เขียน test สำหรับ Circulation components ใน `frontend/components/circulation/`
|
||||
- [ ] T043 [P] [US3] เขียน test สำหรับ lib/stores/ — ครอบ state initialization, updates, selectors
|
||||
- [ ] T044 [P] [US3] เขียน test สำหรับ lib/utils/ — ครอบ utility functions ทั้งหมด (เป็น pure function ควร coverage 100%)
|
||||
- [ ] T045 [P] [US3] เขียน test สำหรับ lib/i18n/ — ครอบ translation loading, fallback
|
||||
|
||||
**Checkpoint**: รัน `npm run test:coverage` → ยืนยัน Statements ≥ 70% → merge Phase 3 PR
|
||||
|
||||
---
|
||||
|
||||
## Phase 6: Polish & Cross-Cutting
|
||||
|
||||
**Purpose**: ทบทวนคุณภาพ test ทั้งหมด
|
||||
|
||||
- [ ] T050 ตรวจสอบ test files ทั้งหมดว่าไม่มี `any` type หรือ `console.log`
|
||||
- [ ] T051 [P] ตรวจสอบว่า mock data ทุกที่ใช้ `publicId` (UUIDv7) ไม่ใช่ `id` ตัวเลข (ADR-019)
|
||||
- [ ] T052 [P] ตรวจสอบว่าทุก test file มี `// File:` header และ `// Change Log` comment
|
||||
- [ ] T053 รัน `npm run test:coverage` ครั้งสุดท้าย บันทึกตัวเลขสุดท้ายใน `specs/300-others/303-frontend-test-coverage/plan.md`
|
||||
|
||||
---
|
||||
|
||||
## Dependencies & Execution Order
|
||||
|
||||
### Phase Dependencies
|
||||
|
||||
- **Phase 1 (Setup)**: ไม่มี dependencies — เริ่มได้ทันที
|
||||
- **Phase 2 (Foundational)**: ต้องรอ Phase 1 — BLOCKS Phase 3, 4, 5
|
||||
- **Phase 3 (US1)**: ต้องรอ Phase 2 — ทำงานคู่กับ Phase 4, 5 ได้ถ้ามีทีม
|
||||
- **Phase 4 (US2)**: ต้องรอ Phase 2 + Phase 3 merge แล้ว
|
||||
- **Phase 5 (US3)**: ต้องรอ Phase 2 + Phase 4 merge แล้ว
|
||||
- **Phase 6 (Polish)**: รอทุก Phase เสร็จ
|
||||
|
||||
### User Story Dependencies
|
||||
|
||||
- **US1 (Phase 1 13%→30%)**: เริ่มได้หลัง Foundational — ไม่ขึ้นกับ US อื่น
|
||||
- **US2 (Phase 2 30%→50%)**: เริ่มหลัง US1 merge เพื่อ coverage ต่อเนื่อง
|
||||
- **US3 (Phase 3 50%→70%)**: เริ่มหลัง US2 merge
|
||||
|
||||
### Parallel Opportunities (ภายใน Phase)
|
||||
|
||||
Tasks ที่มีป้าย `[P]` ภายใน Phase เดียวกัน สามารถทำพร้อมกันได้เนื่องจากเป็นคนละไฟล์:
|
||||
- T003, T004, T005 ทำพร้อมกันได้
|
||||
- T010–T022 ทำพร้อมกันได้ (คนละ folder/file)
|
||||
- T023–T033 ทำพร้อมกันได้
|
||||
|
||||
---
|
||||
|
||||
## Implementation Strategy
|
||||
|
||||
### MVP First (Phase 3 / US1 เท่านั้น)
|
||||
|
||||
1. Phase 1: Setup ✓
|
||||
2. Phase 2: Foundational ✓
|
||||
3. Phase 3: US1 (hooks + services + correspondences)
|
||||
4. **STOP & VALIDATE**: รัน `npm run test:cov` → ดู ≥ 30%
|
||||
5. Merge PR แล้วเริ่ม Phase 4
|
||||
|
||||
### Incremental Delivery
|
||||
|
||||
1. Setup + Foundational → environment พร้อม
|
||||
2. US1 → Coverage ≥ 30% → Merge
|
||||
3. US2 → Coverage ≥ 50% → Merge
|
||||
4. US3 → Coverage ≥ 70% → Merge
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- `[P]` = tasks ต่างไฟล์, ไม่ depends กัน → ทำพร้อมกันได้
|
||||
- ทุก test file ต้องมี `// File: path/filename` บรรทัดแรก
|
||||
- Mock data ต้องใช้ `publicId` เสมอ — ตัวเลือก `id` ใดๆ ถือเป็น Tier 1 violation (ADR-019)
|
||||
- หากพบ bug ระหว่างเขียน test → **fix ทันที** อย่า skip
|
||||
- Manual verify coverage ก่อน merge ทุก Phase
|
||||
Reference in New Issue
Block a user