Files
novalon-website/docs/plans/2026-03-06-test-suite-fixes.md
T
张翔 feb646efe5 fix: 修复移动端导航菜单选择器问题
feat: 为主导航菜单和页面区块添加ARIA属性

fix: 解决工作时间信息获取问题

perf: 优化页面滚动功能实现

fix: 修正联系页面标题显示问题

test: 运行完整测试套件验证修复效果

docs: 添加修复完成报告
2026-03-07 15:20:40 +08:00

545 lines
14 KiB
Markdown
Raw 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.
# 测试套件修复计划
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**目标:** 修复表单测试中的元素定位、CSS选择器和Toast组件处理问题,使所有9个失败的表单测试通过。
**架构:** 通过在系统代码中添加data-testid属性、修正测试代码的CSS选择器、更新Toast组件处理逻辑,实现测试与实际UI的同步。
**技术栈:** Playwright测试框架、React组件、TypeScript、CSS选择器
---
## 问题分析
当前9个表单测试失败的根本原因:
1. **元素定位器不匹配** - 测试使用了不存在的`data-testid`属性
2. **CSS选择器转义错误** - 错误消息选择器使用了双转义
3. **Toast组件处理不当** - 测试没有正确处理动态Toast显示
4. **缺少等待机制** - 没有等待异步UI更新完成
---
### Task 1: 在系统代码中添加data-testid属性
**目标:** 为表单元素添加data-testid属性,使测试能够正确定位元素。
**文件:**
- 修改: `/Users/zhangxiang/Codes/Gitee/home-page/novalon-website/src/components/sections/contact-section.tsx`
**Step 1: 为姓名输入框添加data-testid**
```tsx
<Input
label="姓名"
id="name"
placeholder="请输入您的姓名"
required
data-testid="name-input"
value={formData.name}
onChange={(e) => handleChange('name', e.target.value)}
onBlur={(e) => handleBlur('name', e.target.value)}
error={errors.name}
/>
```
**Step 2: 为电话输入框添加data-testid**
```tsx
<Input
label="电话"
id="phone"
type="tel"
placeholder="请输入您的电话"
required
data-testid="phone-input"
value={formData.phone}
onChange={(e) => handleChange('phone', e.target.value)}
onBlur={(e) => handleBlur('phone', e.target.value)}
error={errors.phone}
/>
```
**Step 3: 为邮箱输入框添加data-testid**
```tsx
<Input
label="邮箱"
id="email"
type="email"
placeholder="请输入您的邮箱"
required
data-testid="email-input"
value={formData.email}
onChange={(e) => handleChange('email', e.target.value)}
onBlur={(e) => handleBlur('email', e.target.value)}
error={errors.email}
/>
```
**Step 4: 为留言输入框添加data-testid**
```tsx
<Textarea
label="留言内容"
id="message"
placeholder="请输入您想咨询的内容"
rows={5}
required
data-testid="message-input"
value={formData.message}
onChange={(e) => handleChange('message', e.target.value)}
onBlur={(e) => handleBlur('message', e.target.value)}
error={errors.message}
/>
```
**Step 5: 为提交按钮添加data-testid**
```tsx
<Button
type="submit"
size="lg"
className="w-full group mt-auto min-h-[52px] md:min-h-0"
disabled={isSubmitting}
data-testid="submit-button"
>
```
**Step 6: 为成功消息添加data-testid**
```tsx
{isSubmitted ? (
<div className="text-center py-12 flex-1 flex items-center justify-center" data-testid="success-message">
<div className="w-16 h-16 bg-[#C41E3A] rounded-full flex items-center justify-center mx-auto mb-4 animate-stamp-in">
<CheckCircle2 className="w-8 h-8 text-white" />
</div>
<h4 className="text-xl font-semibold text-[#1A1A2E] mb-2"></h4>
<p className="text-[#718096]"></p>
</div>
) : (
```
**Step 7: 为Toast组件添加data-testid**
```tsx
{showToast && (
<Toast
message={toastMessage}
type={toastType}
onClose={() => setShowToast(false)}
data-testid="toast-notification"
/>
)}
```
**Step 8: 为错误消息容器添加data-testid**
在Input组件中,为错误消息添加data-testid
```tsx
{error && (
<p id={errorId} className="mt-1 text-sm text-[#C41E3A]" role="alert" data-testid="error-message">
{error}
</p>
)}
```
**Step 9: 验证data-testid属性已正确添加**
运行: `cd /Users/zhangxiang/Codes/Gitee/home-page/novalon-website && npm run dev`
预期: 开发服务器正常启动,无编译错误
**Step 10: 提交更改**
```bash
git add src/components/sections/contact-section.tsx src/components/ui/input.tsx src/components/ui/textarea.tsx
git commit -m "feat: add data-testid attributes for form elements to improve testability"
```
---
### Task 2: 修正测试代码中的CSS选择器
**目标:** 修正ContactPage中的CSS选择器,使用正确的转义和data-testid定位。
**文件:**
- 修改: `/Users/zhangxiang/Codes/Gitee/home-page/novalon-website/test-framework/shared/pages/ContactPage.ts`
**Step 1: 修正getFormErrorMessage方法**
```typescript
async getFormErrorMessage(): Promise<string> {
const errorElement = await this.page.locator('[data-testid="error-message"]').first();
return await errorElement.textContent() || '';
}
```
**Step 2: 修正getFormSuccessMessage方法**
```typescript
async getFormSuccessMessage(): Promise<string> {
await this.page.waitForTimeout(1000);
const successElement = await this.page.locator('[data-testid="success-message"]');
if (await successElement.count() > 0) {
return await successElement.textContent() || '';
}
return '';
}
```
**Step 3: 添加getToastMessage方法**
```typescript
async getToastMessage(): Promise<{ message: string; type: 'success' | 'error' }> {
const toastElement = await this.page.locator('[data-testid="toast-notification"]');
if (await toastElement.count() > 0) {
const message = await toastElement.textContent() || '';
const type = await toastElement.getAttribute('data-type') as 'success' | 'error';
return { message, type };
}
return { message: '', type: 'success' };
}
```
**Step 4: 添加waitForToast方法**
```typescript
async waitForToast(timeout: number = 5000): Promise<boolean> {
try {
await this.page.waitForSelector('[data-testid="toast-notification"]', { timeout });
return true;
} catch {
return false;
}
}
```
**Step 5: 添加waitForToastHidden方法**
```typescript
async waitForToastHidden(timeout: number = 5000): Promise<boolean> {
try {
await this.page.waitForSelector('[data-testid="toast-notification"]', { state: 'hidden', timeout });
return true;
} catch {
return false;
}
}
```
**Step 6: 运行表单测试验证选择器修复**
运行: `cd test-framework && npm run test:dev-audit:forms`
预期: 部分测试通过,元素定位错误减少
**Step 7: 提交更改**
```bash
git add test-framework/shared/pages/ContactPage.ts
git commit -m "fix: correct CSS selectors and add toast handling methods in ContactPage"
```
---
### Task 3: 更新表单测试以正确处理Toast组件
**目标:** 修改测试用例,正确处理Toast组件的异步显示和消失。
**文件:**
- 修改: `/Users/zhangxiang/Codes/Gitee/home-page/novalon-website/test-framework/dev-audit/forms/forms.spec.ts`
**Step 1: 修改"联系表单 - 有效数据提交"测试**
```typescript
test('联系表单 - 有效数据提交', async ({ page }) => {
const contactPage = new ContactPage(page);
await page.route('**/api/contact', async route => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ success: true, message: '消息已发送' })
});
});
await contactPage.navigate();
await contactPage.fillContactForm({
name: formData.valid.name,
email: formData.valid.email,
phone: formData.valid.phone,
message: formData.valid.message,
subject: '测试主题'
});
await contactPage.submitForm();
await contactPage.waitForToast();
const toastMessage = await contactPage.getToastMessage();
expect(toastMessage.message).toContain('成功');
expect(toastMessage.type).toBe('success');
await contactPage.waitForToastHidden();
});
```
**Step 2: 修改"联系表单 - 必填字段验证"测试**
```typescript
test('联系表单 - 必填字段验证', async ({ page }) => {
const contactPage = new ContactPage(page);
await contactPage.navigate();
await contactPage.fillContactForm({
name: '',
email: '',
phone: '',
message: '',
subject: ''
});
await contactPage.submitForm();
await page.waitForTimeout(500);
const errorMessage = await contactPage.getFormErrorMessage();
expect(errorMessage).toBeTruthy();
});
```
**Step 3: 修改"联系表单 - 邮箱格式验证"测试**
```typescript
test('联系表单 - 邮箱格式验证', async ({ page }) => {
const contactPage = new ContactPage(page);
await contactPage.navigate();
await contactPage.fillContactForm({
name: '测试用户',
email: formData.invalid.email,
phone: '13800138000',
message: '测试消息',
subject: '测试主题'
});
await contactPage.submitForm();
await page.waitForTimeout(500);
const errorMessage = await contactPage.getFormErrorMessage();
expect(errorMessage).toContain('邮箱');
});
```
**Step 4: 修改"联系表单 - API错误处理"测试**
```typescript
test('联系表单 - API错误处理', async ({ page }) => {
const contactPage = new ContactPage(page);
await page.route('**/api/contact', async route => {
await route.fulfill({
status: 500,
contentType: 'application/json',
body: JSON.stringify({ success: false, message: '服务器错误' })
});
});
await contactPage.navigate();
await contactPage.fillContactForm({
name: formData.valid.name,
email: formData.valid.email,
phone: formData.valid.phone,
message: formData.valid.message,
subject: '测试主题'
});
await contactPage.submitForm();
await contactPage.waitForToast();
const toastMessage = await contactPage.getToastMessage();
expect(toastMessage.message).toContain('失败');
expect(toastMessage.type).toBe('error');
await contactPage.waitForToastHidden();
});
```
**Step 5: 运行表单测试验证Toast处理**
运行: `cd test-framework && npm run test:dev-audit:forms`
预期: 所有表单测试通过,Toast组件正确处理
**Step 6: 提交更改**
```bash
git add test-framework/dev-audit/forms/forms.spec.ts
git commit -m "fix: update form tests to properly handle toast component and async UI updates"
```
---
### Task 4: 更新Input和Textarea组件以支持data-testid
**目标:** 确保UI组件正确传递data-testid属性。
**文件:**
- 修改: `/Users/zhangxiang/Codes/Gitee/home-page/novalon-website/src/components/ui/input.tsx`
- 修改: `/Users/zhangxiang/Codes/Gitee/home-page/novalon-website/src/components/ui/textarea.tsx`
**Step 1: 更新Input组件接口**
```typescript
export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
label?: string
error?: string
'data-testid'?: string
}
```
**Step 2: 在Input组件中使用data-testid**
```typescript
<input
type={type}
id={inputId}
data-slot="input"
data-testid={props['data-testid']}
className={cn(
// ... existing className
)}
ref={ref}
aria-required={props.required ? "true" : undefined}
aria-invalid={error ? "true" : "false"}
aria-describedby={error ? errorId : undefined}
{...props}
/>
```
**Step 3: 更新Textarea组件接口**
```typescript
export interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
label?: string
error?: string
'data-testid'?: string
}
```
**Step 4: 在Textarea组件中使用data-testid**
```typescript
<textarea
id={textareaId}
data-slot="textarea"
data-testid={props['data-testid']}
className={cn(
// ... existing className
)}
ref={ref}
aria-required={props.required ? "true" : undefined}
aria-invalid={error ? "true" : "false"}
aria-describedby={error ? errorId : undefined}
{...props}
/>
```
**Step 5: 验证组件正确传递data-testid**
运行: `cd /Users/zhangxiang/Codes/Gitee/home-page/novalon-website && npm run dev`
预期: 开发服务器正常启动,组件正确编译
**Step 6: 提交更改**
```bash
git add src/components/ui/input.tsx src/components/ui/textarea.tsx
git commit -m "feat: add data-testid support to Input and Textarea components"
```
---
### Task 5: 运行完整测试套件验证修复
**目标:** 运行所有测试验证修复效果。
**文件:**
- 无需修改
**Step 1: 运行表单测试**
运行: `cd test-framework && npm run test:dev-audit:forms`
预期: 所有表单测试通过(20/20)
**Step 2: 运行性能测试**
运行: `cd test-framework && npm run test:dev-audit:performance`
预期: 所有性能测试通过(35/35)
**Step 3: 运行完整测试套件**
运行: `cd test-framework && npm run test:dev-audit`
预期: 所有测试通过(85/85),通过率100%
**Step 4: 生成测试报告**
运行: `cd test-framework && npx ts-node scripts/generate-report.ts`
预期: 生成完整的测试报告,显示所有测试通过
**Step 5: 查看测试结果**
运行: `cat test-framework/reports/results.json | jq '.stats'`
预期输出:
```json
{
"startTime": "2026-03-06T...",
"duration": ...,
"expected": 85,
"skipped": 0,
"unexpected": 0,
"flaky": 0
}
```
**Step 6: 提交最终验证**
```bash
git add test-framework/reports/
git commit -m "test: verify all tests passing after test suite fixes"
```
---
## 验收标准
- [ ] 所有表单测试通过(20/20
- [ ] 所有性能测试通过(35/35
- [ ] 所有SEO测试通过
- [ ] 所有可访问性测试通过
- [ ] 完整测试套件通过率100%(85/85)
- [ ] 测试报告显示所有测试通过
- [ ] 无测试超时错误
- [ ] 无元素定位错误
---
## 风险与注意事项
1. **Toast组件时序问题** - 需要确保测试等待足够时间让Toast显示和消失
2. **移动端兼容性** - 某些测试在移动设备上可能需要额外的等待时间
3. **并发测试冲突** - 多个测试同时运行可能导致状态污染,确保每个测试独立运行
4. **API模拟准确性** - 确保API模拟响应与真实API响应格式一致
---
## 后续优化建议
1. **添加视觉回归测试** - 使用Percy或Applitools进行UI视觉测试
2. **增加测试覆盖率** - 添加更多边界条件和异常场景测试
3. **性能基准测试** - 建立性能基准,监控性能回归
4. **测试数据管理** - 使用测试数据工厂生成更多样化的测试数据
5. **CI/CD集成** - 将测试集成到持续集成流程中