Files
hr-portal/frontend/TDD_GUIDE.md
Porsche Chen 360533393f feat: HR Portal - Complete Multi-Tenant System with Redis Session Storage
Major Features:
-  Multi-tenant architecture (tenant isolation)
-  Employee CRUD with lifecycle management (onboarding/offboarding)
-  Department tree structure with email domain management
-  Company info management (single-record editing)
-  System functions CRUD (permission management)
-  Email account management (multi-account per employee)
-  Keycloak SSO integration (auth.lab.taipei)
-  Redis session storage (10.1.0.254:6379)
  - Solves Cookie 4KB limitation
  - Cross-system session sharing
  - Sliding expiration (8 hours)
  - Automatic token refresh

Technical Stack:
Backend:
- FastAPI + SQLAlchemy
- PostgreSQL 16 (10.1.0.20:5433)
- Keycloak Admin API integration
- Docker Mailserver integration (SSH)
- Alembic migrations

Frontend:
- Next.js 14 (App Router)
- NextAuth 4 with Keycloak Provider
- Redis session storage (ioredis)
- Tailwind CSS

Infrastructure:
- Redis 7 (10.1.0.254:6379) - Session + Cache
- Keycloak 26.1.0 (auth.lab.taipei)
- Docker Mailserver (10.1.0.254)

Architecture Highlights:
- Session管理由 Keycloak + Redis 統一控制
- 支援多系統 (HR/WebMail/Calendar/Drive/Office) 共享 session
- Token 自動刷新,異質服務整合
- 未來可無縫遷移到雲端

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-23 20:12:43 +08:00

