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

20 KiB

冒烟测试失败修复实施计划

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":

// 修改前 (第 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: 验证导航选择器

运行测试验证导航可见性:

cd e2e && npm test -- home-page.smoke.spec.ts -g "应该显示主导航菜单"

预期: PASS

Step 3: 添加移动端导航测试

e2e/src/tests/smoke/home-page.smoke.spec.ts 第 25 行后添加移动端导航测试:

test('移动端应该显示导航菜单按钮', async ({ homePage, page }) => {
  await page.setViewportSize({ width: 375, height: 667 });
  await expect(homePage.mobileMenuButton).toBeVisible();
});

Step 4: 运行移动端测试

cd e2e && npm test -- home-page.smoke.spec.ts -g "移动端应该显示导航菜单按钮"

预期: PASS

Step 5: 提交修改

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 行附近,为联系信息卡片添加测试标识:

// 修改前 (第 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 行附近:

// 修改前
<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 行附近更新选择器:

// 修改前
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: 运行联系页面测试

cd e2e && npm test -- contact-page.smoke.spec.ts

预期: 大部分测试通过

Step 5: 提交修改

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 行附近的表单部分:

// 修改前
<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: 为提交按钮添加测试标识

// 修改前
<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 行:

// 修改前
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: 运行表单输入测试

cd e2e && npm test -- contact-page.smoke.spec.ts -g "应该能够输入"

预期: PASS

Step 5: 提交修改

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:

cat e2e/src/pages/BasePage.ts

Step 2: 修复 scrollToTop 方法

e2e/src/pages/BasePage.ts 中:

// 修改前
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: 运行滚动测试

cd e2e && npm test -- home-page.smoke.spec.ts -g "应该能够滚动到页面顶部"

预期: PASS

Step 4: 提交修改

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 行:

// 修改前
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: 运行区块可见性测试

cd e2e && npm test -- home-page.smoke.spec.ts -g "应该显示.*区块标题"

预期: PASS

Step 4: 提交修改

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 行附近:

// 修改前
<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: 添加页面描述元素

// 修改前
<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 行附近添加:

// 添加新属性
readonly pageBadge: Locator;
readonly pageDescription: Locator;

// 在构造函数中初始化
this.pageBadge = page.locator('[data-testid="page-badge"]');
this.pageDescription = page.locator('[data-testid="page-description"]');

Step 4: 添加获取方法

async getBadgeText(): Promise<string> {
  return await this.pageBadge.textContent() || '';
}

async getPageDescription(): Promise<string> {
  return await this.pageDescription.textContent() || '';
}

Step 5: 运行测试

cd e2e && npm test -- contact-page.smoke.spec.ts -g "应该显示页面"

预期: PASS

Step 6: 提交修改

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 组件

cat src/components/layout/footer.tsx | head -50

Step 2: 为 Footer 添加测试标识

src/components/layout/footer.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 行:

// 修改前
this.footer = page.locator('footer');

// 修改后
this.footer = page.locator('[data-testid="footer"]');

Step 4: 改进页脚等待逻辑

async waitForFooter(): Promise<void> {
  await this.scrollToBottom();
  await this.page.waitForLoadState('networkidle');
  await this.footer.waitFor({ state: 'visible', timeout: 10000 });
}

Step 5: 运行页脚测试

cd e2e && npm test -- home-page.smoke.spec.ts -g "应该显示页脚"

预期: PASS

Step 6: 提交修改

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 行:

// 修改前
<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:

test('应该显示立即咨询按钮', async ({ homePage }) => {
  const consultButton = homePage.page.locator('[data-testid="consult-button"]');
  await expect(consultButton).toBeVisible();
});

Step 3: 运行测试

cd e2e && npm test -- home-page.smoke.spec.ts -g "应该显示立即咨询按钮"

预期: PASS

Step 4: 提交修改

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:

// 修改前
timeout: 60000,
expect: {
  timeout: 30000,
},

// 修改后
timeout: 90000,
expect: {
  timeout: 45000,
  toHaveScreenshot: { timeout: 60000 },
},

Step 2: 添加测试夹具超时配置

e2e/src/fixtures/base.fixture.ts:

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: 运行完整测试套件

cd e2e && npm test -- --retries=2

Step 4: 提交修改

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: 运行完整冒烟测试套件

cd e2e && npm test -- --grep "@smoke" --reporter=html

Step 2: 生成测试报告

cd e2e && npm run test:report

Step 3: 分析剩余失败

检查 HTML 报告,识别剩余的失败测试:

open e2e/playwright-report/index.html

Step 4: 创建修复总结文档

创建 docs/test-fix-summary.md:

# 冒烟测试修复总结

## 修复前统计
- 总测试数: 1083
- 通过: ~850 (78.5%)
- 失败: ~233 (21.5%)

## 修复后统计
- 总测试数: 1083
- 通过: [填写实际数字]
- 失败: [填写实际数字]
- 通过率: [填写实际百分比]

## 已修复问题
1. ✅ 导航菜单选择器
2. ✅ 联系页面元素选择器
3. ✅ 表单输入字段选择器
4. ✅ 滚动到顶部功能
5. ✅ 区块滚动和可见性
6. ✅ 页面徽章和描述
7. ✅ 页脚可见性
8. ✅ 立即咨询按钮
9. ✅ 测试超时设置

## 剩余问题
[列出剩余的失败测试]

## 下一步计划
[列出后续优化建议]

Step 5: 提交最终修改

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. 优化测试执行时间