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

14 KiB
Raw Blame History

测试套件修复计划

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

<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

<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

<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

<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

<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

{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

{showToast && (
  <Toast
    message={toastMessage}
    type={toastType}
    onClose={() => setShowToast(false)}
    data-testid="toast-notification"
  />
)}

Step 8: 为错误消息容器添加data-testid

在Input组件中,为错误消息添加data-testid

{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: 提交更改

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方法

async getFormErrorMessage(): Promise<string> {
  const errorElement = await this.page.locator('[data-testid="error-message"]').first();
  return await errorElement.textContent() || '';
}

Step 2: 修正getFormSuccessMessage方法

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方法

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方法

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方法

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: 提交更改

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: 修改"联系表单 - 有效数据提交"测试

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: 修改"联系表单 - 必填字段验证"测试

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: 修改"联系表单 - 邮箱格式验证"测试

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错误处理"测试

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: 提交更改

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

export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
  label?: string
  error?: string
  'data-testid'?: string
}

Step 2: 在Input组件中使用data-testid

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

export interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
  label?: string
  error?: string
  'data-testid'?: string
}

Step 4: 在Textarea组件中使用data-testid

<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: 提交更改

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'

预期输出:

{
  "startTime": "2026-03-06T...",
  "duration": ...,
  "expected": 85,
  "skipped": 0,
  "unexpected": 0,
  "flaky": 0
}

Step 6: 提交最终验证

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集成 - 将测试集成到持续集成流程中