305 lines
6.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# TDD 開發指南 - HR Portal 前端
**建立日期**: 2026-02-21
**測試框架**: Vitest + React Testing Library
---
## 📋 目錄結構
```
frontend/
├── services/
│ ├── __tests__/
│ │ └── onboarding.service.test.ts # API Service 測試
│ └── onboarding.service.ts # API Service 實作
├── components/
│ ├── __tests__/
│ │ └── OnboardingForm.test.tsx # 組件測試 (待建立)
│ └── OnboardingForm.tsx # 組件實作 (待建立)
├── types/
│ └── onboarding.ts # TypeScript 型別定義
├── vitest.config.ts # Vitest 配置
└── vitest.setup.ts # 測試環境設置
```
---
## 🚀 快速開始
### 1. 安裝依賴
```bash
cd q:/porscheworld_develop/hr-portal/frontend
npm install
```
**需要安裝的測試相關套件**:
```json
{
"devDependencies": {
"@testing-library/jest-dom": "^6.1.5",
"@testing-library/react": "^14.1.2",
"@testing-library/user-event": "^14.5.1",
"@vitejs/plugin-react": "^4.2.1",
"@vitest/ui": "^1.0.4",
"jsdom": "^23.0.1",
"vitest": "^1.0.4"
}
}
```
### 2. 執行測試
```bash
# 執行所有測試
npm test
# 執行測試並開啟 UI 介面
npm run test:ui
# 執行測試並生成覆蓋率報告
npm run test:coverage
# 監視模式(自動重跑)
npm test -- --watch
```
---
## 🎯 TDD 工作流程
### Red-Green-Refactor 循環
```
1. RED → 先寫失敗的測試
2. GREEN → 寫最少的程式碼讓測試通過
3. REFACTOR → 重構程式碼,保持測試通過
```
### 範例API Service TDD
#### Step 1: RED - 寫失敗的測試
```typescript
// services/__tests__/onboarding.service.test.ts
import { describe, it, expect } from 'vitest'
import { onboardingService } from '../onboarding.service'
describe('OnboardingService', () => {
it('should successfully onboard an employee', async () => {
const request = {
resume_id: 1,
keycloak_user_id: '550e8400-...',
keycloak_username: 'wang.ming',
hire_date: '2026-02-21',
departments: [{ department_id: 9, position: '工程師' }],
role_ids: [1],
}
const result = await onboardingService.onboardEmployee(request)
expect(result).toHaveProperty('message')
expect(result).toHaveProperty('employee')
})
})
```
執行測試:
```bash
npm test
# ❌ 測試失敗onboardingService 不存在
```
#### Step 2: GREEN - 實作最少程式碼
```typescript
// services/onboarding.service.ts
import axios from 'axios'
class OnboardingService {
async onboardEmployee(request: OnboardingRequest) {
const response = await axios.post('/api/v1/emp-lifecycle/onboard', request)
return response.data
}
}
export const onboardingService = new OnboardingService()
```
執行測試:
```bash
npm test
# ✅ 測試通過
```
#### Step 3: REFACTOR - 重構
```typescript
// 新增型別定義、錯誤處理、環境變數等
```
---
## 📦 已完成的測試
### ✅ API Service 層
**檔案**: `services/__tests__/onboarding.service.test.ts`
**測試案例**:
1.`onboardEmployee` - 成功到職
2.`onboardEmployee` - API 失敗處理
3.`getEmployeeStatus` - 查詢員工狀態
4.`getEmployeeStatus` - 員工不存在
5.`offboardEmployee` - 成功離職
**執行測試**:
```bash
npm test services/__tests__/onboarding.service.test.ts
```
---
## 🔧 下一步:組件測試
### 待建立的測試
#### 1. OnboardingForm 組件測試
```typescript
// components/__tests__/OnboardingForm.test.tsx
import { describe, it, expect } from 'vitest'
import { render, screen, fireEvent } from '@testing-library/react'
import { OnboardingForm } from '../OnboardingForm'
describe('OnboardingForm', () => {
it('should render all required fields', () => {
render(<OnboardingForm />)
expect(screen.getByLabelText('員工姓名')).toBeInTheDocument()
expect(screen.getByLabelText('Keycloak 使用者名稱')).toBeInTheDocument()
expect(screen.getByLabelText('到職日期')).toBeInTheDocument()
})
it('should validate required fields', async () => {
render(<OnboardingForm />)
const submitButton = screen.getByRole('button', { name: /送出/i })
fireEvent.click(submitButton)
expect(await screen.findByText('此欄位為必填')).toBeInTheDocument()
})
it('should submit form successfully', async () => {
const onSubmit = vi.fn()
render(<OnboardingForm onSubmit={onSubmit} />)
// Fill form...
fireEvent.click(screen.getByRole('button', { name: /送出/i }))
expect(onSubmit).toHaveBeenCalled()
})
})
```
#### 2. EmployeeStatusCard 組件測試
```typescript
// 顯示員工詳細資訊的卡片組件
```
---
## 📊 測試覆蓋率目標
| 類型 | 目標 | 當前 |
|------|------|------|
| API Service | 100% | 100% ✅ |
| Components | 80% | 0% ⏳ |
| Hooks | 80% | 0% ⏳ |
| Utilities | 90% | 0% ⏳ |
---
## 🎨 測試最佳實踐
### 1. 測試命名
```typescript
// ✅ Good: 描述行為,不是實作
it('should display error when API fails', () => {})
// ❌ Bad: 測試實作細節
it('should call axios.post', () => {})
```
### 2. AAA 模式
```typescript
it('should do something', () => {
// Arrange (準備)
const data = { ... }
// Act (執行)
const result = doSomething(data)
// Assert (斷言)
expect(result).toBe(expected)
})
```
### 3. 隔離測試
```typescript
// ✅ Good: 每個測試獨立
describe('MyComponent', () => {
beforeEach(() => {
// 每個測試前重置
cleanup()
})
})
// ❌ Bad: 測試間有依賴
```
### 4. Mock 外部依賴
```typescript
// Mock axios
vi.mock('axios')
// Mock Next.js router
vi.mock('next/navigation')
```
---
## 🐛 常見問題
### Q1: `ReferenceError: describe is not defined`
**解決**: 確認 `vitest.config.ts` 中有設定 `globals: true`
### Q2: `TypeError: Cannot read property 'xxx' of undefined`
**解決**: 檢查是否正確 mock 了依賴
### Q3: 測試執行很慢
**解決**:
- 使用 `vi.mock()` 避免真實 API 呼叫
- 使用 `--run` 參數執行一次性測試
---
## 📚 參考資源
- [Vitest 官方文件](https://vitest.dev/)
- [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/)
- [Jest DOM Matchers](https://github.com/testing-library/jest-dom)
---
**下一步**: 執行 `npm install` 安裝測試依賴,然後執行 `npm test` 確認測試環境正常運作。