Files
s1mba 731cc340d8 [packages/cli] Add comprehensive CLI test suite (#128)
* [packages/cli]test: add comprehensive CLI test suite with E2E browser tests

- Add unit tests for wrapper (actionbook.js), postinstall, config, profile,
  search, get, and sources commands
- Add Tier 1 browser tests (arg validation, help output, defaults)
- Add Tier 2 browser E2E tests (navigation, interaction, screenshots,
  cookies, tabs, error handling, JSON output)
- Fix E2E test reliability: add --wait for element readiness after navigation,
  use eval-based form submit for login flow, match actual CLI exit codes
- Suppress macOS Keychain dialogs in tests via --use-mock-keychain config
- Set testTimeout to 60s for browser E2E operations
- Export modules from actionbook.js and postinstall.js for testability

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* [packages/cli]fix: resolve Windows .exe suffix when locating test CLI binary

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* [packages/cli]fix: skip shell-script mock tests on Windows

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 14:36:15 +08:00

63 lines
1.9 KiB
TypeScript

import { describe, it, expect, beforeAll } from "vitest";
import { getActionbookBinary, runCli } from "./helpers/binary";
import { isApiAvailable } from "./helpers/api";
import { createIsolatedEnv } from "./helpers/config";
const binary = getActionbookBinary();
const hasBinary = !!binary;
let apiAvailable = false;
describe.skipIf(!hasBinary)("sources command", () => {
let isolatedEnv: ReturnType<typeof createIsolatedEnv>;
beforeAll(async () => {
isolatedEnv = createIsolatedEnv();
apiAvailable = await isApiAvailable();
});
describe("argument validation", () => {
it("requires a subcommand", async () => {
const result = await runCli(["sources"], { env: isolatedEnv.env });
expect(result.exitCode).toBe(2);
expect(result.stderr).toContain("subcommand");
});
it("sources search requires a query", async () => {
const result = await runCli(["sources", "search"], {
env: isolatedEnv.env,
});
expect(result.exitCode).toBe(2);
expect(result.stderr).toContain("QUERY");
});
});
describe.skipIf(!apiAvailable)("with API", () => {
it("lists sources", async () => {
const result = await runCli(["sources", "list"], {
env: isolatedEnv.env,
timeout: 30000,
});
expect(result.exitCode).toBe(0);
});
it("lists sources in JSON format", async () => {
const result = await runCli(["--json", "sources", "list"], {
env: isolatedEnv.env,
timeout: 30000,
});
expect(result.exitCode).toBe(0);
// Verify output is valid JSON
expect(() => JSON.parse(result.stdout)).not.toThrow();
});
it("searches sources by keyword", async () => {
const result = await runCli(["sources", "search", "airbnb"], {
env: isolatedEnv.env,
timeout: 30000,
});
expect(result.exitCode).toBe(0);
});
});
});