Files
novalon-website/docs/plans/2026-03-05-smoke-test-failures-fix.md
T
2026-03-06 11:51:46 +08:00

831 lines
20 KiB
Markdown

# 冒烟测试失败修复实施计划
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**目标:** 修复 233 个冒烟测试失败,将测试通过率从 78.5% 提升至 95% 以上
**架构:** 采用分层修复策略,优先修复关键路径(导航、联系页面、表单),然后处理次要问题(滚动、可见性),最后优化测试稳定性
**技术栈:** Next.js 16.1.6, React 19.2.3, TypeScript 5, Playwright, Tailwind CSS 4
---
## 优先级分类
### P0 - 关键问题 (立即修复)
- 导航菜单不可见
- 联系页面加载失败
- 表单输入功能失败
- 提交按钮不可见
### P1 - 高优先级 (尽快修复)
- 区块可见性问题
- 滚动功能问题
- 联系信息显示问题
### P2 - 中优先级 (稍后修复)
- 页脚可见性
- 页面描述和徽章
- 必填字段标记
### P3 - 低优先级 (可选优化)
- 字段占位符
- 测试稳定性优化
---
## Task 1: 修复导航菜单选择器
**问题:** 导航元素选择器不正确,导致移动端和部分设备上导航测试失败
**文件:**
- 修改: `src/components/layout/header.tsx:242-248`
- 测试: `e2e/src/tests/smoke/home-page.smoke.spec.ts:25-29`
**Step 1: 添加导航语义化属性**
`src/components/layout/header.tsx` 第 242 行附近,为桌面导航添加 `role="navigation"`:
```tsx
// 修改前 (第 242 行)
<nav className="hidden md:flex items-center gap-1">
// 修改后
<nav className="hidden md:flex items-center gap-1" role="navigation" aria-label="主导航">
```
**Step 2: 验证导航选择器**
运行测试验证导航可见性:
```bash
cd e2e && npm test -- home-page.smoke.spec.ts -g "应该显示主导航菜单"
```
预期: PASS
**Step 3: 添加移动端导航测试**
`e2e/src/tests/smoke/home-page.smoke.spec.ts` 第 25 行后添加移动端导航测试:
```typescript
test('移动端应该显示导航菜单按钮', async ({ homePage, page }) => {
await page.setViewportSize({ width: 375, height: 667 });
await expect(homePage.mobileMenuButton).toBeVisible();
});
```
**Step 4: 运行移动端测试**
```bash
cd e2e && npm test -- home-page.smoke.spec.ts -g "移动端应该显示导航菜单按钮"
```
预期: PASS
**Step 5: 提交修改**
```bash
git add src/components/layout/header.tsx e2e/src/tests/smoke/home-page.smoke.spec.ts
git commit -m "fix: add navigation role attribute for better test selector"
```
---
## Task 2: 修复联系页面元素选择器
**问题:** 联系页面多个元素选择器不正确,导致大量测试失败
**文件:**
- 修改: `e2e/src/pages/ContactPage.ts:16-51`
- 修改: `src/app/(marketing)/contact/page.tsx:152-165`
- 测试: `e2e/src/tests/smoke/contact-page.smoke.spec.ts`
**Step 1: 为联系页面元素添加 data-testid**
`src/app/(marketing)/contact/page.tsx` 第 152 行附近,为联系信息卡片添加测试标识:
```tsx
// 修改前 (第 152 行附近)
<div className="space-y-4">
<div className="flex items-start gap-4 group">
<div className="w-10 h-10 bg-[#C41E3A] rounded-md flex items-center justify-center flex-shrink-0 transition-transform duration-200 group-hover:scale-105">
<Mail className="w-5 h-5 text-white" />
</div>
<div>
<p className="text-sm text-[#5C5C5C] mb-1"></p>
<a href={`mailto:${COMPANY_INFO.email}`} className="text-[#1C1C1C] hover:text-[#C41E3A] transition-colors duration-200">
{COMPANY_INFO.email}
</a>
</div>
</div>
</div>
// 修改后
<div className="space-y-4" data-testid="contact-info">
<div className="flex items-start gap-4 group" data-testid="email-info">
<div className="w-10 h-10 bg-[#C41E3A] rounded-md flex items-center justify-center flex-shrink-0 transition-transform duration-200 group-hover:scale-105">
<Mail className="w-5 h-5 text-white" />
</div>
<div>
<p className="text-sm text-[#5C5C5C] mb-1"></p>
<a href={`mailto:${COMPANY_INFO.email}`} className="text-[#1C1C1C] hover:text-[#C41E3A] transition-colors duration-200" data-testid="email-link">
{COMPANY_INFO.email}
</a>
</div>
</div>
</div>
```
**Step 2: 为工作时间卡片添加测试标识**
在第 181 行附近:
```tsx
// 修改前
<div className="bg-[#FFFBF5] p-5 rounded-lg border border-[#E5E5E5]">
<div className="flex items-center gap-2 mb-3">
<Clock className="w-4 h-4 text-[#C41E3A]" />
<h4 className="text-sm font-medium text-[#1C1C1C]"></h4>
</div>
// 修改后
<div className="bg-[#FFFBF5] p-5 rounded-lg border border-[#E5E5E5]" data-testid="work-hours-card">
<div className="flex items-center gap-2 mb-3">
<Clock className="w-4 h-4 text-[#C41E3A]" />
<h4 className="text-sm font-medium text-[#1C1C1C]"></h4>
</div>
```
**Step 3: 更新 ContactPage 测试选择器**
`e2e/src/pages/ContactPage.ts` 第 16 行附近更新选择器:
```typescript
// 修改前
this.contactInfoCard = page.locator('[data-slot="card"]').filter({ hasText: '联系方式' }).first();
this.workHoursCard = page.locator('[data-slot="card"]').filter({ hasText: '工作时间' }).first();
// 修改后
this.contactInfoCard = page.locator('[data-testid="contact-info"]');
this.workHoursCard = page.locator('[data-testid="work-hours-card"]');
this.emailInfo = page.locator('[data-testid="email-info"]');
this.emailLink = page.locator('[data-testid="email-link"]');
```
**Step 4: 运行联系页面测试**
```bash
cd e2e && npm test -- contact-page.smoke.spec.ts
```
预期: 大部分测试通过
**Step 5: 提交修改**
```bash
git add src/app/\(marketing\)/contact/page.tsx e2e/src/pages/ContactPage.ts
git commit -m "fix: add data-testid attributes for contact page elements"
```
---
## Task 3: 修复表单输入字段选择器
**问题:** 表单输入字段选择器不正确,导致输入测试失败
**文件:**
- 修改: `src/app/(marketing)/contact/page.tsx:250-290`
- 修改: `e2e/src/pages/ContactPage.ts:12-17`
- 测试: `e2e/src/tests/smoke/contact-page.smoke.spec.ts:36-40`
**Step 1: 为表单字段添加 name 属性和 data-testid**
`src/app/(marketing)/contact/page.tsx` 第 250 行附近的表单部分:
```tsx
// 修改前
<Input
type="text"
placeholder="请输入您的姓名"
value={formData.name}
onChange={(e) => handleChange('name', e.target.value)}
onBlur={(e) => handleBlur('name', e.target.value)}
className={errors.name ? 'border-red-500' : ''}
/>
// 修改后
<Input
type="text"
name="name"
data-testid="name-input"
placeholder="请输入您的姓名"
value={formData.name}
onChange={(e) => handleChange('name', e.target.value)}
onBlur={(e) => handleBlur('name', e.target.value)}
className={errors.name ? 'border-red-500' : ''}
required
/>
```
对其他字段重复此操作:
- email (第 260 行附近)
- phone (第 270 行附近)
- subject (第 280 行附近)
- message (第 290 行附近)
**Step 2: 为提交按钮添加测试标识**
```tsx
// 修改前
<Button type="submit" disabled={isSubmitting}>
{isSubmitting ? (
<>
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
...
</>
) : (
<>
<Send className="w-4 h-4 mr-2" />
</>
)}
</Button>
// 修改后
<Button type="submit" disabled={isSubmitting} data-testid="submit-button">
{isSubmitting ? (
<>
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
...
</>
) : (
<>
<Send className="w-4 h-4 mr-2" />
</>
)}
</Button>
```
**Step 3: 更新 ContactPage 选择器**
`e2e/src/pages/ContactPage.ts` 第 12 行:
```typescript
// 修改前
this.nameInput = page.locator('input[name="name"]');
this.phoneInput = page.locator('input[name="phone"]');
this.emailInput = page.locator('input[name="email"]');
this.subjectInput = page.locator('input[name="subject"]');
this.messageInput = page.locator('textarea[name="message"]');
this.submitButton = page.locator('button[type="submit"]');
// 修改后
this.nameInput = page.locator('[data-testid="name-input"]');
this.phoneInput = page.locator('[data-testid="phone-input"]');
this.emailInput = page.locator('[data-testid="email-input"]');
this.subjectInput = page.locator('[data-testid="subject-input"]');
this.messageInput = page.locator('[data-testid="message-input"]');
this.submitButton = page.locator('[data-testid="submit-button"]');
```
**Step 4: 运行表单输入测试**
```bash
cd e2e && npm test -- contact-page.smoke.spec.ts -g "应该能够输入"
```
预期: PASS
**Step 5: 提交修改**
```bash
git add src/app/\(marketing\)/contact/page.tsx e2e/src/pages/ContactPage.ts
git commit -m "fix: add name and data-testid attributes for form inputs"
```
---
## Task 4: 修复滚动到顶部功能
**问题:** `scrollToTop()` 函数实现有问题,滚动位置未归零
**文件:**
- 修改: `e2e/src/pages/BasePage.ts:40-50`
- 测试: `e2e/src/tests/smoke/home-page.smoke.spec.ts:55-59`
**Step 1: 检查 BasePage 滚动实现**
读取 `e2e/src/pages/BasePage.ts`:
```bash
cat e2e/src/pages/BasePage.ts
```
**Step 2: 修复 scrollToTop 方法**
`e2e/src/pages/BasePage.ts` 中:
```typescript
// 修改前
async scrollToTop(): Promise<void> {
await this.page.evaluate(() => window.scrollTo(0, 0));
}
// 修改后
async scrollToTop(): Promise<void> {
await this.page.evaluate(() => {
window.scrollTo({ top: 0, behavior: 'instant' });
});
await this.page.waitForLoadState('domcontentloaded');
await this.page.waitForTimeout(500);
}
```
**Step 3: 运行滚动测试**
```bash
cd e2e && npm test -- home-page.smoke.spec.ts -g "应该能够滚动到页面顶部"
```
预期: PASS
**Step 4: 提交修改**
```bash
git add e2e/src/pages/BasePage.ts
git commit -m "fix: improve scrollToTop implementation with instant behavior"
```
---
## Task 5: 修复区块滚动和可见性问题
**问题:** 区块滚动和可见性检测存在时序问题
**文件:**
- 修改: `e2e/src/pages/HomePage.ts:73-80`
- 测试: `e2e/src/tests/smoke/home-page.smoke.spec.ts:31-45`
**Step 1: 优化 scrollToSection 方法**
`e2e/src/pages/HomePage.ts` 第 73 行:
```typescript
// 修改前
async scrollToSection(sectionId: string): Promise<void> {
const section = this.page.locator(`#${sectionId}`);
await section.waitFor({ state: 'attached', timeout: 10000 });
await section.scrollIntoViewIfNeeded();
await this.page.waitForLoadState('networkidle');
await this.page.waitForTimeout(1000);
}
// 修改后
async scrollToSection(sectionId: string): Promise<void> {
const section = this.page.locator(`#${sectionId}`);
await section.waitFor({ state: 'attached', timeout: 15000 });
await section.scrollIntoViewIfNeeded();
await this.page.waitForLoadState('networkidle');
await this.page.waitForTimeout(1500);
await section.waitFor({ state: 'visible', timeout: 5000 });
}
```
**Step 2: 为区块添加 ID 属性**
检查并确保所有区块都有正确的 ID:
- `src/components/sections/hero-section.tsx` - `id="home"`
- `src/components/sections/services-section.tsx` - `id="services"`
- `src/components/sections/products-section.tsx` - `id="products"`
- `src/components/sections/cases-section.tsx` - `id="cases"`
- `src/components/sections/about-section.tsx` - `id="about"`
- `src/components/sections/news-section.tsx` - `id="news"`
**Step 3: 运行区块可见性测试**
```bash
cd e2e && npm test -- home-page.smoke.spec.ts -g "应该显示.*区块标题"
```
预期: PASS
**Step 4: 提交修改**
```bash
git add e2e/src/pages/HomePage.ts
git commit -m "fix: improve scrollToSection with better wait conditions"
```
---
## Task 6: 添加页面徽章和描述元素
**问题:** 页面徽章和描述元素缺失或选择器不正确
**文件:**
- 修改: `src/app/(marketing)/contact/page.tsx:138-145`
- 修改: `e2e/src/pages/ContactPage.ts:60-70`
**Step 1: 为联系页面添加徽章元素**
`src/app/(marketing)/contact/page.tsx` 第 138 行附近:
```tsx
// 修改前
<div className="flex items-center gap-3 mb-4">
<div className="w-8 h-px bg-gradient-to-r from-[#1C1C1C] to-[#C41E3A]" />
<span className="text-sm text-[#5C5C5C] tracking-wide"></span>
</div>
// 修改后
<div className="flex items-center gap-3 mb-4">
<div className="w-8 h-px bg-gradient-to-r from-[#1C1C1C] to-[#C41E3A]" />
<span className="text-sm text-[#5C5C5C] tracking-wide" data-testid="page-badge"></span>
</div>
```
**Step 2: 添加页面描述元素**
```tsx
// 修改前
<p className="mt-4 text-[#5C5C5C] max-w-2xl">
,
</p>
// 修改后
<p className="mt-4 text-[#5C5C5C] max-w-2xl" data-testid="page-description">
,
</p>
```
**Step 3: 更新 ContactPage 选择器**
`e2e/src/pages/ContactPage.ts` 第 60 行附近添加:
```typescript
// 添加新属性
readonly pageBadge: Locator;
readonly pageDescription: Locator;
// 在构造函数中初始化
this.pageBadge = page.locator('[data-testid="page-badge"]');
this.pageDescription = page.locator('[data-testid="page-description"]');
```
**Step 4: 添加获取方法**
```typescript
async getBadgeText(): Promise<string> {
return await this.pageBadge.textContent() || '';
}
async getPageDescription(): Promise<string> {
return await this.pageDescription.textContent() || '';
}
```
**Step 5: 运行测试**
```bash
cd e2e && npm test -- contact-page.smoke.spec.ts -g "应该显示页面"
```
预期: PASS
**Step 6: 提交修改**
```bash
git add src/app/\(marketing\)/contact/page.tsx e2e/src/pages/ContactPage.ts
git commit -m "fix: add data-testid for page badge and description"
```
---
## Task 7: 修复页脚可见性问题
**问题:** 部分移动设备上页脚不可见
**文件:**
- 修改: `src/components/layout/footer.tsx:1-50`
- 修改: `e2e/src/pages/HomePage.ts:22`
- 测试: `e2e/src/tests/smoke/home-page.smoke.spec.ts:47-51`
**Step 1: 检查 Footer 组件**
```bash
cat src/components/layout/footer.tsx | head -50
```
**Step 2: 为 Footer 添加测试标识**
`src/components/layout/footer.tsx`:
```tsx
// 修改前
<footer className="bg-[#1C1C1C] text-white">
// 修改后
<footer className="bg-[#1C1C1C] text-white" data-testid="footer" role="contentinfo">
```
**Step 3: 更新 HomePage 选择器**
`e2e/src/pages/HomePage.ts` 第 22 行:
```typescript
// 修改前
this.footer = page.locator('footer');
// 修改后
this.footer = page.locator('[data-testid="footer"]');
```
**Step 4: 改进页脚等待逻辑**
```typescript
async waitForFooter(): Promise<void> {
await this.scrollToBottom();
await this.page.waitForLoadState('networkidle');
await this.footer.waitFor({ state: 'visible', timeout: 10000 });
}
```
**Step 5: 运行页脚测试**
```bash
cd e2e && npm test -- home-page.smoke.spec.ts -g "应该显示页脚"
```
预期: PASS
**Step 6: 提交修改**
```bash
git add src/components/layout/footer.tsx e2e/src/pages/HomePage.ts
git commit -m "fix: add data-testid and role for footer element"
```
---
## Task 8: 修复立即咨询按钮可见性
**问题:** 移动端立即咨询按钮不可见
**文件:**
- 修改: `src/components/layout/header.tsx:200-205`
- 测试: `e2e/src/tests/smoke/home-page.smoke.spec.ts`
**Step 1: 为咨询按钮添加测试标识**
`src/components/layout/header.tsx` 第 200 行:
```tsx
// 修改前
<Button
size="sm"
asChild
>
<Link href="/contact"></Link>
</Button>
// 修改后
<Button
size="sm"
asChild
data-testid="consult-button"
>
<Link href="/contact"></Link>
</Button>
```
**Step 2: 添加测试用例**
`e2e/src/tests/smoke/home-page.smoke.spec.ts`:
```typescript
test('应该显示立即咨询按钮', async ({ homePage }) => {
const consultButton = homePage.page.locator('[data-testid="consult-button"]');
await expect(consultButton).toBeVisible();
});
```
**Step 3: 运行测试**
```bash
cd e2e && npm test -- home-page.smoke.spec.ts -g "应该显示立即咨询按钮"
```
预期: PASS
**Step 4: 提交修改**
```bash
git add src/components/layout/header.tsx e2e/src/tests/smoke/home-page.smoke.spec.ts
git commit -m "fix: add data-testid for consult button"
```
---
## Task 9: 优化测试等待和超时设置
**问题:** 测试因时序问题导致不稳定
**文件:**
- 修改: `e2e/playwright.config.ts:30-40`
- 修改: `e2e/src/fixtures/base.fixture.ts`
**Step 1: 增加全局超时时间**
`e2e/playwright.config.ts`:
```typescript
// 修改前
timeout: 60000,
expect: {
timeout: 30000,
},
// 修改后
timeout: 90000,
expect: {
timeout: 45000,
toHaveScreenshot: { timeout: 60000 },
},
```
**Step 2: 添加测试夹具超时配置**
`e2e/src/fixtures/base.fixture.ts`:
```typescript
import { test as base } from '@playwright/test';
export const test = base.extend({
page: async ({ page }, use) => {
page.setDefaultTimeout(45000);
page.setDefaultNavigationTimeout(90000);
await use(page);
},
});
```
**Step 3: 运行完整测试套件**
```bash
cd e2e && npm test -- --retries=2
```
**Step 4: 提交修改**
```bash
git add e2e/playwright.config.ts e2e/src/fixtures/base.fixture.ts
git commit -m "perf: increase test timeouts for better stability"
```
---
## Task 10: 运行完整冒烟测试并验证修复
**目标:** 验证所有修复,确保测试通过率达到 95% 以上
**Step 1: 运行完整冒烟测试套件**
```bash
cd e2e && npm test -- --grep "@smoke" --reporter=html
```
**Step 2: 生成测试报告**
```bash
cd e2e && npm run test:report
```
**Step 3: 分析剩余失败**
检查 HTML 报告,识别剩余的失败测试:
```bash
open e2e/playwright-report/index.html
```
**Step 4: 创建修复总结文档**
创建 `docs/test-fix-summary.md`:
```markdown
# 冒烟测试修复总结
## 修复前统计
- 总测试数: 1083
- 通过: ~850 (78.5%)
- 失败: ~233 (21.5%)
## 修复后统计
- 总测试数: 1083
- 通过: [填写实际数字]
- 失败: [填写实际数字]
- 通过率: [填写实际百分比]
## 已修复问题
1. ✅ 导航菜单选择器
2. ✅ 联系页面元素选择器
3. ✅ 表单输入字段选择器
4. ✅ 滚动到顶部功能
5. ✅ 区块滚动和可见性
6. ✅ 页面徽章和描述
7. ✅ 页脚可见性
8. ✅ 立即咨询按钮
9. ✅ 测试超时设置
## 剩余问题
[列出剩余的失败测试]
## 下一步计划
[列出后续优化建议]
```
**Step 5: 提交最终修改**
```bash
git add docs/test-fix-summary.md
git commit -m "docs: add smoke test fix summary"
```
---
## 验收标准
### 功能验收
- [ ] 所有 P0 关键问题已修复
- [ ] 所有 P1 高优先级问题已修复
- [ ] 至少 80% 的 P2 中优先级问题已修复
- [ ] 测试通过率 ≥ 95%
### 代码质量验收
- [ ] 所有修改都有对应的测试
- [ ] 代码符合 ESLint 规则
- [ ] TypeScript 类型检查通过
- [ ] 无 console.log 或调试代码
### 文档验收
- [ ] 修复总结文档完整
- [ ] 所有修改都有清晰的 commit message
- [ ] 测试报告已生成
---
## 风险与缓解
### 风险 1: 选择器变更影响生产代码
**缓解:** 使用 `data-testid` 属性,不影响样式和功能
### 风险 2: 超时时间增加影响 CI 速度
**缓解:** 仅在必要时增加超时,优先优化等待逻辑
### 风险 3: 移动端测试不稳定
**缓解:** 增加重试次数,优化移动端特定选择器
---
## 执行建议
### 推荐执行方式
使用 **Subagent-Driven Development** 模式:
- 每个任务由独立的 subagent 执行
- 任务间进行代码审查
- 快速迭代,及时调整
### 执行顺序
1. Task 1-3: 关键路径修复 (导航、联系页面、表单)
2. Task 4-5: 功能修复 (滚动、区块)
3. Task 6-8: 可见性修复 (徽章、页脚、按钮)
4. Task 9: 稳定性优化
5. Task 10: 验证和总结
### 预计时间
- Task 1-3: 2-3 小时
- Task 4-8: 2-3 小时
- Task 9-10: 1-2 小时
- **总计: 5-8 小时**
---
## 后续优化建议
### 短期 (1-2 周)
1. 为所有关键元素添加 `data-testid` 属性
2. 优化移动端测试配置
3. 增加视觉回归测试
### 中期 (1 个月)
1. 建立 E2E 测试最佳实践文档
2. 实施测试覆盖率监控
3. 集成到 CI/CD 流程
### 长期 (3 个月)
1. 建立测试性能基准
2. 实施自动化测试报告
3. 优化测试执行时间