add --selector flag to scroll command (#537)
* add --selector flag to scroll command
The `scroll` command uses `window.scrollBy()`, which has no effect on apps
that use custom scrollable containers (e.g. a nested div with overflow-y: auto).
The backend `handleScroll` already supports a `selector` parameter, but the CLI
never exposed it. This adds `-s` / `--selector` to the `scroll` command so users
can target a specific scrollable element:
agent-browser scroll down 500 --selector "div.scroll-container"
Also fixes the backend to apply `direction`/`amount` when a selector is present
(previously those fields were only used in the no-selector branch).
Closes #501
* fixes
This commit is contained in:
+123
-6
@@ -298,12 +298,48 @@ pub fn parse_command(args: &[String], flags: &Flags) -> Result<Value, ParseError
|
||||
|
||||
// === Scroll ===
|
||||
"scroll" => {
|
||||
let dir = rest.first().unwrap_or(&"down");
|
||||
let amount = rest
|
||||
.get(1)
|
||||
.and_then(|s| s.parse::<i32>().ok())
|
||||
.unwrap_or(300);
|
||||
Ok(json!({ "id": id, "action": "scroll", "direction": dir, "amount": amount }))
|
||||
let mut cmd = json!({ "id": id, "action": "scroll" });
|
||||
let obj = cmd.as_object_mut().unwrap();
|
||||
let mut positional_index = 0;
|
||||
let mut i = 0;
|
||||
while i < rest.len() {
|
||||
match rest[i] {
|
||||
"-s" | "--selector" => {
|
||||
if let Some(s) = rest.get(i + 1) {
|
||||
obj.insert("selector".to_string(), json!(s));
|
||||
i += 1;
|
||||
} else {
|
||||
return Err(ParseError::MissingArguments {
|
||||
context: "scroll --selector".to_string(),
|
||||
usage: "scroll [direction] [amount] [--selector <sel>]",
|
||||
});
|
||||
}
|
||||
}
|
||||
arg if arg.starts_with('-') => {}
|
||||
_ => {
|
||||
match positional_index {
|
||||
0 => {
|
||||
obj.insert("direction".to_string(), json!(rest[i]));
|
||||
}
|
||||
1 => {
|
||||
if let Ok(n) = rest[i].parse::<i32>() {
|
||||
obj.insert("amount".to_string(), json!(n));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
positional_index += 1;
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
if !obj.contains_key("direction") {
|
||||
obj.insert("direction".to_string(), json!("down"));
|
||||
}
|
||||
if !obj.contains_key("amount") {
|
||||
obj.insert("amount".to_string(), json!(300));
|
||||
}
|
||||
Ok(cmd)
|
||||
}
|
||||
"scrollintoview" | "scrollinto" => {
|
||||
let sel = rest.first().ok_or_else(|| ParseError::MissingArguments {
|
||||
@@ -3436,4 +3472,85 @@ mod tests {
|
||||
ParseError::MissingArguments { .. }
|
||||
));
|
||||
}
|
||||
|
||||
// === Scroll Tests ===
|
||||
|
||||
#[test]
|
||||
fn test_scroll_defaults() {
|
||||
let cmd = parse_command(&args("scroll"), &default_flags()).unwrap();
|
||||
assert_eq!(cmd["action"], "scroll");
|
||||
assert_eq!(cmd["direction"], "down");
|
||||
assert_eq!(cmd["amount"], 300);
|
||||
assert!(cmd.get("selector").is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scroll_direction_and_amount() {
|
||||
let cmd = parse_command(&args("scroll up 200"), &default_flags()).unwrap();
|
||||
assert_eq!(cmd["action"], "scroll");
|
||||
assert_eq!(cmd["direction"], "up");
|
||||
assert_eq!(cmd["amount"], 200);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scroll_with_selector() {
|
||||
let cmd = parse_command(
|
||||
&args("scroll down 500 --selector div.scroll-container"),
|
||||
&default_flags(),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(cmd["action"], "scroll");
|
||||
assert_eq!(cmd["direction"], "down");
|
||||
assert_eq!(cmd["amount"], 500);
|
||||
assert_eq!(cmd["selector"], "div.scroll-container");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scroll_with_selector_short_flag() {
|
||||
let cmd = parse_command(
|
||||
&args("scroll left 100 -s .sidebar"),
|
||||
&default_flags(),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(cmd["action"], "scroll");
|
||||
assert_eq!(cmd["direction"], "left");
|
||||
assert_eq!(cmd["amount"], 100);
|
||||
assert_eq!(cmd["selector"], ".sidebar");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scroll_selector_before_positional() {
|
||||
let cmd = parse_command(
|
||||
&args("scroll --selector .panel down 400"),
|
||||
&default_flags(),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(cmd["action"], "scroll");
|
||||
assert_eq!(cmd["direction"], "down");
|
||||
assert_eq!(cmd["amount"], 400);
|
||||
assert_eq!(cmd["selector"], ".panel");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scroll_selector_only() {
|
||||
let cmd = parse_command(
|
||||
&args("scroll --selector .content"),
|
||||
&default_flags(),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(cmd["action"], "scroll");
|
||||
assert_eq!(cmd["direction"], "down");
|
||||
assert_eq!(cmd["amount"], 300);
|
||||
assert_eq!(cmd["selector"], ".content");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scroll_selector_missing_value() {
|
||||
let result = parse_command(&args("scroll down 500 --selector"), &default_flags());
|
||||
assert!(result.is_err());
|
||||
assert!(matches!(
|
||||
result.unwrap_err(),
|
||||
ParseError::MissingArguments { .. }
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
+6
-2
@@ -973,14 +973,17 @@ Use Cases:
|
||||
r##"
|
||||
agent-browser scroll - Scroll the page
|
||||
|
||||
Usage: agent-browser scroll [direction] [amount]
|
||||
Usage: agent-browser scroll [direction] [amount] [options]
|
||||
|
||||
Scrolls the page in the specified direction.
|
||||
Scrolls the page or a specific element in the specified direction.
|
||||
|
||||
Arguments:
|
||||
direction up, down, left, right (default: down)
|
||||
amount Pixels to scroll (default: 300)
|
||||
|
||||
Options:
|
||||
-s, --selector <sel> CSS selector for a scrollable container
|
||||
|
||||
Global Options:
|
||||
--json Output as JSON
|
||||
--session <name> Use specific session
|
||||
@@ -990,6 +993,7 @@ Examples:
|
||||
agent-browser scroll down 500
|
||||
agent-browser scroll up 200
|
||||
agent-browser scroll left 100
|
||||
agent-browser scroll down 500 --selector "div.scroll-container"
|
||||
"##
|
||||
}
|
||||
"scrollintoview" | "scrollinto" => {
|
||||
|
||||
Reference in New Issue
Block a user