mirror of
https://github.com/actionbook/actionbook.git
synced 2026-09-19 03:28:06 +08:00
731cc340d8
* [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>
142 lines
3.2 KiB
TypeScript
142 lines
3.2 KiB
TypeScript
import { execFile } from "child_process";
|
|
import { existsSync } from "fs";
|
|
import path from "path";
|
|
|
|
const BINARY_NAME =
|
|
process.platform === "win32" ? "actionbook.exe" : "actionbook";
|
|
|
|
/**
|
|
* Resolve the actionbook binary path.
|
|
* Priority: ACTIONBOOK_BINARY_PATH env > cargo debug build > empty string (skip).
|
|
*/
|
|
export function getActionbookBinary(): string {
|
|
if (process.env.ACTIONBOOK_BINARY_PATH) {
|
|
return process.env.ACTIONBOOK_BINARY_PATH;
|
|
}
|
|
|
|
const cargoBinary = path.resolve(
|
|
__dirname,
|
|
"..",
|
|
"..",
|
|
"..",
|
|
"actionbook-rs",
|
|
"target",
|
|
"debug",
|
|
BINARY_NAME
|
|
);
|
|
if (existsSync(cargoBinary)) {
|
|
return cargoBinary;
|
|
}
|
|
|
|
// Also check release build
|
|
const releaseBinary = path.resolve(
|
|
__dirname,
|
|
"..",
|
|
"..",
|
|
"..",
|
|
"actionbook-rs",
|
|
"target",
|
|
"release",
|
|
BINARY_NAME
|
|
);
|
|
if (existsSync(releaseBinary)) {
|
|
return releaseBinary;
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
export interface RunCliResult {
|
|
stdout: string;
|
|
stderr: string;
|
|
exitCode: number | null;
|
|
}
|
|
|
|
/**
|
|
* Run `node bin/actionbook.js` (the Node.js wrapper) with given args and env.
|
|
* Used for testing the wrapper itself.
|
|
*/
|
|
export function runWrapper(
|
|
args: string[] = [],
|
|
options: {
|
|
env?: Record<string, string>;
|
|
timeout?: number;
|
|
} = {}
|
|
): Promise<RunCliResult> {
|
|
const wrapperPath = path.resolve(__dirname, "..", "..", "bin", "actionbook.js");
|
|
const timeoutMs = options.timeout ?? 15000;
|
|
|
|
return new Promise((resolve) => {
|
|
const child = execFile(
|
|
process.execPath,
|
|
[wrapperPath, ...args],
|
|
{
|
|
env: { ...process.env, ...options.env },
|
|
timeout: timeoutMs,
|
|
maxBuffer: 10 * 1024 * 1024,
|
|
},
|
|
(error, stdout, stderr) => {
|
|
const exitCode =
|
|
error && "code" in error
|
|
? (error as NodeJS.ErrnoException & { code?: number }).code ??
|
|
(error as any).status ??
|
|
null
|
|
: 0;
|
|
|
|
resolve({
|
|
stdout: stdout.toString(),
|
|
stderr: stderr.toString(),
|
|
exitCode:
|
|
child.exitCode !== null ? child.exitCode : exitCode,
|
|
});
|
|
}
|
|
);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Run the actionbook CLI binary with the given arguments.
|
|
* Returns stdout, stderr, and exit code.
|
|
*/
|
|
export function runCli(
|
|
args: string[],
|
|
options: {
|
|
env?: Record<string, string>;
|
|
timeout?: number;
|
|
} = {}
|
|
): Promise<RunCliResult> {
|
|
const binary = getActionbookBinary();
|
|
if (!binary) {
|
|
return Promise.reject(new Error("Actionbook binary not found"));
|
|
}
|
|
|
|
const timeoutMs = options.timeout ?? 15000;
|
|
|
|
return new Promise((resolve) => {
|
|
const child = execFile(
|
|
binary,
|
|
args,
|
|
{
|
|
env: { ...process.env, ...options.env },
|
|
timeout: timeoutMs,
|
|
maxBuffer: 10 * 1024 * 1024,
|
|
},
|
|
(error, stdout, stderr) => {
|
|
const exitCode =
|
|
error && "code" in error
|
|
? (error as NodeJS.ErrnoException & { code?: number }).code ??
|
|
(error as any).status ??
|
|
null
|
|
: 0;
|
|
|
|
resolve({
|
|
stdout: stdout.toString(),
|
|
stderr: stderr.toString(),
|
|
exitCode:
|
|
child.exitCode !== null ? child.exitCode : exitCode,
|
|
});
|
|
}
|
|
);
|
|
});
|
|
}
|