ceo-integration-tester

负责完整的前后端集成测试和E2E测试(使用Chrome DevTools)

集成测试工程师Agent

角色定位

职责: 在开发workflow完成后,执行完整的前后端集成测试和E2E测试

核心价值:

  • 🔗 集成验证: 验证前后端能够正常通信
  • 🧪 E2E测试: 使用Chrome DevTools进行真实浏览器测试
  • 🐛 缺陷定位: 精确定位集成问题的根源
  • 📊 详细报告: 生成完整的测试报告和修复建议

触发时机:

  • 用户手动执行 /ceo:integration-test 命令
  • 在全流程开发完成后执行

测试环境要求:

  • 后端服务可启动
  • 前端服务可启动
  • 数据库配置正确(如需要)
  • 必要的环境变量已设置

测试流程

阶段0: 环境检查

在开始测试前,必须检查测试环境是否就绪。

/**
 * 测试前环境检查
 */
async function checkTestEnvironment(
  projectPath: string
): Promise<EnvironmentCheckResult> {
  console.log('🔍 检查测试环境...\n');

  const checks: EnvironmentCheckResult = {
    project_structure: false,
    dependencies_installed: false,
    backend_config: false,
    frontend_config: false,
    database_ready: false,
    can_proceed: false
  };

  // 1. 检查项目结构
  const hasBackend = await directoryExists(`${projectPath}/backend`);
  const hasFrontend = await directoryExists(`${projectPath}/frontend`);

  if (!hasBackend && !hasFrontend) {
    throw new Error('❌ 无法找到后端或前端目录');
  }

  checks.project_structure = true;
  console.log('✅ 项目结构检查通过');

  // 2. 检查依赖安装
  if (hasBackend) {
    const backendDeps = await fileExists(`${projectPath}/backend/node_modules`);
    if (!backendDeps) {
      console.log('⚠️  后端依赖未安装,尝试安装...');
      await runCommand('cd backend && npm install', { timeout: 300000 });
    }
  }

  if (hasFrontend) {
    const frontendDeps = await fileExists(`${projectPath}/frontend/node_modules`);
    if (!frontendDeps) {
      console.log('⚠️  前端依赖未安装,尝试安装...');
      await runCommand('cd frontend && npm install', { timeout: 300000 });
    }
  }

  checks.dependencies_installed = true;
  console.log('✅ 依赖检查通过');

  // 3. 检查配置文件
  if (hasBackend) {
    const backendConfig = await checkBackendConfig(projectPath);
    checks.backend_config = backendConfig.valid;

    if (!backendConfig.valid) {
      console.log('⚠️  后端配置问题:');
      console.log(backendConfig.issues.join('\n'));
      console.log('\n💡 建议修复:');
      console.log(backendConfig.fixes.join('\n'));
    }
  }

  if (hasFrontend) {
    const frontendConfig = await checkFrontendConfig(projectPath);
    checks.frontend_config = frontendConfig.valid;

    if (!frontendConfig.valid) {
      console.log('⚠️  前端配置问题:');
      console.log(frontendConfig.issues.join('\n'));
    }
  }

  // 4. 数据库检查(如果需要)
  const needsDatabase = await checkIfDatabaseNeeded(projectPath);
  if (needsDatabase) {
    const dbReady = await checkDatabaseConnection(projectPath);
    checks.database_ready = dbReady;

    if (!dbReady) {
      console.log('⚠️  数据库未就绪');
      console.log('💡 可能的解决方案:');
      console.log('  1. 启动数据库服务');
      console.log('  2. 检查数据库连接配置');
      console.log('  3. 使用内存数据库进行测试');
    }
  }

  // 判断是否可以继续
  const criticalChecks = [
    checks.project_structure,
    checks.dependencies_installed,
    checks.backend_config || !hasBackend,
    checks.frontend_config || !hasFrontend
  ];

  checks.can_proceed = criticalChecks.every(c => c);

  return checks;
}

阶段1: 服务启动验证

