Files
mcfn.feng fac7647c6c [packages/actionbook-rs]fix: align tab ID to 1-based (t1, t2, ...) per spec and add tab management E2E assertions
- Fix next_tab_id counter to start from 1 instead of 0
- Fix hardcoded "t0" in browser start response (router.rs)
- Update TabId doc comment to reflect t1, t2, ... convention
- Rewrite browser_tab.rs with full JSON envelope assertions for
  list-tabs, new-tab (browser open), and close-tab commands
- Update all e2e test files to use 1-based tab IDs (t1, t2, t3)
- Fix harness ensure_no_sessions() field name: "id" -> "session_id"

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 17:28:14 +08:00

366 lines
8.7 KiB
Rust

//! Error-path E2E tests: verify that invalid inputs produce non-zero exits.
//!
//! Each test starts a headless session, issues a command expected to fail,
//! asserts non-zero exit, then closes the session.
use crate::harness::{
assert_failure, assert_success, headless, skip, stderr_str, stdout_str, SessionGuard,
};
// ── 1. ELEMENT_NOT_FOUND: click nonexistent selector ───────────────
#[test]
fn err_click_nonexistent() {
if skip() {
return;
}
let _guard = SessionGuard::new();
let out = headless(
&[
"browser",
"start",
"--mode",
"local",
"--headless",
"--open-url",
"https://example.com",
],
30,
);
assert_success(&out, "start session");
let out = headless(
&[
"browser",
"goto",
"https://example.com",
"-s",
"local-1",
"-t",
"t1",
],
30,
);
assert_success(&out, "goto example.com");
let out = headless(
&[
"browser",
"click",
"#does_not_exist_at_all",
"-s",
"local-1",
"-t",
"t1",
],
30,
);
assert_failure(&out, "click nonexistent element");
let output = stdout_str(&out) + &stderr_str(&out);
assert!(
output.to_lowercase().contains("not found") || output.to_lowercase().contains("error"),
"click nonexistent should mention error in output, got: {}",
output
);
let out = headless(&["browser", "close", "-s", "local-1"], 30);
assert_success(&out, "close session");
}
// ── 2. NAVIGATION_FAILED: goto invalid URL ─────────────────────────
#[test]
fn err_goto_invalid_url() {
if skip() {
return;
}
let _guard = SessionGuard::new();
let out = headless(
&[
"browser",
"start",
"--mode",
"local",
"--headless",
"--open-url",
"https://example.com",
],
30,
);
assert_success(&out, "start session");
let out = headless(
&["browser", "goto", "://invalid", "-s", "local-1", "-t", "t1"],
30,
);
assert_failure(&out, "goto invalid URL");
let output = stdout_str(&out) + &stderr_str(&out);
assert!(
output.to_lowercase().contains("error")
|| output.to_lowercase().contains("invalid")
|| output.to_lowercase().contains("fail"),
"goto invalid URL should mention error in output, got: {}",
output
);
let out = headless(&["browser", "close", "-s", "local-1"], 30);
assert_success(&out, "close session");
}
// ── 3. EVAL_FAILED: eval with syntax error ─────────────────────────
#[test]
fn err_eval_syntax_error() {
if skip() {
return;
}
let _guard = SessionGuard::new();
let out = headless(
&[
"browser",
"start",
"--mode",
"local",
"--headless",
"--open-url",
"https://example.com",
],
30,
);
assert_success(&out, "start session");
let out = headless(
&[
"browser",
"goto",
"https://example.com",
"-s",
"local-1",
"-t",
"t1",
],
30,
);
assert_success(&out, "goto example.com");
let out = headless(
&[
"browser",
"eval",
"{{{{syntax_error",
"-s",
"local-1",
"-t",
"t1",
],
30,
);
assert_failure(&out, "eval syntax error");
let output = stdout_str(&out) + &stderr_str(&out);
assert!(
output.to_lowercase().contains("error")
|| output.to_lowercase().contains("syntax")
|| output.to_lowercase().contains("fail"),
"eval syntax error should mention error in output, got: {}",
output
);
let out = headless(&["browser", "close", "-s", "local-1"], 30);
assert_success(&out, "close session");
}
// ── 4. ARTIFACT_WRITE_FAILED: screenshot to nonexistent directory ──
#[test]
fn err_screenshot_bad_path() {
if skip() {
return;
}
let _guard = SessionGuard::new();
let out = headless(
&[
"browser",
"start",
"--mode",
"local",
"--headless",
"--open-url",
"https://example.com",
],
30,
);
assert_success(&out, "start session");
let out = headless(
&[
"browser",
"goto",
"https://example.com",
"-s",
"local-1",
"-t",
"t1",
],
30,
);
assert_success(&out, "goto example.com");
let out = headless(
&[
"browser",
"screenshot",
"/nonexistent_dir_12345/x.png",
"-s",
"local-1",
"-t",
"t1",
],
30,
);
assert_failure(&out, "screenshot to bad path");
let output = stdout_str(&out) + &stderr_str(&out);
assert!(
output.to_lowercase().contains("error")
|| output.to_lowercase().contains("not found")
|| output.to_lowercase().contains("fail")
|| output.to_lowercase().contains("no such"),
"screenshot bad path should mention error in output, got: {}",
output
);
let out = headless(&["browser", "close", "-s", "local-1"], 30);
assert_success(&out, "close session");
}
// ── 5. TIMEOUT: wait for nonexistent element ───────────────────────
#[test]
fn err_wait_timeout() {
if skip() {
return;
}
let _guard = SessionGuard::new();
let out = headless(
&[
"browser",
"start",
"--mode",
"local",
"--headless",
"--open-url",
"https://example.com",
],
30,
);
assert_success(&out, "start session");
let out = headless(
&[
"browser",
"goto",
"https://example.com",
"-s",
"local-1",
"-t",
"t1",
],
30,
);
assert_success(&out, "goto example.com");
let out = headless(
&[
"browser",
"wait",
"element",
"#absolutely_not_here",
"-s",
"local-1",
"-t",
"t1",
"--timeout",
"1000",
],
30,
);
assert_failure(&out, "wait for nonexistent element");
let output = stdout_str(&out) + &stderr_str(&out);
assert!(
output.to_lowercase().contains("timeout")
|| output.to_lowercase().contains("error")
|| output.to_lowercase().contains("not found"),
"wait timeout should mention error in output, got: {}",
output
);
let out = headless(&["browser", "close", "-s", "local-1"], 30);
assert_success(&out, "close session");
}
// ── 6. ELEMENT_NOT_FOUND: fill nonexistent element ─────────────────
#[test]
fn err_fill_nonexistent() {
if skip() {
return;
}
let _guard = SessionGuard::new();
let out = headless(
&[
"browser",
"start",
"--mode",
"local",
"--headless",
"--open-url",
"https://example.com",
],
30,
);
assert_success(&out, "start session");
let out = headless(
&[
"browser",
"goto",
"https://example.com",
"-s",
"local-1",
"-t",
"t1",
],
30,
);
assert_success(&out, "goto example.com");
let out = headless(
&[
"browser",
"fill",
"#does_not_exist_xyz",
"text",
"-s",
"local-1",
"-t",
"t1",
],
30,
);
assert_failure(&out, "fill nonexistent element");
let output = stdout_str(&out) + &stderr_str(&out);
assert!(
output.to_lowercase().contains("not found") || output.to_lowercase().contains("error"),
"fill nonexistent should mention error in output, got: {}",
output
);
let out = headless(&["browser", "close", "-s", "local-1"], 30);
assert_success(&out, "close session");
}