feat(click): report (and optionally --follow) a tab opened by a click (#24-A)
A click on a target=_blank link / window.open opened a new tab, but the active
tab stayed put, so the post-click snapshot showed the OLD page — looking exactly
like the click failed. On the relay the new tab is discovered only via getTargets
(the relay doesn't push target events to the daemon), so it went unsurfaced.
handle_click now snapshots tracked targets before the click and, after, runs a
lightweight BrowserManager::adopt_newly_opened (one getTargets, attaches only the
new target — far cheaper than a full resync) to detect a freshly-opened tab. It's
reported as openedTab {tabId,url,title} in the response (and a '→ opened new tab
[tN] <url>' hint in text mode). Default keeps focus on the current tab (so
multi-tab flows aren't hijacked, per #7/#8.1); 'click <sel> --follow' switches to
the new tab. Verified live: clicking a _blank link prints
'→ opened new tab [t13] https://example.org/'.
Completes the #24 friction items (B/C/D shipped in 770708b).
This commit is contained in:
+28
-6
@@ -424,6 +424,9 @@ fn parse_command_inner(args: &[String], flags: &Flags) -> Result<Value, ParseErr
|
||||
// === Core Actions ===
|
||||
"click" => {
|
||||
let new_tab = rest.contains(&"--new-tab");
|
||||
// `--follow`: if the click opens a new tab, switch the active tab to
|
||||
// it (default reports the opened tab but stays put) (issue #24-A).
|
||||
let follow = rest.contains(&"--follow");
|
||||
// Coordinate click as a first-class form (issue #8.4): when the only
|
||||
// handle is a pixel position, no element/selector is needed.
|
||||
// click <x> <y> e.g. click 449 320
|
||||
@@ -432,23 +435,27 @@ fn parse_command_inner(args: &[String], flags: &Flags) -> Result<Value, ParseErr
|
||||
let coord_args: Vec<&str> = rest
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|a| *a != "--new-tab" && *a != "--coords")
|
||||
.filter(|a| !a.starts_with("--"))
|
||||
.collect();
|
||||
if let Some((x, y)) = parse_coords(&coord_args) {
|
||||
return Ok(json!({ "id": id, "action": "click", "x": x, "y": y }));
|
||||
}
|
||||
let sel = rest
|
||||
.iter()
|
||||
.find(|arg| **arg != "--new-tab")
|
||||
.find(|arg| !arg.starts_with("--"))
|
||||
.ok_or_else(|| ParseError::MissingArguments {
|
||||
context: "click".to_string(),
|
||||
usage: "click <selector> | click <x> <y> | click --coords <x>,<y> [--new-tab]",
|
||||
usage:
|
||||
"click <selector> | click <x> <y> | click --coords <x>,<y> [--new-tab] [--follow]",
|
||||
})?;
|
||||
let mut cmd = json!({ "id": id, "action": "click", "selector": sel });
|
||||
if new_tab {
|
||||
Ok(json!({ "id": id, "action": "click", "selector": sel, "newTab": true }))
|
||||
} else {
|
||||
Ok(json!({ "id": id, "action": "click", "selector": sel }))
|
||||
cmd["newTab"] = json!(true);
|
||||
}
|
||||
if follow {
|
||||
cmd["follow"] = json!(true);
|
||||
}
|
||||
Ok(cmd)
|
||||
}
|
||||
"dblclick" => {
|
||||
let sel = rest.first().ok_or_else(|| ParseError::MissingArguments {
|
||||
@@ -3784,6 +3791,21 @@ mod tests {
|
||||
assert!(cmd.get("x").is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_click_follow_flag() {
|
||||
// `--follow` sets the flag; the selector is still found even with the flag
|
||||
// before it (issue #24-A).
|
||||
let cmd = parse_command(&args("click @e5 --follow"), &default_flags()).unwrap();
|
||||
assert_eq!(cmd["selector"], "@e5");
|
||||
assert_eq!(cmd["follow"], true);
|
||||
let cmd2 = parse_command(&args("click --follow @e5"), &default_flags()).unwrap();
|
||||
assert_eq!(cmd2["selector"], "@e5");
|
||||
assert_eq!(cmd2["follow"], true);
|
||||
// Absent by default.
|
||||
let plain = parse_command(&args("click @e5"), &default_flags()).unwrap();
|
||||
assert!(plain.get("follow").is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tabs_alias_lists() {
|
||||
assert_eq!(
|
||||
|
||||
Reference in New Issue
Block a user