mirror of
https://github.com/actionbook/actionbook.git
synced 2026-09-19 03:28:06 +08:00
fac7647c6c
- 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>
366 lines
8.7 KiB
Rust
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");
|
|
}
|