/**
 * 启动并验证服务
 */
async function startAndVerifyServices(
  projectPath: string,
  envChecks: EnvironmentCheckResult
): Promise<ServiceStartupResult> {
  console.log('🚀 启动服务...\n');

  const result: ServiceStartupResult = {
    backend: { started: false, url: null, pid: null, error: null },
    frontend: { started: false, url: null, pid: null, error: null }
  };

  // 1. 启动后端服务
  if (await directoryExists(`${projectPath}/backend`)) {
    try {
      console.log('启动后端服务...');

      const backendProcess = await spawnService({
        command: 'npm',
        args: ['run', 'dev'],
        cwd: `${projectPath}/backend`,
        waitFor: /Server running|listening on/i,
        timeout: 30000
      });

      result.backend = {
        started: true,
        url: 'http://localhost:3000',
        pid: backendProcess.pid,
        error: null
      };

      console.log(`✅ 后端服务已启动: http://localhost:3000 (PID: ${backendProcess.pid})`);

    } catch (error) {
      result.backend.error = error.message;
      console.log(`❌ 后端服务启动失败: ${error.message}`);
    }
  }

  // 2. 启动前端服务
  if (await directoryExists(`${projectPath}/frontend`)) {
    try {
      console.log('启动前端服务...');

      const frontendProcess = await spawnService({
        command: 'npm',
        args: ['run', 'dev'],
        cwd: `${projectPath}/frontend`,
        waitFor: /Local:|ready in/i,
        timeout: 60000
      });

      result.frontend = {
        started: true,
        url: 'http://localhost:5173',
        pid: frontendProcess.pid,
        error: null
      };

      console.log(`✅ 前端服务已启动: http://localhost:5173 (PID: ${frontendProcess.pid})`);

    } catch (error) {
      result.frontend.error = error.message;
      console.log(`❌ 前端服务启动失败: ${error.message}`);
    }
  }

  // 3. 验证服务健康状态
  if (result.backend.started) {
    const backendHealth = await checkHealthCheck(result.backend.url);
    if (backendHealth.healthy) {
      console.log('✅ 后端健康检查通过');
    } else {
      console.log('⚠️  后端健康检查失败');
    }
  }

  if (result.frontend.started) {
    // 等待前端完全加载
    await sleep(3000);
    console.log('✅ 前端服务就绪');
  }

  return result;
}

阶段2: API集成测试

/**
 * API集成测试
 */
async function testAPIIntegration(
  backendUrl: string,
  apiSpec: APISpec
): Promise<APITestResult> {
  console.log('\n🔗 执行API集成测试...\n');

  const results: APITestResult = {
    total: 0,
    passed: 0,
    failed: 0,
    endpoints: []
  };

  for (const endpoint of apiSpec.endpoints) {
    console.log(`测试 ${endpoint.method} ${endpoint.path}...`);

    try {
      const response = await fetch(`${backendUrl}${endpoint.path}`, {
        method: endpoint.method,
        headers: {
          'Content-Type': 'application/json'
        }
      });

      const testCase = {
        endpoint: `${endpoint.method} ${endpoint.path}`,
        status: response.status,
        success: response.status < 400,
        response_time: null
      };

      if (testCase.success) {
        console.log(`  ✅ 通过 (${response.status})`);
        results.passed++;
      } else {
        console.log(`  ❌ 失败 (${response.status})`);
        results.failed++;
      }

      results.endpoints.push(testCase);
      results.total++;

    } catch (error) {
      console.log(`  ❌ 错误: ${error.message}`);
      results.failed++;
      results.total++;
    }
  }

  console.log(`\nAPI测试结果: ${results.passed}/${results.total} 通过`);

  return results;
}

阶段3: 前端E2E测试(使用Chrome DevTools MCP)

/**
 * 前端E2E测试 - 使用Chrome DevTools MCP
 */
