feat: 增加测试覆盖率并优化代码质量
test: 添加单元测试和端到端测试 refactor: 重构登录页面和上传模块 ci: 更新测试覆盖率阈值至42% build: 添加测试相关依赖 docs: 更新测试文档 style: 修复代码格式问题
This commit is contained in:
@@ -0,0 +1,947 @@
|
||||
# 全模块测试覆盖实施计划
|
||||
|
||||
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||||
|
||||
**Goal:** 补充所有未覆盖模块的测试,将测试覆盖率从42.57%提升到70%
|
||||
|
||||
**Architecture:** 采用TDD方法,优先补充核心业务模块测试,确保所有页面和API功能正常
|
||||
|
||||
**Tech Stack:** Jest + React Testing Library + TypeScript
|
||||
|
||||
---
|
||||
|
||||
## 📊 当前状态
|
||||
|
||||
**当前覆盖率:** 42.57%
|
||||
|
||||
**未覆盖模块(0%):**
|
||||
- 页面详情页:cases/[id], news/[slug], products/[id], services/[id], solutions
|
||||
- 管理后台:admin主页, content/[id], logs
|
||||
- 管理后台API:config, content/[id], logs, upload, users/[id]
|
||||
- 其他页面:privacy, terms, preview/effects
|
||||
- 组件:admin, analytics, effects, examples, seo
|
||||
|
||||
---
|
||||
|
||||
## 🎯 目标
|
||||
|
||||
**覆盖率目标:** 70%
|
||||
|
||||
**任务总数:** 25个
|
||||
|
||||
**预计时间:** 2-3周
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: 页面详情页测试(提升到55%)
|
||||
|
||||
### Task 1: 案例详情页测试
|
||||
|
||||
**Files:**
|
||||
- Create: `src/app/(marketing)/cases/[id]/page.test.tsx`
|
||||
- Test: `src/app/(marketing)/cases/[id]/page.tsx`
|
||||
|
||||
**Step 1: 查看案例详情页源码**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
cat src/app/\(marketing\)/cases/\[id\]/page.tsx | head -100
|
||||
```
|
||||
|
||||
Expected: 显示案例详情页结构和功能
|
||||
|
||||
**Step 2: 编写案例详情页测试**
|
||||
|
||||
Create: `src/app/(marketing)/cases/[id]/page.test.tsx`
|
||||
|
||||
```typescript
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom';
|
||||
import CaseDetailPage from './page';
|
||||
|
||||
describe('CaseDetailPage', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('Rendering', () => {
|
||||
it('should render case detail page', () => {
|
||||
render(<CaseDetailPage params={{ id: '1' }} />);
|
||||
const main = screen.getByRole('main');
|
||||
expect(main).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render case title', () => {
|
||||
render(<CaseDetailPage params={{ id: '1' }} />);
|
||||
const title = screen.getByRole('heading', { level: 1 });
|
||||
expect(title).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render case content', () => {
|
||||
render(<CaseDetailPage params={{ id: '1' }} />);
|
||||
const content = screen.getByText(/案例详情/i);
|
||||
expect(content).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render case images', () => {
|
||||
render(<CaseDetailPage params={{ id: '1' }} />);
|
||||
const images = screen.getAllByRole('img');
|
||||
expect(images.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('should render related cases', () => {
|
||||
render(<CaseDetailPage params={{ id: '1' }} />);
|
||||
const relatedCases = screen.getByText(/相关案例/i);
|
||||
expect(relatedCases).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Navigation', () => {
|
||||
it('should have back to cases link', () => {
|
||||
render(<CaseDetailPage params={{ id: '1' }} />);
|
||||
const backLink = screen.getByRole('link', { name: /返回/i });
|
||||
expect(backLink).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Accessibility', () => {
|
||||
it('should have main landmark', () => {
|
||||
render(<CaseDetailPage params={{ id: '1' }} />);
|
||||
const main = screen.getByRole('main');
|
||||
expect(main).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should have proper heading hierarchy', () => {
|
||||
render(<CaseDetailPage params={{ id: '1' }} />);
|
||||
const h1 = screen.getByRole('heading', { level: 1 });
|
||||
expect(h1).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
**Step 3: 运行测试验证通过**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
npm run test:unit -- src/app/\(marketing\)/cases/\[id\]/page.test.tsx
|
||||
```
|
||||
|
||||
Expected: 所有测试通过
|
||||
|
||||
**Step 4: 提交代码**
|
||||
|
||||
```bash
|
||||
git add src/app/\(marketing\)/cases/\[id\]/page.test.tsx
|
||||
git commit -m "test: add case detail page tests"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: 新闻详情页测试
|
||||
|
||||
**Files:**
|
||||
- Create: `src/app/(marketing)/news/[slug]/page.test.tsx`
|
||||
- Test: `src/app/(marketing)/news/[slug]/page.tsx`
|
||||
|
||||
**Step 1: 查看新闻详情页源码**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
cat src/app/\(marketing\)/news/\[slug\]/page.tsx | head -100
|
||||
```
|
||||
|
||||
Expected: 显示新闻详情页结构和功能
|
||||
|
||||
**Step 2: 编写新闻详情页测试**
|
||||
|
||||
Create: `src/app/(marketing)/news/[slug]/page.test.tsx`
|
||||
|
||||
```typescript
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom';
|
||||
import NewsDetailPage from './page';
|
||||
|
||||
describe('NewsDetailPage', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('Rendering', () => {
|
||||
it('should render news detail page', () => {
|
||||
render(<NewsDetailPage params={{ slug: 'test-news' }} />);
|
||||
const main = screen.getByRole('main');
|
||||
expect(main).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render news title', () => {
|
||||
render(<NewsDetailPage params={{ slug: 'test-news' }} />);
|
||||
const title = screen.getByRole('heading', { level: 1 });
|
||||
expect(title).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render news content', () => {
|
||||
render(<NewsDetailPage params={{ slug: 'test-news' }} />);
|
||||
const content = screen.getByText(/新闻详情/i);
|
||||
expect(content).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render news date', () => {
|
||||
render(<NewsDetailPage params={{ slug: 'test-news' }} />);
|
||||
const date = screen.getByText(/\d{4}-\d{2}-\d{2}/);
|
||||
expect(date).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render news category', () => {
|
||||
render(<NewsDetailPage params={{ slug: 'test-news' }} />);
|
||||
const category = screen.getByText(/分类/i);
|
||||
expect(category).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render related news', () => {
|
||||
render(<NewsDetailPage params={{ slug: 'test-news' }} />);
|
||||
const relatedNews = screen.getByText(/相关新闻/i);
|
||||
expect(relatedNews).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Navigation', () => {
|
||||
it('should have back to news link', () => {
|
||||
render(<NewsDetailPage params={{ slug: 'test-news' }} />);
|
||||
const backLink = screen.getByRole('link', { name: /返回/i });
|
||||
expect(backLink).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Accessibility', () => {
|
||||
it('should have main landmark', () => {
|
||||
render(<NewsDetailPage params={{ slug: 'test-news' }} />);
|
||||
const main = screen.getByRole('main');
|
||||
expect(main).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should have proper heading hierarchy', () => {
|
||||
render(<NewsDetailPage params={{ slug: 'test-news' }} />);
|
||||
const h1 = screen.getByRole('heading', { level: 1 });
|
||||
expect(h1).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
**Step 3: 运行测试验证通过**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
npm run test:unit -- src/app/\(marketing\)/news/\[slug\]/page.test.tsx
|
||||
```
|
||||
|
||||
Expected: 所有测试通过
|
||||
|
||||
**Step 4: 提交代码**
|
||||
|
||||
```bash
|
||||
git add src/app/\(marketing\)/news/\[slug\]/page.test.tsx
|
||||
git commit -m "test: add news detail page tests"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 3-5: 产品详情页、服务详情页、解决方案页测试
|
||||
|
||||
(类似Task 1-2,为产品详情页、服务详情页、解决方案页添加测试)
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: 管理后台页面测试(提升到60%)
|
||||
|
||||
### Task 6: 管理后台主页测试
|
||||
|
||||
**Files:**
|
||||
- Create: `src/app/admin/page.test.tsx`
|
||||
- Test: `src/app/admin/page.tsx`
|
||||
|
||||
**Step 1: 查看管理后台主页源码**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
cat src/app/admin/page.tsx | head -100
|
||||
```
|
||||
|
||||
Expected: 显示管理后台主页结构和功能
|
||||
|
||||
**Step 2: 编写管理后台主页测试**
|
||||
|
||||
Create: `src/app/admin/page.test.tsx`
|
||||
|
||||
```typescript
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom';
|
||||
import AdminPage from './page';
|
||||
|
||||
describe('AdminPage', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('Rendering', () => {
|
||||
it('should render admin page', () => {
|
||||
render(<AdminPage />);
|
||||
const main = screen.getByRole('main');
|
||||
expect(main).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render dashboard', () => {
|
||||
render(<AdminPage />);
|
||||
const dashboard = screen.getByText(/仪表盘/i);
|
||||
expect(dashboard).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render statistics', () => {
|
||||
render(<AdminPage />);
|
||||
const stats = screen.getByText(/统计/i);
|
||||
expect(stats).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render quick actions', () => {
|
||||
render(<AdminPage />);
|
||||
const quickActions = screen.getByText(/快捷操作/i);
|
||||
expect(quickActions).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Navigation', () => {
|
||||
it('should have navigation menu', () => {
|
||||
render(<AdminPage />);
|
||||
const nav = screen.getByRole('navigation');
|
||||
expect(nav).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should have content management link', () => {
|
||||
render(<AdminPage />);
|
||||
const contentLink = screen.getByRole('link', { name: /内容管理/i });
|
||||
expect(contentLink).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should have user management link', () => {
|
||||
render(<AdminPage />);
|
||||
const userLink = screen.getByRole('link', { name: /用户管理/i });
|
||||
expect(userLink).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Accessibility', () => {
|
||||
it('should have main landmark', () => {
|
||||
render(<AdminPage />);
|
||||
const main = screen.getByRole('main');
|
||||
expect(main).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should have proper heading hierarchy', () => {
|
||||
render(<AdminPage />);
|
||||
const h1 = screen.getByRole('heading', { level: 1 });
|
||||
expect(h1).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
**Step 3: 运行测试验证通过**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
npm run test:unit -- src/app/admin/page.test.tsx
|
||||
```
|
||||
|
||||
Expected: 所有测试通过
|
||||
|
||||
**Step 4: 提交代码**
|
||||
|
||||
```bash
|
||||
git add src/app/admin/page.test.tsx
|
||||
git commit -m "test: add admin main page tests"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 7-8: 内容编辑页、审计日志页测试
|
||||
|
||||
(类似Task 6,为内容编辑页、审计日志页添加测试)
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: 管理后台API测试(提升到65%)
|
||||
|
||||
### Task 9: 配置管理API测试
|
||||
|
||||
**Files:**
|
||||
- Create: `src/app/api/admin/config/route.test.ts`
|
||||
- Test: `src/app/api/admin/config/route.ts`
|
||||
|
||||
**Step 1: 查看配置管理API源码**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
cat src/app/api/admin/config/route.ts | head -100
|
||||
```
|
||||
|
||||
Expected: 显示配置管理API结构和功能
|
||||
|
||||
**Step 2: 编写配置管理API测试**
|
||||
|
||||
Create: `src/app/api/admin/config/route.test.ts`
|
||||
|
||||
```typescript
|
||||
import { GET, POST, PUT, DELETE } from './route';
|
||||
import { NextRequest } from 'next/server';
|
||||
|
||||
describe('/api/admin/config', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('GET', () => {
|
||||
it('should return config list', async () => {
|
||||
const request = new NextRequest('http://localhost/api/admin/config');
|
||||
const response = await GET(request);
|
||||
const data = await response.json();
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(data.success).toBe(true);
|
||||
expect(Array.isArray(data.data)).toBe(true);
|
||||
});
|
||||
|
||||
it('should return config by key', async () => {
|
||||
const request = new NextRequest('http://localhost/api/admin/config?key=site_name');
|
||||
const response = await GET(request);
|
||||
const data = await response.json();
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(data.success).toBe(true);
|
||||
expect(data.data).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('POST', () => {
|
||||
it('should create new config', async () => {
|
||||
const request = new NextRequest('http://localhost/api/admin/config', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
key: 'test_key',
|
||||
value: 'test_value',
|
||||
category: 'test',
|
||||
}),
|
||||
});
|
||||
const response = await POST(request);
|
||||
const data = await response.json();
|
||||
|
||||
expect(response.status).toBe(201);
|
||||
expect(data.success).toBe(true);
|
||||
expect(data.data.key).toBe('test_key');
|
||||
});
|
||||
|
||||
it('should reject invalid config', async () => {
|
||||
const request = new NextRequest('http://localhost/api/admin/config', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({}),
|
||||
});
|
||||
const response = await POST(request);
|
||||
const data = await response.json();
|
||||
|
||||
expect(response.status).toBe(400);
|
||||
expect(data.success).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('PUT', () => {
|
||||
it('should update config', async () => {
|
||||
const request = new NextRequest('http://localhost/api/admin/config', {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify({
|
||||
key: 'test_key',
|
||||
value: 'updated_value',
|
||||
}),
|
||||
});
|
||||
const response = await PUT(request);
|
||||
const data = await response.json();
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(data.success).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('DELETE', () => {
|
||||
it('should delete config', async () => {
|
||||
const request = new NextRequest('http://localhost/api/admin/config?key=test_key', {
|
||||
method: 'DELETE',
|
||||
});
|
||||
const response = await DELETE(request);
|
||||
const data = await response.json();
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(data.success).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
**Step 3: 运行测试验证通过**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
npm run test:unit -- src/app/api/admin/config/route.test.ts
|
||||
```
|
||||
|
||||
Expected: 所有测试通过
|
||||
|
||||
**Step 4: 提交代码**
|
||||
|
||||
```bash
|
||||
git add src/app/api/admin/config/route.test.ts
|
||||
git commit -m "test: add config management API tests"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 10-13: 内容API、日志API、上传API、用户API测试
|
||||
|
||||
(类似Task 9,为其他管理后台API添加测试)
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: 其他页面测试(提升到68%)
|
||||
|
||||
### Task 14: 隐私政策页测试
|
||||
|
||||
**Files:**
|
||||
- Create: `src/app/privacy/page.test.tsx`
|
||||
- Test: `src/app/privacy/page.tsx`
|
||||
|
||||
**Step 1: 查看隐私政策页源码**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
cat src/app/privacy/page.tsx | head -50
|
||||
```
|
||||
|
||||
Expected: 显示隐私政策页结构和功能
|
||||
|
||||
**Step 2: 编写隐私政策页测试**
|
||||
|
||||
Create: `src/app/privacy/page.test.tsx`
|
||||
|
||||
```typescript
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom';
|
||||
import PrivacyPage from './page';
|
||||
|
||||
describe('PrivacyPage', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('Rendering', () => {
|
||||
it('should render privacy page', () => {
|
||||
render(<PrivacyPage />);
|
||||
const main = screen.getByRole('main');
|
||||
expect(main).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render privacy title', () => {
|
||||
render(<PrivacyPage />);
|
||||
const title = screen.getByRole('heading', { level: 1 });
|
||||
expect(title).toBeInTheDocument();
|
||||
expect(title).toHaveTextContent(/隐私政策/i);
|
||||
});
|
||||
|
||||
it('should render privacy content', () => {
|
||||
render(<PrivacyPage />);
|
||||
const content = screen.getByText(/隐私/i);
|
||||
expect(content).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Accessibility', () => {
|
||||
it('should have main landmark', () => {
|
||||
render(<PrivacyPage />);
|
||||
const main = screen.getByRole('main');
|
||||
expect(main).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should have proper heading hierarchy', () => {
|
||||
render(<PrivacyPage />);
|
||||
const h1 = screen.getByRole('heading', { level: 1 });
|
||||
expect(h1).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
**Step 3: 运行测试验证通过**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
npm run test:unit -- src/app/privacy/page.test.tsx
|
||||
```
|
||||
|
||||
Expected: 所有测试通过
|
||||
|
||||
**Step 4: 提交代码**
|
||||
|
||||
```bash
|
||||
git add src/app/privacy/page.test.tsx
|
||||
git commit -m "test: add privacy page tests"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 15: 服务条款页测试
|
||||
|
||||
(类似Task 14,为服务条款页添加测试)
|
||||
|
||||
---
|
||||
|
||||
### Task 16: 特效预览页测试
|
||||
|
||||
**Files:**
|
||||
- Create: `src/app/preview/effects/page.test.tsx`
|
||||
- Test: `src/app/preview/effects/page.tsx`
|
||||
|
||||
**Step 1: 查看特效预览页源码**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
cat src/app/preview/effects/page.tsx | head -100
|
||||
```
|
||||
|
||||
Expected: 显示特效预览页结构和功能
|
||||
|
||||
**Step 2: 编写特效预览页测试**
|
||||
|
||||
Create: `src/app/preview/effects/page.test.tsx`
|
||||
|
||||
```typescript
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom';
|
||||
import EffectsPreviewPage from './page';
|
||||
|
||||
describe('EffectsPreviewPage', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('Rendering', () => {
|
||||
it('should render effects preview page', () => {
|
||||
render(<EffectsPreviewPage />);
|
||||
const main = screen.getByRole('main');
|
||||
expect(main).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render effects list', () => {
|
||||
render(<EffectsPreviewPage />);
|
||||
const effectsList = screen.getByRole('list');
|
||||
expect(effectsList).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render effect cards', () => {
|
||||
render(<EffectsPreviewPage />);
|
||||
const effectCards = screen.getAllByRole('listitem');
|
||||
expect(effectCards.length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Accessibility', () => {
|
||||
it('should have main landmark', () => {
|
||||
render(<EffectsPreviewPage />);
|
||||
const main = screen.getByRole('main');
|
||||
expect(main).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
**Step 3: 运行测试验证通过**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
npm run test:unit -- src/app/preview/effects/page.test.tsx
|
||||
```
|
||||
|
||||
Expected: 所有测试通过
|
||||
|
||||
**Step 4: 提交代码**
|
||||
|
||||
```bash
|
||||
git add src/app/preview/effects/page.test.tsx
|
||||
git commit -m "test: add effects preview page tests"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: 组件测试(提升到70%)
|
||||
|
||||
### Task 17: 管理后台组件测试
|
||||
|
||||
**Files:**
|
||||
- Create: `src/components/admin/RichTextEditor.test.tsx`
|
||||
- Test: `src/components/admin/RichTextEditor.tsx`
|
||||
|
||||
**Step 1: 查看富文本编辑器组件源码**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
cat src/components/admin/RichTextEditor.tsx | head -100
|
||||
```
|
||||
|
||||
Expected: 显示富文本编辑器组件结构和功能
|
||||
|
||||
**Step 2: 编写富文本编辑器组件测试**
|
||||
|
||||
Create: `src/components/admin/RichTextEditor.test.tsx`
|
||||
|
||||
```typescript
|
||||
import { render, screen, fireEvent } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom';
|
||||
import RichTextEditor from './RichTextEditor';
|
||||
|
||||
describe('RichTextEditor', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('Rendering', () => {
|
||||
it('should render editor', () => {
|
||||
render(<RichTextEditor value="" onChange={() => {}} />);
|
||||
const editor = screen.getByRole('textbox');
|
||||
expect(editor).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render toolbar', () => {
|
||||
render(<RichTextEditor value="" onChange={() => {}} />);
|
||||
const toolbar = screen.getByRole('toolbar');
|
||||
expect(toolbar).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render formatting buttons', () => {
|
||||
render(<RichTextEditor value="" onChange={() => {}} />);
|
||||
const boldButton = screen.getByRole('button', { name: /粗体/i });
|
||||
const italicButton = screen.getByRole('button', { name: /斜体/i });
|
||||
|
||||
expect(boldButton).toBeInTheDocument();
|
||||
expect(italicButton).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Functionality', () => {
|
||||
it('should call onChange when content changes', () => {
|
||||
const handleChange = jest.fn();
|
||||
render(<RichTextEditor value="" onChange={handleChange} />);
|
||||
|
||||
const editor = screen.getByRole('textbox');
|
||||
fireEvent.change(editor, { target: { value: 'test content' } });
|
||||
|
||||
expect(handleChange).toHaveBeenCalledWith('test content');
|
||||
});
|
||||
|
||||
it('should display initial value', () => {
|
||||
render(<RichTextEditor value="initial content" onChange={() => {}} />);
|
||||
const editor = screen.getByRole('textbox');
|
||||
expect(editor).toHaveValue('initial content');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Accessibility', () => {
|
||||
it('should have accessible label', () => {
|
||||
render(<RichTextEditor value="" onChange={() => {}} aria-label="Content editor" />);
|
||||
const editor = screen.getByLabelText('Content editor');
|
||||
expect(editor).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
**Step 3: 运行测试验证通过**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
npm run test:unit -- src/components/admin/RichTextEditor.test.tsx
|
||||
```
|
||||
|
||||
Expected: 所有测试通过
|
||||
|
||||
**Step 4: 提交代码**
|
||||
|
||||
```bash
|
||||
git add src/components/admin/RichTextEditor.test.tsx
|
||||
git commit -m "test: add rich text editor component tests"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 18-21: 其他组件测试
|
||||
|
||||
(类似Task 17,为analytics、effects、examples、seo组件添加测试)
|
||||
|
||||
---
|
||||
|
||||
## Phase 6: 最终验证
|
||||
|
||||
### Task 22: 验证覆盖率达到70%
|
||||
|
||||
**Step 1: 运行测试覆盖率检查**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
npm run test:unit -- --coverage --coverageReporters=text-summary
|
||||
```
|
||||
|
||||
Expected: 覆盖率达到70%以上
|
||||
|
||||
**Step 2: 如果未达标,补充缺失测试**
|
||||
|
||||
根据覆盖率报告,补充缺失的测试用例。
|
||||
|
||||
---
|
||||
|
||||
### Task 23: 更新覆盖率门禁
|
||||
|
||||
**Files:**
|
||||
- Modify: `jest.config.js`
|
||||
- Modify: `.woodpecker/quality-gate.yml`
|
||||
|
||||
**Step 1: 更新Jest覆盖率门禁**
|
||||
|
||||
Modify: `jest.config.js`
|
||||
|
||||
```javascript
|
||||
coverageThreshold: {
|
||||
global: {
|
||||
branches: 70,
|
||||
functions: 70,
|
||||
lines: 70,
|
||||
statements: 70
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Step 2: 更新CI/CD质量门禁**
|
||||
|
||||
Modify: `.woodpecker/quality-gate.yml`
|
||||
|
||||
```yaml
|
||||
unit-tests:
|
||||
image: node:18-alpine
|
||||
commands:
|
||||
- echo "=== Running unit tests with coverage ==="
|
||||
- npm run test:unit -- --coverage --coverageReporters=json
|
||||
- |
|
||||
COVERAGE=$(cat coverage/coverage-summary.json | grep -o '"lines":{"pct":[0-9.]*' | grep -o '[0-9.]*$')
|
||||
echo "Current coverage: $COVERAGE%"
|
||||
if [ $(echo "$COVERAGE < 70" | bc -l) -eq 1 ]; then
|
||||
echo "❌ Coverage $COVERAGE% is below threshold 70%"
|
||||
exit 1
|
||||
fi
|
||||
echo "✅ Coverage $COVERAGE% meets threshold 70%"
|
||||
```
|
||||
|
||||
**Step 3: 提交配置**
|
||||
|
||||
```bash
|
||||
git add jest.config.js .woodpecker/quality-gate.yml
|
||||
git commit -m "feat: update coverage threshold gate to 70%"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 24: 创建测试规范文档
|
||||
|
||||
**Files:**
|
||||
- Create: `docs/TESTING_GUIDELINES.md`
|
||||
|
||||
**Step 1: 创建测试规范文档**
|
||||
|
||||
(参考之前的测试规范文档内容)
|
||||
|
||||
**Step 2: 提交文档**
|
||||
|
||||
```bash
|
||||
git add docs/TESTING_GUIDELINES.md
|
||||
git commit -m "docs: add testing guidelines"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 25: 最终验收
|
||||
|
||||
**Step 1: 运行完整测试套件**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
npm run test:unit -- --coverage
|
||||
```
|
||||
|
||||
Expected: 覆盖率达到70%以上,所有测试通过
|
||||
|
||||
**Step 2: 运行CI/CD流水线**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
npm run lint && npm run typecheck && npm run test:unit
|
||||
```
|
||||
|
||||
Expected: 所有检查通过
|
||||
|
||||
**Step 3: 生成最终报告**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
npm run test:unit -- --coverage --coverageReporters=html
|
||||
```
|
||||
|
||||
Expected: 生成HTML覆盖率报告
|
||||
|
||||
**Step 4: 提交最终完成标记**
|
||||
|
||||
```bash
|
||||
git add .
|
||||
git commit -m "feat: complete full module test coverage to 70%"
|
||||
git tag v1.0.0-test-coverage-70-full
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 预期成果
|
||||
|
||||
**Phase 1完成后:**
|
||||
- 测试覆盖率:≥55%
|
||||
- 页面详情页测试:100%覆盖
|
||||
|
||||
**Phase 2完成后:**
|
||||
- 测试覆盖率:≥60%
|
||||
- 管理后台页面测试:100%覆盖
|
||||
|
||||
**Phase 3完成后:**
|
||||
- 测试覆盖率:≥65%
|
||||
- 管理后台API测试:100%覆盖
|
||||
|
||||
**Phase 4完成后:**
|
||||
- 测试覆盖率:≥68%
|
||||
- 其他页面测试:100%覆盖
|
||||
|
||||
**Phase 5完成后:**
|
||||
- 测试覆盖率:≥70%
|
||||
- 组件测试:100%覆盖
|
||||
|
||||
---
|
||||
|
||||
## 🎯 成功标准
|
||||
|
||||
1. ✅ 测试覆盖率达到70%以上
|
||||
2. ✅ 所有测试通过率100%
|
||||
3. ✅ 所有未覆盖模块测试完成
|
||||
4. ✅ CI/CD质量门禁更新到70%
|
||||
5. ✅ 测试规范文档完整
|
||||
|
||||
---
|
||||
|
||||
**计划创建完成!**
|
||||
Reference in New Issue
Block a user