mirror of
https://github.com/jackwener/OpenCLI.git
synced 2026-09-14 18:25:42 +08:00
78d52d984b
* test(plugin): add E2E integration tests for plugin lifecycle Add plugin-management.test.ts covering the full plugin lifecycle using real GitHub clone of opencli-plugin-hot-digest: - plugin install from github:ByteYue/opencli-plugin-hot-digest - plugin list (table and JSON formats) - plugin update on installed plugin - plugin uninstall with cleanup verification - error paths: invalid source, non-existent plugin, missing args Tests safely backup/restore existing plugin state to avoid interfering with user's real installed plugins. Update TESTING.md to document the new test file. * test(plugin): isolate lifecycle e2e from user home * fix(plugin): respect HOME env var for test isolation The E2E tests for plugin management were failing because os.homedir() doesn't respect the HOME environment variable. This made test isolation impossible since all tests would use the real ~/.opencli directory. Added getHomeDir() helper that checks process.env.HOME first before falling back to os.homedir(). Updated readLockFile() and writeLockFile() to use this new function. Fixes test failures in plugin-management.test.ts where: - plugin install would write to real home instead of temp dir - lock file assertions would fail with ENOENT --------- Co-authored-by: jackwener <jakevingoo@gmail.com>
10 KiB
10 KiB
Testing Guide
面向开发者和 AI Agent 的测试参考手册。
目录
测试架构
测试分为三层,全部使用 vitest 运行:
tests/
├── e2e/ # E2E 集成测试(子进程运行真实 CLI)
│ ├── helpers.ts # runCli() / parseJsonOutput() 共享工具
│ ├── public-commands.test.ts # 公开 API 命令
│ ├── browser-public.test.ts # 浏览器命令(公开数据)
│ ├── browser-auth.test.ts # 需登录命令(graceful failure)
│ ├── management.test.ts # 管理命令(list / validate / verify / help)
│ └── output-formats.test.ts # 输出格式校验
├── smoke/
│ └── api-health.test.ts # 外部 API、adapter 定义、命令注册健康检查
src/
└── **/*.test.ts # 单元测试(当前 32 个文件)
| 层 | 位置 | 当前文件数 | 运行方式 | 用途 |
|---|---|---|---|---|
| 单元测试 | src/**/*.test.ts |
32 | npx vitest run src/ |
内部模块、pipeline、adapter 工具函数 |
| E2E 测试 | tests/e2e/*.test.ts |
5 | npx vitest run tests/e2e/ |
真实 CLI 命令执行 |
| 烟雾测试 | tests/smoke/*.test.ts |
1 | npx vitest run tests/smoke/ |
外部 API 与注册完整性 |
当前覆盖范围
单元测试(32 个文件)
| 领域 | 文件 |
|---|---|
| 核心运行时与输出 | src/browser.test.ts, src/browser/dom-snapshot.test.ts, src/build-manifest.test.ts, src/capabilityRouting.test.ts, src/doctor.test.ts, src/engine.test.ts, src/interceptor.test.ts, src/output.test.ts, src/plugin.test.ts, src/registry.test.ts, src/snapshotFormatter.test.ts |
| pipeline 与下载 | src/download/index.test.ts, src/pipeline/executor.test.ts, src/pipeline/template.test.ts, src/pipeline/transform.test.ts |
| 站点 / adapter 逻辑 | src/clis/apple-podcasts/commands.test.ts, src/clis/apple-podcasts/utils.test.ts, src/clis/bloomberg/utils.test.ts, src/clis/chaoxing/utils.test.ts, src/clis/coupang/utils.test.ts, src/clis/google/utils.test.ts, src/clis/grok/ask.test.ts, src/clis/twitter/timeline.test.ts, src/clis/weread/utils.test.ts, src/clis/xiaohongshu/creator-note-detail.test.ts, src/clis/xiaohongshu/creator-notes-summary.test.ts, src/clis/xiaohongshu/creator-notes.test.ts, src/clis/xiaohongshu/search.test.ts, src/clis/xiaohongshu/user-helpers.test.ts, src/clis/xiaoyuzhou/utils.test.ts, src/clis/youtube/transcript-group.test.ts, src/clis/zhihu/download.test.ts |
这些测试覆盖的重点包括:
- Browser Bridge、DOM snapshot、interceptor、capability routing
- manifest 生成、命令发现、插件安装与注册表
- 输出格式渲染与 snapshot formatting
- pipeline 模板求值、执行器与变换步骤
- 各站点 adapter 的数据归一化、参数处理与容错逻辑
E2E 测试(5 个文件)
| 文件 | 当前覆盖范围 |
|---|---|
tests/e2e/public-commands.test.ts |
bloomberg、apple-podcasts、hackernews、v2ex、xiaoyuzhou、google suggest 等公开命令 |
tests/e2e/browser-public.test.ts |
bbc、bloomberg、bilibili、weibo、zhihu、reddit、twitter、xueqiu、reuters、youtube、smzdm、boss、ctrip、coupang、xiaohongshu、google、yahoo-finance、v2ex daily |
tests/e2e/browser-auth.test.ts |
bilibili、twitter、v2ex、xueqiu、linux-do、xiaohongshu 的需登录命令 graceful failure |
tests/e2e/management.test.ts |
list、validate、verify、--version、--help、unknown command |
tests/e2e/output-formats.test.ts |
json / yaml / csv / md 输出格式校验 |
tests/e2e/plugin-management.test.ts |
plugin install / list / update / uninstall 全生命周期 |
烟雾测试(1 个文件)
| 文件 | 当前覆盖范围 |
|---|---|
tests/smoke/api-health.test.ts |
hackernews、v2ex 公开 API 可用性,validate 全量 adapter 校验,以及命令注册表基础完整性 |
快速核对命令
需要刷新测试清单时,直接以仓库文件为准:
find src -name '*.test.ts' | sort
find tests/e2e -name '*.test.ts' | sort
find tests/smoke -name '*.test.ts' | sort
本地运行测试
前置条件
npm ci # 安装依赖
npm run build # 编译(E2E / smoke 测试需要 dist/main.js)
运行命令
# 全部单元测试
npx vitest run src/
# 全部 E2E 测试(会真实调用外部 API / 浏览器)
npx vitest run tests/e2e/
# 全部 smoke 测试
npx vitest run tests/smoke/
# 单个测试文件
npx vitest run src/clis/apple-podcasts/commands.test.ts
npx vitest run tests/e2e/management.test.ts
# 全部测试
npx vitest run
# watch 模式(开发时推荐)
npx vitest src/
浏览器命令本地测试须知
- opencli 通过 Browser Bridge 扩展连接已运行的 Chrome 浏览器
- E2E 测试通过
tests/e2e/helpers.ts里的runCli()调用已构建的dist/main.js browser-public.test.ts使用tryBrowserCommand(),站点反爬或地域限制导致空数据时会 warn + passbrowser-auth.test.ts验证 graceful failure,重点是不 crash、不 hang、错误信息可控- 如需测试完整登录态,保持 Chrome 登录态并安装 Browser Bridge 扩展,再手动运行对应测试
如何添加新测试
新增 YAML Adapter(如 src/clis/producthunt/trending.yaml)
opencli validate的 E2E / smoke 测试会覆盖 adapter 结构校验- 根据 adapter 类型,在对应测试文件补一个
it()block
// 如果 browser: false(公开 API)→ tests/e2e/public-commands.test.ts
it('producthunt trending returns data', async () => {
const { stdout, code } = await runCli(['producthunt', 'trending', '--limit', '3', '-f', 'json']);
expect(code).toBe(0);
const data = parseJsonOutput(stdout);
expect(Array.isArray(data)).toBe(true);
expect(data.length).toBeGreaterThanOrEqual(1);
expect(data[0]).toHaveProperty('title');
}, 30_000);
// 如果 browser: true 但可公开访问 → tests/e2e/browser-public.test.ts
it('producthunt trending returns data', async () => {
const data = await tryBrowserCommand(['producthunt', 'trending', '--limit', '3', '-f', 'json']);
expectDataOrSkip(data, 'producthunt trending');
}, 60_000);
// 如果 browser: true 且需登录 → tests/e2e/browser-auth.test.ts
it('producthunt me fails gracefully without login', async () => {
await expectGracefulAuthFailure(['producthunt', 'me', '-f', 'json'], 'producthunt me');
}, 60_000);
新增管理命令(如 opencli export)
在 tests/e2e/management.test.ts 添加测试;如果新命令会影响输出格式,也同步补 tests/e2e/output-formats.test.ts。
新增内部模块
在对应源码旁创建 *.test.ts,优先和被测模块放在同一目录下,便于发现与维护。
决策流程图
新增功能 → 是内部模块? → 是 → src/ 下加 *.test.ts
↓ 否
是 CLI 命令? → browser: false? → tests/e2e/public-commands.test.ts
↓ true
公开数据? → tests/e2e/browser-public.test.ts
↓ 需登录
tests/e2e/browser-auth.test.ts
CI/CD 流水线
ci.yml
| Job | 触发条件 | 内容 |
|---|---|---|
build |
push/PR 到 main,dev |
tsc --noEmit + npm run build |
unit-test |
push/PR 到 main,dev |
Node 20 与 22 双版本运行 src/ 单元测试,按 2 shard 并行 |
smoke-test |
schedule 或 workflow_dispatch |
安装真实 Chrome,xvfb-run 执行 tests/smoke/ |
e2e-headed.yml
| Job | 触发条件 | 内容 |
|---|---|---|
e2e-headed |
push/PR 到 main,dev,或手动触发 |
安装真实 Chrome,xvfb-run 执行 tests/e2e/ |
E2E 与 smoke 都使用 ./.github/actions/setup-chrome 准备真实 Chrome,并通过 OPENCLI_BROWSER_EXECUTABLE_PATH 注入浏览器路径。
Sharding
单元测试使用 vitest 内置 shard,并在 Node 20 / 22 两个版本上运行:
strategy:
matrix:
node-version: ['20', '22']
shard: [1, 2]
steps:
- run: npx vitest run src/ --reporter=verbose --shard=${{ matrix.shard }}/2
浏览器模式
opencli 通过 Browser Bridge 扩展连接浏览器:
| 条件 | 模式 | 使用场景 |
|---|---|---|
| 扩展已安装 / 已连接 | Extension 模式 | 本地用户,连接已登录的 Chrome |
| 无扩展 token | CLI 自行拉起浏览器 | CI、无登录态或纯自动化场景 |
CI 中使用 OPENCLI_BROWSER_EXECUTABLE_PATH 指定真实 Chrome 路径:
env:
OPENCLI_BROWSER_EXECUTABLE_PATH: ${{ steps.setup-chrome.outputs.chrome-path }}
站点兼容性
GitHub Actions 的美国 runner 上,部分站点会因为地域限制、登录要求或反爬而返回空数据。当前 E2E 对这些场景采用 warn + pass 策略,避免偶发站点限制把整条 CI 打红。
| 站点 | CI 表现 | 常见原因 |
|---|---|---|
hackernews、bbc、v2ex、bloomberg |
通常返回数据 | 公开接口或公开页面 |
yahoo-finance、google |
通常返回数据 | 页面公开,但仍可能受限流影响 |
bilibili、zhihu、weibo、xiaohongshu、xueqiu |
容易空数据 | 地域限制、反爬、登录要求 |
reddit、twitter、youtube |
容易空数据 | 登录态、cookie、机器人检测 |
smzdm、boss、ctrip、coupang、linux-do |
结果波动较大 | 地域限制、风控或页面结构变动 |
如果需要更稳定的浏览器 E2E 结果,优先使用具备目标站点网络可达性的 self-hosted runner。