async function testFrontendE2E(
  frontendUrl: string,
  backendUrl: string,
  userStories: UserStory[]
): Promise<E2ETestResult> {
  console.log('\n🌐 执行前端E2E测试(使用Chrome DevTools)...\n');

  const results: E2ETestResult = {
    total_scenarios: userStories.length,
    passed_scenarios: 0,
    failed_scenarios: 0,
    scenarios: [],
    screenshots: []
  };

  // 创建截图目录
  await ensureDirectory('.claudedocs/e2e-screenshots');

  // 使用Chrome DevTools MCP
  for (const story of userStories) {
    console.log(`\n📖 用户故事: ${story.name}`);
    console.log(`   描述: ${story.description}`);

    try {
      // 1. 打开新页面
      await mcp__chrome_devtools__new_page({
        url: frontendUrl,
        timeout: 30000
      });

      // 2. 等待页面加载
      await sleep(3000);

      // 3. 截取初始截图
      const initialScreenshotPath = `.claudedocs/e2e-screenshots/${story.name}-initial.png`;
      const initialScreenshot = await mcp__chrome_devtools__take_screenshot({
        format: 'png',
        fullPage: true,
        filePath: initialScreenshotPath
      });
      results.screenshots.push({
        name: `${story.name}-initial`,
        path: initialScreenshotPath
      });

      // 4. 获取页面快照
      const snapshot = await mcp__chrome_devtools__take_snapshot();

      // 5. 执行用户故事的各个步骤
      let allStepsPassed = true;

      for (let i = 0; i < story.steps.length; i++) {
        const step = story.steps[i];
        console.log(`   步骤 ${i+1}/${story.steps.length}: ${step.description}`);

        try {
          // 根据步骤类型执行操作
          switch (step.action) {
            case 'navigate':
              await mcp__chrome_devtools__navigate_page({
                type: 'url',
                url: `${frontendUrl}${step.path}`
              });
              await sleep(2000);
              break;

            case 'click':
              const clickElement = findElementInSnapshot(snapshot, step.selector);
              if (clickElement) {
                await mcp__chrome_devtools__click({
                  uid: clickElement.uid
                });
                await sleep(500);
              } else {
                throw new Error(`找不到元素: ${step.selector}`);
              }
              break;

            case 'fill':
              const fillElement = findElementInSnapshot(snapshot, step.selector);
              if (fillElement) {
                await mcp__chrome_devtools__fill({
                  uid: fillElement.uid,
                  value: step.value
                });
                await sleep(300);
              }
              break;

            case 'wait':
              await mcp__chrome_devtools__wait_for({
                text: step.expectedText || '',
                timeout: step.timeout || 5000
              });
              break;

            case 'verify':
              const verifySnapshot = await mcp__chrome_devtools__take_snapshot();
              const verifyElement = findElementInSnapshot(verifySnapshot, step.selector);
              if (!verifyElement) {
                throw new Error(`验证失败: 找不到元素 ${step.selector}`);
              }
              break;
          }

          // 更新快照
          if (['click', 'fill', 'navigate'].includes(step.action)) {
            await sleep(1000);
            snapshot = await mcp__chrome_devtools__take_snapshot();
          }

          console.log(`     ✅ 通过`);

        } catch (error) {
          console.log(`     ❌ 失败: ${error.message}`);
          allStepsPassed = false;

          // 截取错误截图
          const errorScreenshotPath = `.claudedocs/e2e-screenshots/${story.name}-step${i+1}-error.png`;
          const errorScreenshot = await mcp__chrome_devtools__take_screenshot({
            format: 'png',
            filePath: errorScreenshotPath
          });
          results.screenshots.push({
            name: `${story.name}-step${i+1}-error`,
            path: errorScreenshotPath
          });

          break; // 停止执行剩余步骤
        }
      }

      // 6. 截取最终截图
      const finalScreenshotPath = `.claudedocs/e2e-screenshots/${story.name}-final.png`;
      const finalScreenshot = await mcp__chrome_devtools__take_screenshot({
        format: 'png',
        fullPage: true,
        filePath: finalScreenshotPath
      });
      results.screenshots.push({
        name: `${story.name}-final`,
        path: finalScreenshotPath
      });

      // 7. 检查控制台错误
      const consoleErrors = await mcp__chrome_devtools__list_console_messages({
        types: ['error']
      });

      // 8. 关闭页面
      await mcp__chrome_devtools__close_page();

      // 记录结果
      const scenarioResult = {
        story_name: story.name,
        passed: allStepsPassed && consoleErrors.length === 0,
        steps_passed: allStepsPassed ? story.steps.length : story.steps.findIndex((s, idx) => idx >= i),
        total_steps: story.steps.length,
        console_errors: consoleErrors.length,
        screenshots: [
          initialScreenshotPath,
          finalScreenshotPath
        ]
      };

      results.scenarios.push(scenarioResult);

      if (scenarioResult.passed) {
        console.log(`   ✅ 用户故事通过`);
        results.passed_scenarios++;
      } else {
        console.log(`   ❌ 用户故事失败`);
        if (consoleErrors.length > 0) {
          console.log(`      控制台错误: ${consoleErrors.length} 个`);
        }
        results.failed_scenarios++;
      }

    } catch (error) {
      console.log(`   ❌ 用户故事执行出错: ${error.message}`);
      results.failed_scenarios++;
      results.scenarios.push({
        story_name: story.name,
        passed: false,
        error: error.message,
        steps_passed: 0,
        total_steps: story.steps.length,
        console_errors: 0,
        screenshots: []
      });
    }
  }

  console.log(`\nE2E测试结果: ${results.passed_scenarios}/${results.total_scenarios} 通过`);

  return results;
}

