mcpField
V
visual-evidence

visual-evidence

github.com/fellowship-dev测试与质量3176 次安装2 周前更新
代码仓库

关于 visual-evidence

visual-evidence 是用于在拉取请求和报告中记录界面变更的技能。它通过无头 Playwright Chromium 截取屏幕截图或交互 GIF,并上传供嵌入使用;采集失败不应阻塞流水线。

主要功能

  1. 01无头 Chromium 截图
  2. 02录制交互 GIF
  3. 03通过 evidence-upload 上传云端
  4. 04提供 PR 证据嵌入指引
  5. 05尽力执行,限时 120 秒

适用场景

  1. 01对比修复前后的界面
  2. 02展示新功能的关键流程
  3. 03检查加载或验证交互
  4. 04在拉取请求中记录视觉变更

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/fellowship-dev/dogfooded-skills --skill visual-evidence

安装后在 Claude Code 中输入/visual-evidence确认已加载。

详细介绍

visual-evidence

使用 Playwright 无头 Chromium 捕获浏览器截图并录制 GIF。上传到云存储,以便嵌入 PR 和报告中。

适用场景

  • 完成 Bug 修复后(对比修复前后)
  • 实现功能后(演示关键流程)
  • PR 审查期间验证视觉变化
  • 交互或时序 Bug(双击、加载状态、竞态条件)→ 使用 GIF 录制

不适用场景

  • 仅涉及后端的更改、配置/基础设施、仅测试内容、命令行工具、没有 UI 的 API
  • 如果 Playwright 安装失败则跳过 — 视觉证据尽力而为,绝不会阻塞流水线

前置条件

在远程环境中安装 Playwright 和 Chromium:

cd /tmp && npm install playwright 2>&1 | tail -3 && \
  npx playwright install-deps chromium 2>&1 | tail -3 && \
  npx playwright install chromium 2>&1 | tail -3 && \
  echo PW_READY

录制 GIF 时,还需安装 ffmpeg:

sudo apt-get update -qq && sudo apt-get install -y -qq ffmpeg 2>&1 | tail -1 && echo FFMPEG_READY

工作流

1. 捕获截图

创建 /tmp/screenshot.mjs:

import { chromium } from '/tmp/node_modules/playwright/index.mjs';
const [url, output] = process.argv.slice(2);
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage({ viewport: { width: 1280, height: 720 } });
console.log(`Navigating to ${url}...`);
await page.goto(url, { waitUntil: 'networkidle', timeout: 30000 });
const title = await page.title();
console.log(`Page title: ${title}`);
await page.screenshot({ path: output, fullPage: true });
console.log(`Screenshot saved: ${output}`);
await browser.close();

用法:

node /tmp/screenshot.mjs http://localhost:3000 /tmp/evidence-home.png
node /tmp/screenshot.mjs http://localhost:3000/admin /tmp/evidence-admin.png
2. 录制 GIF(交互证据)

创建 /tmp/record.mjs:

import { chromium } from '/tmp/node_modules/playwright/index.mjs';
const [url, output, ...actions] = process.argv.slice(2);
const browser = await chromium.launch({ headless: true });
const context = await browser.newContext({
  viewport: { width: 1280, height: 720 },
  recordVideo: { dir: '/tmp/videos', size: { width: 1280, height: 720 } }
});
const page = await context.newPage();
console.log(`Navigating to ${url}...`);
await page.goto(url, { waitUntil: 'networkidle', timeout: 30000 });

// Execute actions passed as JSON array
if (actions[0]) {
  const steps = JSON.parse(actions[0]);
  for (const step of steps) {
    if (step.action === 'click') {
      console.log(`Clicking ${step.selector}...`);
      await page.click(step.selector);
    } else if (step.action === 'dblclick') {
      console.log(`Double-clicking ${step.selector}...`);
      await page.click(step.selector);
      await page.waitForTimeout(200);
      await page.click(step.selector);
    } else if (step.action === 'wait') {
      console.log(`Waiting ${step.ms}ms...`);
      await page.waitForTimeout(step.ms);
    } else if (step.action === 'fill') {
      console.log(`Filling ${step.selector}...`);
      await page.fill(step.selector, step.value);
    } else if (step.action === 'screenshot') {
      console.log(`Snapshot: ${step.label}`);
      await page.screenshot({ path: `/tmp/evidence-${step.label}.png` });
    }
  }
}

await page.waitForTimeout(500);
await page.close();
await context.close();
await browser.close();

// Convert to GIF
const { execSync } = await import('child_process');
const webm = execSync('ls -t /tmp/videos/*.webm | head -1').toString().trim();
console.log(`Video: ${webm}`);
execSync(`ffmpeg -y -i "${webm}" -vf "fps=10,scale=1280:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" -loop 0 "${output}" 2>&1 | tail -3`);
console.log(`GIF saved: ${output}`);

用法:

node /tmp/record.mjs http://localhost:3000/form /tmp/evidence-before.gif \
  '[{"action":"click","selector":".submit-btn"},{"action":"wait","ms":2000}]'
3. 上传到云存储

使用 evidence-upload 技能,将截图和 GIF 上传到 pylot 资源后端。$PYLOT_GATEWAY_URL 和 $PYLOT_DISPATCH_TOKEN 已存在于每个操作员/工作节点环境中 — 无需静态 AWS 密钥。

对每个捕获的文件运行 /evidence-upload,并记录返回的 public_url,以便在第 4 步中嵌入。

4. 嵌入 PR

修复前/后(Bug 修复):

## Visual Evidence

<details>
<summary>Before / After</summary>

**Before (bug present on default branch):**
![before](<url>/before.png)

**After (fix applied):**
![after](<url>/after.png)

</details>

交互演示(GIF):

## Visual Evidence

<details>
<summary>Interaction demo</summary>

![demo](<url>/demo.gif)
*Caption: what this demonstrates*

</details>

决策表:截图还是 GIF

场景格式
视觉布局变化截图(PNG)
新页面/功能截图(PNG)
点击时禁用按钮GIF
加载微调器/骨架屏GIF
防止双击GIF
表单验证反馈GIF
过渡/动画GIF
错误 → 重试流程GIF

错误处理

视觉证据尽力而为。绝不要阻塞流水线。

  • Playwright 安装失败 → 记录日志、跳过证据、继续
  • 开发服务器无法启动 → 记录日志、跳过证据、继续
  • 截图失败(404、超时)→ 记录日志、跳过证据、继续
  • 上传失败(无凭据、权限被拒)→ 记录日志、跳过证据、继续
  • **硬超时:**整个证据阶段 120 秒。超时后终止并继续。

关键规则

  • 绝不要阻塞流水线 — 证据尽力而为
  • 硬超时 120 秒 — 终止并继续
  • 嵌入 PR 中的 URL 前务必验证上传结果
  • /tmp/ 中的截图是临时文件 — 环境停止后会消失

评论

支持 Markdown