mirror of
https://github.com/jackwener/OpenCLI.git
synced 2026-09-14 18:25:42 +08:00
257e9ec4f8
# feat: add E2E testing infrastructure with real Chrome in CI ## What Establish a comprehensive E2E testing framework for opencli using **real Chrome + xvfb virtual display** in GitHub Actions CI. ## Changes ### E2E Test Suite (~52 test cases) - `public-commands.test.ts` — Public API commands (hackernews, v2ex) - `browser-public.test.ts` — Browser commands for public data across all 18 sites (21 tests) - `browser-auth.test.ts` — Graceful failure verification for login-required commands (14 tests) - `management.test.ts` — Full coverage of management commands (list/validate/verify/version/help) - `output-formats.test.ts` — Output format validation (json/yaml/csv/md) - `smoke/api-health.test.ts` — Scheduled API health checks ### Auto-detect Browser Mode - `buildMcpArgs` automatically selects mode based on `PLAYWRIGHT_MCP_EXTENSION_TOKEN`: - Token present → `--extension` (local user, connects to logged-in Chrome) - Token absent → standalone (CI launches its own browser) - No extra environment variables needed ### CI Pipeline - `e2e-headed.yml` — Real Chrome via `browser-actions/setup-chrome` + `xvfb-run` in headed mode - `ci.yml` — build + unit-test (2 shards) + smoke-test (scheduled/manual) - Browser commands that return empty data due to geo-blocking or bot detection gracefully warn + pass without blocking CI ### Documentation - New `TESTING.md` — Architecture, coverage, local setup, how to add tests, CI explanation - Updated `README.md` — Added Testing section with quick-start commands
57 lines
2.1 KiB
TypeScript
57 lines
2.1 KiB
TypeScript
/**
|
|
* E2E tests for public API commands (browser: false).
|
|
* These commands use Node.js fetch directly — no browser needed.
|
|
*/
|
|
|
|
import { describe, it, expect } from 'vitest';
|
|
import { runCli, parseJsonOutput } from './helpers.js';
|
|
|
|
describe('public commands E2E', () => {
|
|
// ── hackernews ──
|
|
it('hackernews top returns structured data', async () => {
|
|
const { stdout, code } = await runCli(['hackernews', 'top', '--limit', '3', '-f', 'json']);
|
|
expect(code).toBe(0);
|
|
const data = parseJsonOutput(stdout);
|
|
expect(Array.isArray(data)).toBe(true);
|
|
expect(data.length).toBe(3);
|
|
expect(data[0]).toHaveProperty('title');
|
|
expect(data[0]).toHaveProperty('score');
|
|
expect(data[0]).toHaveProperty('rank');
|
|
}, 30_000);
|
|
|
|
it('hackernews top respects --limit', async () => {
|
|
const { stdout, code } = await runCli(['hackernews', 'top', '--limit', '1', '-f', 'json']);
|
|
expect(code).toBe(0);
|
|
const data = parseJsonOutput(stdout);
|
|
expect(data.length).toBe(1);
|
|
}, 30_000);
|
|
|
|
// ── v2ex (public API, browser: false) ──
|
|
it('v2ex hot returns topics', async () => {
|
|
const { stdout, code } = await runCli(['v2ex', 'hot', '--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);
|
|
|
|
it('v2ex latest returns topics', async () => {
|
|
const { stdout, code } = await runCli(['v2ex', 'latest', '--limit', '3', '-f', 'json']);
|
|
expect(code).toBe(0);
|
|
const data = parseJsonOutput(stdout);
|
|
expect(Array.isArray(data)).toBe(true);
|
|
expect(data.length).toBeGreaterThanOrEqual(1);
|
|
}, 30_000);
|
|
|
|
it('v2ex topic returns topic detail', async () => {
|
|
// Topic 1000001 is a well-known V2EX topic
|
|
const { stdout, code } = await runCli(['v2ex', 'topic', '--id', '1000001', '-f', 'json']);
|
|
// May fail if V2EX rate-limits, but should return structured data
|
|
if (code === 0) {
|
|
const data = parseJsonOutput(stdout);
|
|
expect(data).toBeDefined();
|
|
}
|
|
}, 30_000);
|
|
});
|