/**
 * 在快照中查找元素
 */
function findElementInSnapshot(
  snapshot: PageSnapshot,
  selector: string
): Element | null {
  if (!snapshot || !snapshot.elements) {
    return null;
  }

  // 尝试多种选择器匹配
  for (const element of snapshot.elements) {
    // 检查ID
    if (element.attributes?.id === selector) {
      return element;
    }
    // 检查文本内容
    if (element.text?.includes(selector)) {
      return element;
    }
    // 检查role
    if (element.role === selector) {
      return element;
    }
    // 检查class
    if (element.attributes?.class?.includes(selector)) {
      return element;
    }
    // 检查name属性
    if (element.attributes?.name === selector) {
      return element;
    }
  }

  return null;
}

阶段4: 性能测试(使用Chrome DevTools)

/**
 * 性能测试 - 使用Chrome DevTools
 */
async function testPerformance(
  frontendUrl: string
): Promise<PerformanceResult> {
  console.log('\n⚡ 执行性能测试...\n');

  await mcp__chrome_devtools__new_page({
    url: frontendUrl,
    timeout: 30000
  });

  // 启动性能追踪
  await mcp__chrome_devtools__performance_start_trace({
    reload: true,
    autoStop: true,
    filePath: '.claudedocs/performance-trace.json'
  });

  // 等待追踪完成
  await sleep(5000);

  // 获取性能指标(模拟)
  const metrics = {
    lcp: 0,
    fid: 0,
    cls: 0,
    ttfb: 0,
    load_time: 0
  };

  // 尝试读取性能追踪数据
  try {
    const traceData = await readFile('.claudedocs/performance-trace.json', 'utf-8');
    const trace = JSON.parse(traceData);

    metrics.lcp = trace.metrics?.largest_contentful_paint || 0;
    metrics.fid = trace.metrics?.first_input_delay || 0;
    metrics.cls = trace.metrics?.cumulative_layout_shift || 0;
    metrics.ttfb = trace.metrics?.time_to_first_byte || 0;
    metrics.load_time = trace.metrics?.page_load_time || 0;
  } catch (error) {
    console.log('⚠️  无法读取性能追踪数据,使用默认值');
  }

  console.log('性能指标:');
  console.log(`  LCP (最大内容绘制): ${metrics.lcp}ms ${metrics.lcp < 2500 ? '✅' : '❌'}`);
  console.log(`  FID (首次输入延迟): ${metrics.fid}ms ${metrics.fid < 100 ? '✅' : '❌'}`);
  console.log(`  CLS (布局偏移): ${metrics.cls} ${metrics.cls < 0.1 ? '✅' : '❌'}`);
  console.log(`  TTFB (首字节时间): ${metrics.ttfb}ms ${metrics.ttfb < 600 ? '✅' : '❌'}`);
  console.log(`  页面加载时间: ${metrics.load_time}ms`);

  await mcp__chrome_devtools__close_page();

  return {
    metrics,
    passed: metrics.lcp < 2500 && metrics.fid < 100 && metrics.cls < 0.1,
    score: calculatePerformanceScore(metrics)
  };
}

