diff --git a/cli/src/commands.rs b/cli/src/commands.rs index d113616b..aa4119a8 100644 --- a/cli/src/commands.rs +++ b/cli/src/commands.rs @@ -101,6 +101,10 @@ pub fn parse_command(args: &[String], flags: &Flags) -> Result(headers_json) { @@ -1535,6 +1539,7 @@ mod tests { device: None, auto_connect: false, session_name: None, + wait_until: None, cli_executable_path: false, cli_extensions: false, cli_profile: false, @@ -1836,6 +1841,38 @@ mod tests { assert!(cmd.get("headers").is_none()); } + #[test] + fn test_navigate_with_wait_until() { + let mut flags = default_flags(); + flags.wait_until = Some("domcontentloaded".to_string()); + let cmd = parse_command(&args("open example.com"), &flags).unwrap(); + assert_eq!(cmd["action"], "navigate"); + assert_eq!(cmd["waitUntil"], "domcontentloaded"); + } + + #[test] + fn test_navigate_with_wait_until_load() { + let mut flags = default_flags(); + flags.wait_until = Some("load".to_string()); + let cmd = parse_command(&args("open example.com"), &flags).unwrap(); + assert_eq!(cmd["waitUntil"], "load"); + } + + #[test] + fn test_navigate_with_wait_until_networkidle() { + let mut flags = default_flags(); + flags.wait_until = Some("networkidle".to_string()); + let cmd = parse_command(&args("open example.com"), &flags).unwrap(); + assert_eq!(cmd["waitUntil"], "networkidle"); + } + + #[test] + fn test_navigate_without_wait_until() { + let cmd = parse_command(&args("open example.com"), &default_flags()).unwrap(); + // waitUntil should not be present when flag is not set (daemon uses its own default) + assert!(cmd.get("waitUntil").is_none()); + } + // === Set Headers Tests === #[test] diff --git a/cli/src/flags.rs b/cli/src/flags.rs index 84d41ebf..a9ad9a32 100644 --- a/cli/src/flags.rs +++ b/cli/src/flags.rs @@ -22,6 +22,7 @@ pub struct Flags { pub device: Option, pub auto_connect: bool, pub session_name: Option, + pub wait_until: Option, // Track which launch-time options were explicitly passed via CLI // (as opposed to being set only via environment variables) @@ -69,6 +70,7 @@ pub fn parse_flags(args: &[String]) -> Flags { device: env::var("AGENT_BROWSER_IOS_DEVICE").ok(), auto_connect: env::var("AGENT_BROWSER_AUTO_CONNECT").is_ok(), session_name: env::var("AGENT_BROWSER_SESSION_NAME").ok(), + wait_until: env::var("AGENT_BROWSER_WAIT_UNTIL").ok(), // Track CLI-passed flags (default false, set to true when flag is passed) cli_executable_path: false, cli_extensions: false, @@ -186,6 +188,12 @@ pub fn parse_flags(args: &[String]) -> Flags { i += 1; } } + "--wait-until" => { + if let Some(s) = args.get(i + 1) { + flags.wait_until = Some(s.clone()); + i += 1; + } + } _ => {} } i += 1; @@ -224,6 +232,7 @@ pub fn clean_args(args: &[String]) -> Vec { "--provider", "--device", "--session-name", + "--wait-until", ]; for arg in args.iter() { @@ -401,4 +410,22 @@ mod tests { assert!(!flags.cli_extensions); assert!(!flags.cli_state); } + + #[test] + fn test_parse_wait_until_flag() { + let flags = parse_flags(&args("open example.com --wait-until domcontentloaded")); + assert_eq!(flags.wait_until, Some("domcontentloaded".to_string())); + } + + #[test] + fn test_parse_no_wait_until_flag() { + let flags = parse_flags(&args("open example.com")); + assert!(flags.wait_until.is_none()); + } + + #[test] + fn test_clean_args_removes_wait_until() { + let cleaned = clean_args(&args("open example.com --wait-until domcontentloaded")); + assert_eq!(cleaned, vec!["open", "example.com"]); + } } diff --git a/cli/src/output.rs b/cli/src/output.rs index 7f083a51..dd8e747a 100644 --- a/cli/src/output.rs +++ b/cli/src/output.rs @@ -491,6 +491,10 @@ https:// is automatically prepended. Aliases: goto, navigate +Options: + --wait-until When to consider navigation done (default: domcontentloaded) + Values: load, domcontentloaded, networkidle + Global Options: --json Output as JSON --session Use specific session @@ -501,6 +505,7 @@ Examples: agent-browser open example.com agent-browser open https://github.com agent-browser open localhost:3000 + agent-browser open example.com --wait-until networkidle agent-browser open api.example.com --headers '{"Authorization": "Bearer token"}' # ^ Headers only sent to api.example.com, not other domains "## @@ -1872,6 +1877,8 @@ Options: --headed Show browser window (not headless) --cdp Connect via CDP (Chrome DevTools Protocol) --auto-connect Auto-discover and connect to running Chrome + --wait-until Navigation wait strategy: load, domcontentloaded, networkidle + (or AGENT_BROWSER_WAIT_UNTIL, default: domcontentloaded) --session-name Auto-save/restore session state (cookies, localStorage) --debug Debug output --version, -V Show version @@ -1885,6 +1892,7 @@ Environment: AGENT_BROWSER_PROVIDER Browser provider (ios, browserbase, kernel, browseruse) AGENT_BROWSER_AUTO_CONNECT Auto-discover and connect to running Chrome AGENT_BROWSER_STREAM_PORT Enable WebSocket streaming on port (e.g., 9223) + AGENT_BROWSER_WAIT_UNTIL Navigation wait strategy (default: domcontentloaded) AGENT_BROWSER_IOS_DEVICE Default iOS device name AGENT_BROWSER_IOS_UDID Default iOS device UDID diff --git a/src/actions.ts b/src/actions.ts index 22314e06..4ebccfa9 100644 --- a/src/actions.ts +++ b/src/actions.ts @@ -511,7 +511,7 @@ async function handleNavigate( } await page.goto(command.url, { - waitUntil: command.waitUntil ?? 'load', + waitUntil: command.waitUntil ?? 'domcontentloaded', }); return successResponse(command.id, {