fix: 修复移动端导航菜单选择器问题

feat: 为主导航菜单和页面区块添加ARIA属性

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

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

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

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

docs: 添加修复完成报告
This commit is contained in:
张翔
2026-03-07 15:20:40 +08:00
parent 11b0123c20
commit feb646efe5
12 changed files with 8241 additions and 1 deletions
+544
View File
@@ -0,0 +1,544 @@
# 测试套件修复计划
> **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集成** - 将测试集成到持续集成流程中