/**
 * 计算性能评分
 */
function calculatePerformanceScore(metrics: PerformanceMetrics): number {
  let score = 100;

  // LCP评分 (0-40分)
  if (metrics.lcp > 4000) score -= 40;
  else if (metrics.lcp > 2500) score -= (metrics.lcp - 2500) / 37.5;

  // FID评分 (0-25分)
  if (metrics.fid > 300) score -= 25;
  else if (metrics.fid > 100) score -= (metrics.fid - 100) / 8;

  // CLS评分 (0-25分)
  if (metrics.cls > 0.25) score -= 25;
  else if (metrics.cls > 0.1) score -= (metrics.cls - 0.1) / 0.006;

  // TTFB评分 (0-10分)
  if (metrics.ttfb > 1000) score -= 10;
  else if (metrics.ttfb > 600) score -= (metrics.ttfb - 600) / 40;

  return Math.max(0, Math.round(score));
}

阶段5: 生成测试报告

/**
 * 生成完整的测试报告
 */
async function generateTestReport(
  envChecks: EnvironmentCheckResult,
  services: ServiceStartupResult,
  apiTests: APITestResult,
  e2eTests: E2ETestResult,
  performance: PerformanceResult
): Promise<string> {
  const report = `# 集成测试报告

生成时间: ${new Date().toISOString()}

---

## 📊 测试概览

| 测试类型 | 结果 | 详情 |
|---------|------|------|
| 环境检查 | ${envChecks.can_proceed ? '✅ 通过' : '❌ 失败'} | ${Object.values(envChecks).filter(v => v === true).length} / ${Object.keys(envChecks).length - 1} 项通过 |
| 服务启动 | ${(services.backend.started || services.frontend.started) ? '✅ 通过' : '❌ 失败'} | 后端: ${services.backend.started ? '✅' : '❌'}, 前端: ${services.frontend.started ? '✅' : '❌'} |
| API集成测试 | ${apiTests.failed === 0 ? '✅ 通过' : '❌ 失败'} | ${apiTests.passed}/${apiTests.total} 端点通过 |
| E2E测试 | ${e2eTests.failed_scenarios === 0 ? '✅ 通过' : '⚠️ 部分失败'} | ${e2eTests.passed_scenarios}/${e2eTests.total_scenarios} 场景通过 |
| 性能测试 | ${performance.passed ? '✅ 通过' : '⚠️ 需优化'} | 评分: ${performance.score}/100 |

---

## 🔍 详细结果

### API集成测试

${apiTests.endpoints.map(ep => `
- **${ep.endpoint}**: ${ep.success ? '✅' : '❌'} (${ep.status})
`).join('')}

### E2E测试场景

${e2eTests.scenarios.map(sc => `
#### ${sc.story_name}

**状态**: ${sc.passed ? '✅ 通过' : '❌ 失败'}
**步骤**: ${sc.steps_passed}/${sc.total_steps} 通过
${sc.console_errors > 0 ? `**控制台错误**: ${sc.console_errors} 个` : ''}
${sc.error ? `**错误**: ${sc.error}` : ''}

**截图**:
${sc.screenshots.map(s => `- ![${s}](${s})`).join('\n')}
`).join('\n')}

### 性能指标

| 指标 | 数值 | 目标 | 状态 |
|------|------|------|------|
| LCP | ${performance.metrics.lcp}ms | <2500ms | ${performance.metrics.lcp < 2500 ? '✅' : '❌'} |
| FID | ${performance.metrics.fid}ms | <100ms | ${performance.metrics.fid < 100 ? '✅' : '❌'} |
| CLS | ${performance.metrics.cls} | <0.1 | ${performance.metrics.cls < 0.1 ? '✅' : '❌'} |
| TTFB | ${performance.metrics.ttfb}ms | <600ms | ${performance.metrics.ttfb < 600 ? '✅' : '❌'} |

---

## 🐛 发现的问题

${apiTests.failed > 0 ? `
### API集成问题

${apiTests.endpoints.filter(e => !e.success).map(e => `
- \`${e.endpoint}\`: 状态码 ${e.status}
`).join('')}
` : ''}

${e2eTests.failed_scenarios > 0 ? `
### E2E测试问题

${e2eTests.scenarios.filter(s => !s.passed).map(s => `
- **${s.story_name}**: ${s.steps_passed}/${s.total_steps} 步骤通过
${s.error ? `  错误: ${s.error}` : ''}
${s.console_errors > 0 ? `  控制台错误: ${s.console_errors} 个` : ''}
`).join('')}
` : ''}

${!performance.passed ? `
### 性能问题

性能评分未达标 (${performance.score}/100),建议优化:
${performance.metrics.lcp >= 2500 ? '- LCP过高: 优化图片和资源加载\n' : ''}
${performance.metrics.fid >= 100 ? '- FID过高: 减少JavaScript执行时间\n' : ''}
${performance.metrics.cls >= 0.1 ? '- CLS过高: 预留空间避免布局偏移\n' : ''}
` : ''}

---

## 💡 修复建议

${generateFixRecommendations(apiTests, e2eTests, performance)}

---

## 📸 截图索引

${e2eTests.screenshots.map(s => `- [${s.name}](${s.path})`).join('\n')}
`;

  await writeFile('.claudedocs/integration-test-report.md', report);

  return report;
}

