test(eval): cover file and stdin eval inputs

This commit is contained in:
mcfn
2026-04-21 15:28:21 +08:00
parent 8cf1a3e0b8
commit 5f53650959
2 changed files with 195 additions and 2 deletions
+17
View File
@@ -1151,6 +1151,23 @@ mod tests {
assert!(cli.is_ok(), "browser eval command should parse");
}
#[test]
fn try_parse_from_accepts_browser_eval_file_flag() {
let cli = Cli::try_parse_from([
"actionbook",
"browser",
"eval",
"--file",
"/tmp/script.js",
"--session",
"session-1",
"--tab",
"tab-1",
]);
assert!(cli.is_ok(), "browser eval should accept --file");
}
#[test]
fn try_parse_from_accepts_browser_mouse_move_command() {
let cli = Cli::try_parse_from([
+178 -2
View File
@@ -5,9 +5,14 @@
//! `browser fill`, and `browser select`,
//! per api-reference.md §11.
use std::io::Write;
use std::process::Stdio;
use assert_cmd::cargo::cargo_bin;
use crate::harness::{
SessionGuard, assert_failure, assert_success, headless, headless_json, parse_json, skip,
stderr_str, stdout_str, unique_session, wait_page_ready,
SessionGuard, assert_failure, assert_success, headless, headless_json, parse_json,
shared_env, skip, stderr_str, stdout_str, unique_session, wait_page_ready,
};
const TEST_URL: &str = "https://example.com";
@@ -583,6 +588,37 @@ fn eval_value(session_id: &str, tab_id: &str, expression: &str) -> String {
v["data"]["value"].as_str().unwrap_or("").to_string()
}
fn eval_json_with_stdin(session_id: &str, tab_id: &str, stdin_expr: &str) -> serde_json::Value {
let env = shared_env();
let bin = cargo_bin("actionbook");
let mut cmd = std::process::Command::new(&bin);
cmd.env("ACTIONBOOK_HOME", &env.actionbook_home)
.arg("--json")
.arg("browser")
.arg("eval")
.arg("-")
.arg("--session")
.arg(session_id)
.arg("--tab")
.arg(tab_id)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped());
let mut child = cmd.spawn().expect("spawn browser eval stdin command");
{
let stdin = child.stdin.as_mut().expect("stdin pipe");
stdin
.write_all(stdin_expr.as_bytes())
.expect("write stdin expression");
}
let out = child
.wait_with_output()
.expect("wait for browser eval stdin command");
assert_success(&out, "browser eval stdin");
parse_json(&out)
}
fn install_click_fixture(session_id: &str, tab_id: &str) {
let expression = r#"
(() => {
@@ -5858,6 +5894,146 @@ fn eval_async_trailing_line_comment() {
close_session(&sid);
}
#[test]
fn browser_eval_file_basic_executes_expression() {
if skip() {
return;
}
let (sid, tid) = start_session("about:blank");
let _guard = SessionGuard::new(&sid);
let script = tempfile::NamedTempFile::new().expect("create eval temp file");
std::fs::write(script.path(), "(() => ({ a: 1, ok: true }))()").expect("write eval file");
let out = headless_json(
&[
"browser",
"eval",
"--file",
script.path().to_str().expect("utf8 path"),
"--session",
&sid,
"--tab",
&tid,
],
10,
);
assert_success(&out, "browser eval --file basic");
let v = parse_json(&out);
assert_eq!(v["data"]["value"]["a"], 1);
assert_eq!(v["data"]["value"]["ok"], true);
close_session(&sid);
}
#[test]
fn browser_eval_file_preserves_complex_javascript() {
if skip() {
return;
}
let (sid, tid) = start_session("about:blank");
let _guard = SessionGuard::new(&sid);
let script = tempfile::NamedTempFile::new().expect("create eval temp file");
std::fs::write(
script.path(),
r#"(() => ({ regex: /\w+/.toString(), text: "quote:\" slash:\\" }))()"#,
)
.expect("write complex eval file");
let out = headless_json(
&[
"browser",
"eval",
"--file",
script.path().to_str().expect("utf8 path"),
"--session",
&sid,
"--tab",
&tid,
],
10,
);
assert_success(&out, "browser eval --file complex");
let v = parse_json(&out);
assert_eq!(v["data"]["value"]["regex"], "/\\w+/");
assert_eq!(v["data"]["value"]["text"], r#"quote:" slash:\"#);
close_session(&sid);
}
#[test]
fn browser_eval_stdin_dash_reads_expression() {
if skip() {
return;
}
let (sid, tid) = start_session("about:blank");
let _guard = SessionGuard::new(&sid);
let v = eval_json_with_stdin(&sid, &tid, "(() => ({ via: 'stdin', value: 1 }))()");
assert_eq!(v["data"]["value"]["via"], "stdin");
assert_eq!(v["data"]["value"]["value"], 1);
close_session(&sid);
}
#[test]
fn browser_eval_file_missing_is_structured() {
if skip() {
return;
}
let (sid, tid) = start_session("about:blank");
let _guard = SessionGuard::new(&sid);
let out = headless_json(
&[
"browser",
"eval",
"--file",
"/tmp/actionbook-eval-file-missing.js",
"--session",
&sid,
"--tab",
&tid,
],
10,
);
assert_failure(&out, "browser eval --file missing");
let v = parse_json(&out);
assert_error_envelope(&v, "EVAL_FILE_NOT_FOUND");
close_session(&sid);
}
#[test]
fn browser_eval_file_and_expression_conflict_is_structured() {
if skip() {
return;
}
let (sid, tid) = start_session("about:blank");
let _guard = SessionGuard::new(&sid);
let script = tempfile::NamedTempFile::new().expect("create eval temp file");
std::fs::write(script.path(), "(() => 1)()").expect("write eval file");
let out = headless_json(
&[
"browser",
"eval",
"(() => 2)()",
"--file",
script.path().to_str().expect("utf8 path"),
"--session",
&sid,
"--tab",
&tid,
],
10,
);
assert_failure(&out, "browser eval positional + file conflict");
let v = parse_json(&out);
assert_error_envelope(&v, "EVAL_ARGS_CONFLICT");
close_session(&sid);
}
// ========================================================================
// Group 20: mouse-move — command wiring, success path, and error path
// ========================================================================