关于 visual-evidence
visual-evidence 是用于在拉取请求和报告中记录界面变更的技能。它通过无头 Playwright Chromium 截取屏幕截图或交互 GIF,并上传供嵌入使用;采集失败不应阻塞流水线。
主要功能
- 01无头 Chromium 截图
- 02录制交互 GIF
- 03通过 evidence-upload 上传云端
- 04提供 PR 证据嵌入指引
- 05尽力执行,限时 120 秒
适用场景
- 01对比修复前后的界面
- 02展示新功能的关键流程
- 03检查加载或验证交互
- 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):**

**After (fix applied):**

</details>
交互演示(GIF):
## Visual Evidence
<details>
<summary>Interaction demo</summary>

*Caption: what this demonstrates*
</details>
决策表:截图还是 GIF
| 场景 | 格式 |
|---|---|
| 视觉布局变化 | 截图(PNG) |
| 新页面/功能 | 截图(PNG) |
| 点击时禁用按钮 | GIF |
| 加载微调器/骨架屏 | GIF |
| 防止双击 | GIF |
| 表单验证反馈 | GIF |
| 过渡/动画 | GIF |
| 错误 → 重试流程 | GIF |
错误处理
视觉证据尽力而为。绝不要阻塞流水线。
- Playwright 安装失败 → 记录日志、跳过证据、继续
- 开发服务器无法启动 → 记录日志、跳过证据、继续
- 截图失败(404、超时)→ 记录日志、跳过证据、继续
- 上传失败(无凭据、权限被拒)→ 记录日志、跳过证据、继续
- **硬超时:**整个证据阶段 120 秒。超时后终止并继续。
关键规则
- 绝不要阻塞流水线 — 证据尽力而为
- 硬超时 120 秒 — 终止并继续
- 嵌入 PR 中的 URL 前务必验证上传结果
/tmp/中的截图是临时文件 — 环境停止后会消失
评论
支持 Markdown