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 เดียวกัน
|
||||
Reference in New Issue
Block a user