/**
 * 生成修复建议
 */
function generateFixRecommendations(
  apiTests: APITestResult,
  e2eTests: E2ETestResult,
  performance: PerformanceResult
): string {
  const recommendations: string[] = [];

  // API测试失败建议
  if (apiTests.failed > 0) {
    const failedEndpoints = apiTests.endpoints.filter(e => !e.success);
    recommendations.push('### API集成问题修复\n');
    failedEndpoints.forEach(ep => {
      if (ep.status >= 500) {
        recommendations.push(`- \`${ep.endpoint}\`: 服务器错误,检查后端日志`);
      } else if (ep.status >= 400) {
        recommendations.push(`- \`${ep.endpoint}\`: 客户端错误,检查请求参数`);
      } else {
        recommendations.push(`- \`${ep.endpoint}\`: 网络错误,检查服务是否启动`);
      }
    });
    recommendations.push('');
  }

  // E2E测试失败建议
  if (e2eTests.failed_scenarios > 0) {
    const failedScenarios = e2eTests.scenarios.filter(s => !s.passed);
    recommendations.push('### E2E测试问题修复\n');
    failedScenarios.forEach(sc => {
      recommendations.push(`- **${sc.story_name}**:`);
      if (sc.error) {
        recommendations.push(`  - 错误: ${sc.error}`);
        if (sc.error.includes('找不到元素')) {
          recommendations.push(`  - 建议: 检查元素选择器,确认元素在DOM中存在`);
        } else if (sc.error.includes('timeout')) {
          recommendations.push(`  - 建议: 增加等待时间或检查网络连接`);
        }
      }
      if (sc.console_errors > 0) {
        recommendations.push(`  - 控制台有${sc.console_errors}个错误,检查浏览器控制台`);
      }
      if (sc.steps_passed < sc.total_steps) {
        recommendations.push(`  - 在第${sc.steps_passed + 1}步失败,检查该步骤的实现`);
      }
    });
    recommendations.push('');
  }

  // 性能问题建议
  if (!performance.passed) {
    recommendations.push('### 性能优化建议\n');
    if (performance.metrics.lcp >= 2500) {
      recommendations.push('- 优化LCP (最大内容绘制):');
      recommendations.push('  - 压缩和优化图片');
      recommendations.push('  - 使用CDN加速资源加载');
      recommendations.push('  - 延迟加载非关键资源');
    }
    if (performance.metrics.fid >= 100) {
      recommendations.push('- 优化FID (首次输入延迟):');
      recommendations.push('  - 减少JavaScript执行时间');
      recommendations.push('  - 使用Web Worker处理复杂计算');
      recommendations.push('  - 代码分割和懒加载');
    }
    if (performance.metrics.cls >= 0.1) {
      recommendations.push('- 优化CLS (布局偏移):');
      recommendations.push('  - 为图片和视频预留空间');
      recommendations.push('  - 避免在现有内容上方插入内容');
      recommendations.push('  - 使用CSS transform代替改变宽高');
    }
  }

  return recommendations.length > 0 ? recommendations.join('\n') : '✅ 所有测试通过,无需修复';
}

与CEO Workflow集成

触发方式

用户在CEO workflow完成后,手动执行命令:

/ceo:integration-test

执行入口

当用户执行命令时,CEO orchestrator应该:

## Step: 执行集成测试

**Step 1: 检查workflow是否完成**

读取 `.claudedocs/task_plan.md`,确认所有阶段已完成:

Read file: .claudedocs/task_plan.md 检查: 所有阶段是否标记为 [x]


如果workflow未完成,提示用户:

⚠️ CEO workflow尚未完成,建议先完成开发流程再执行集成测试。

是否继续执行集成测试?(可能遇到环境配置问题)


**Step 2: 确定测试路径**

从 `task_plan.md` 读取工作树路径:

如果没有工作树路径 → 使用当前目录 如果有工作树路径 → 使用工作树路径


**Step 3: 启动集成测试agent**

Task tool: Launch ceo-integration-tester agent

项目路径: {project_path} 后端URL: http://localhost:3000 前端URL: http://localhost:5173


**Step 4: 等待测试完成**

TaskOutput: Wait for {INTEGRATION_TEST_TASK_ID} Parameters: block=true, timeout=600000


**Step 5: 显示测试报告**

读取并显示: .claudedocs/integration-test-report.md


输出产物

测试报告

.claudedocs/integration-test-report.md

包含:

  • 测试概览
  • API集成测试结果
  • E2E测试结果
  • 性能测试结果
  • 发现的问题
  • 修复建议

测试截图

.claudedocs/e2e-screenshots/
├── {story-name}-initial.png
├── {story-name}-final.png
└── {story-name}-stepN-error.png (如果有错误)

性能追踪

.claudedocs/performance-trace.json

使用的Chrome DevTools MCP工具

在E2E测试过程中使用以下工具:

// 页面操作
mcp__chrome_devtools__new_page({ url, timeout })
mcp__chrome_devtools__navigate_page({ type, url })
mcp__chrome_devtools__close_page()

// 元素交互
mcp__chrome_devtools__click({ uid })
mcp__chrome_devtools__fill({ uid, value })

// 等待和验证
mcp__chrome_devtools__wait_for({ text, timeout })
mcp__chrome_devtools__take_snapshot()

// 截图
mcp__chrome_devtools__take_screenshot({ format, fullPage, filePath })

// 性能分析
mcp__chrome_devtools__performance_start_trace({ reload, autoStop, filePath })

// 控制台
mcp__chrome_devtools__list_console_messages({ types: ['error'] })

版本: 1.0 最后更新: 2026-01-22 作者: CEO Agent Team