refactor: make --full/-f a command-level flag instead of global (#877)
* refactor: make --full/-f a command-level flag instead of global Move --full/-f from global flags (parsed in flags.rs) to command-level parsing in commands.rs, scoped to the three commands that actually use it: `screenshot`, `diff screenshot`, and `diff url`. This frees up `-f` for other commands (e.g. `--follow` on `console`/`errors`, see #867) and better reflects that full-page capture is not a global concern. Changes: - Remove `full` from Flags struct, Config struct, and global flag parsing - Remove `--full`/`-f` from clean_args global boolean flags list - Parse `--full`/`-f` inline in `screenshot` command handler - Accept `-f` shorthand in `diff screenshot` and `diff url` (previously only `--full` was accepted at command level) - Remove fallback from global `flags.full` in diff subcommands - Update tests to pass --full as a command argument rather than a global flag Fixes #876 * fix: remove stale AGENT_BROWSER_FULL env var from help and add -f shorthand tests - Remove AGENT_BROWSER_FULL from help text in output.rs since the env var is no longer read after moving --full to command-level parsing - Add test_screenshot_full_page_shorthand to verify screenshot -f works - Add test_diff_screenshot_command_full_flag_shorthand to verify diff screenshot -f works --------- Co-authored-by: ctate <366502+ctate@users.noreply.github.com>
This commit is contained in:
+54
-29
@@ -449,10 +449,22 @@ pub fn parse_command(args: &[String], flags: &Flags) -> Result<Value, ParseError
|
|||||||
|
|
||||||
// === Screenshot/PDF ===
|
// === Screenshot/PDF ===
|
||||||
"screenshot" => {
|
"screenshot" => {
|
||||||
// screenshot [selector] [path]
|
// screenshot [selector] [path] [--full/-f]
|
||||||
// selector: @ref or CSS selector
|
// selector: @ref or CSS selector
|
||||||
// path: file path (contains / or . or ends with known extension)
|
// path: file path (contains / or . or ends with known extension)
|
||||||
let (selector, path) = match (rest.first(), rest.get(1)) {
|
let mut full_page = false;
|
||||||
|
let positional: Vec<&str> = rest
|
||||||
|
.iter()
|
||||||
|
.filter(|arg| match **arg {
|
||||||
|
"--full" | "-f" => {
|
||||||
|
full_page = true;
|
||||||
|
false
|
||||||
|
}
|
||||||
|
_ => true,
|
||||||
|
})
|
||||||
|
.copied()
|
||||||
|
.collect();
|
||||||
|
let (selector, path) = match (positional.first(), positional.get(1)) {
|
||||||
(Some(first), Some(second)) => {
|
(Some(first), Some(second)) => {
|
||||||
// Two args: first is selector, second is path
|
// Two args: first is selector, second is path
|
||||||
(Some(*first), Some(*second))
|
(Some(*first), Some(*second))
|
||||||
@@ -480,7 +492,7 @@ pub fn parse_command(args: &[String], flags: &Flags) -> Result<Value, ParseError
|
|||||||
let mut cmd = json!({
|
let mut cmd = json!({
|
||||||
"id": id, "action": "screenshot",
|
"id": id, "action": "screenshot",
|
||||||
"path": path, "selector": selector,
|
"path": path, "selector": selector,
|
||||||
"fullPage": flags.full, "annotate": flags.annotate
|
"fullPage": full_page, "annotate": flags.annotate
|
||||||
});
|
});
|
||||||
if let Some(ref fmt) = flags.screenshot_format {
|
if let Some(ref fmt) = flags.screenshot_format {
|
||||||
cmd["format"] = json!(fmt);
|
cmd["format"] = json!(fmt);
|
||||||
@@ -1315,7 +1327,7 @@ pub fn parse_command(args: &[String], flags: &Flags) -> Result<Value, ParseError
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"diff" => parse_diff(&rest, &id, flags),
|
"diff" => parse_diff(&rest, &id),
|
||||||
|
|
||||||
// === Batch ===
|
// === Batch ===
|
||||||
"batch" => {
|
"batch" => {
|
||||||
@@ -1329,7 +1341,7 @@ pub fn parse_command(args: &[String], flags: &Flags) -> Result<Value, ParseError
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_diff(rest: &[&str], id: &str, flags: &Flags) -> Result<Value, ParseError> {
|
fn parse_diff(rest: &[&str], id: &str) -> Result<Value, ParseError> {
|
||||||
const VALID: &[&str] = &["snapshot", "screenshot", "url"];
|
const VALID: &[&str] = &["snapshot", "screenshot", "url"];
|
||||||
|
|
||||||
match rest.first().copied() {
|
match rest.first().copied() {
|
||||||
@@ -1474,27 +1486,24 @@ fn parse_diff(rest: &[&str], id: &str, flags: &Flags) -> Result<Value, ParseErro
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"--full" => {
|
"--full" | "-f" => {
|
||||||
obj.insert("fullPage".to_string(), json!(true));
|
obj.insert("fullPage".to_string(), json!(true));
|
||||||
}
|
}
|
||||||
other if other.starts_with('-') => {
|
other if other.starts_with('-') => {
|
||||||
return Err(ParseError::InvalidValue {
|
return Err(ParseError::InvalidValue {
|
||||||
message: format!("Unknown flag: {}", other),
|
message: format!("Unknown flag: {}", other),
|
||||||
usage: "diff screenshot --baseline <file> [--output <file>] [--threshold <0-1>] [--selector <sel>] [--full]",
|
usage: "diff screenshot --baseline <file> [--output <file>] [--threshold <0-1>] [--selector <sel>] [--full/-f]",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
other => {
|
other => {
|
||||||
return Err(ParseError::InvalidValue {
|
return Err(ParseError::InvalidValue {
|
||||||
message: format!("Unexpected argument: {}", other),
|
message: format!("Unexpected argument: {}", other),
|
||||||
usage: "diff screenshot --baseline <file> [--output <file>] [--threshold <0-1>] [--selector <sel>] [--full]",
|
usage: "diff screenshot --baseline <file> [--output <file>] [--threshold <0-1>] [--selector <sel>] [--full/-f]",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
if flags.full {
|
|
||||||
obj.insert("fullPage".to_string(), json!(true));
|
|
||||||
}
|
|
||||||
if !obj.contains_key("baseline") {
|
if !obj.contains_key("baseline") {
|
||||||
return Err(ParseError::MissingArguments {
|
return Err(ParseError::MissingArguments {
|
||||||
context: "diff screenshot".to_string(),
|
context: "diff screenshot".to_string(),
|
||||||
@@ -1525,7 +1534,7 @@ fn parse_diff(rest: &[&str], id: &str, flags: &Flags) -> Result<Value, ParseErro
|
|||||||
"--screenshot" => {
|
"--screenshot" => {
|
||||||
obj.insert("screenshot".to_string(), json!(true));
|
obj.insert("screenshot".to_string(), json!(true));
|
||||||
}
|
}
|
||||||
"--full" => {
|
"--full" | "-f" => {
|
||||||
obj.insert("fullPage".to_string(), json!(true));
|
obj.insert("fullPage".to_string(), json!(true));
|
||||||
}
|
}
|
||||||
"--wait-until" => {
|
"--wait-until" => {
|
||||||
@@ -1580,21 +1589,18 @@ fn parse_diff(rest: &[&str], id: &str, flags: &Flags) -> Result<Value, ParseErro
|
|||||||
other if other.starts_with('-') => {
|
other if other.starts_with('-') => {
|
||||||
return Err(ParseError::InvalidValue {
|
return Err(ParseError::InvalidValue {
|
||||||
message: format!("Unknown flag: {}", other),
|
message: format!("Unknown flag: {}", other),
|
||||||
usage: "diff url <url1> <url2> [--screenshot] [--full] [--wait-until <strategy>] [--selector <sel>] [--compact] [--depth <n>]",
|
usage: "diff url <url1> <url2> [--screenshot] [--full/-f] [--wait-until <strategy>] [--selector <sel>] [--compact] [--depth <n>]",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
other => {
|
other => {
|
||||||
return Err(ParseError::InvalidValue {
|
return Err(ParseError::InvalidValue {
|
||||||
message: format!("Unexpected argument: {}", other),
|
message: format!("Unexpected argument: {}", other),
|
||||||
usage: "diff url <url1> <url2> [--screenshot] [--full] [--wait-until <strategy>] [--selector <sel>] [--compact] [--depth <n>]",
|
usage: "diff url <url1> <url2> [--screenshot] [--full/-f] [--wait-until <strategy>] [--selector <sel>] [--compact] [--depth <n>]",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
if flags.full {
|
|
||||||
obj.insert("fullPage".to_string(), json!(true));
|
|
||||||
}
|
|
||||||
Ok(cmd)
|
Ok(cmd)
|
||||||
}
|
}
|
||||||
Some(sub) => Err(ParseError::UnknownSubcommand {
|
Some(sub) => Err(ParseError::UnknownSubcommand {
|
||||||
@@ -2165,7 +2171,6 @@ mod tests {
|
|||||||
Flags {
|
Flags {
|
||||||
session: "test".to_string(),
|
session: "test".to_string(),
|
||||||
json: false,
|
json: false,
|
||||||
full: false,
|
|
||||||
headed: false,
|
headed: false,
|
||||||
debug: false,
|
debug: false,
|
||||||
headers: None,
|
headers: None,
|
||||||
@@ -2728,9 +2733,14 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_screenshot_full_page() {
|
fn test_screenshot_full_page() {
|
||||||
let mut flags = default_flags();
|
let cmd = parse_command(&args("screenshot --full"), &default_flags()).unwrap();
|
||||||
flags.full = true;
|
assert_eq!(cmd["action"], "screenshot");
|
||||||
let cmd = parse_command(&args("screenshot"), &flags).unwrap();
|
assert_eq!(cmd["fullPage"], true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_screenshot_full_page_shorthand() {
|
||||||
|
let cmd = parse_command(&args("screenshot -f"), &default_flags()).unwrap();
|
||||||
assert_eq!(cmd["action"], "screenshot");
|
assert_eq!(cmd["action"], "screenshot");
|
||||||
assert_eq!(cmd["fullPage"], true);
|
assert_eq!(cmd["fullPage"], true);
|
||||||
}
|
}
|
||||||
@@ -3598,10 +3608,23 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_diff_screenshot_global_full_flag() {
|
fn test_diff_screenshot_command_full_flag() {
|
||||||
let mut flags = default_flags();
|
let cmd = parse_command(
|
||||||
flags.full = true;
|
&args("diff screenshot --baseline b.png --full"),
|
||||||
let cmd = parse_command(&args("diff screenshot --baseline b.png"), &flags).unwrap();
|
&default_flags(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(cmd["action"], "diff_screenshot");
|
||||||
|
assert_eq!(cmd["fullPage"], true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_diff_screenshot_command_full_flag_shorthand() {
|
||||||
|
let cmd = parse_command(
|
||||||
|
&args("diff screenshot --baseline b.png -f"),
|
||||||
|
&default_flags(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
assert_eq!(cmd["action"], "diff_screenshot");
|
assert_eq!(cmd["action"], "diff_screenshot");
|
||||||
assert_eq!(cmd["fullPage"], true);
|
assert_eq!(cmd["fullPage"], true);
|
||||||
}
|
}
|
||||||
@@ -3642,10 +3665,12 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_diff_url_global_full_flag() {
|
fn test_diff_url_command_full_flag() {
|
||||||
let mut flags = default_flags();
|
let cmd = parse_command(
|
||||||
flags.full = true;
|
&args("diff url https://a.com https://b.com --full"),
|
||||||
let cmd = parse_command(&args("diff url https://a.com https://b.com"), &flags).unwrap();
|
&default_flags(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
assert_eq!(cmd["fullPage"], true);
|
assert_eq!(cmd["fullPage"], true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-45
@@ -55,7 +55,6 @@ fn parse_idle_timeout_value(value: Option<String>, source: &str) -> Option<Strin
|
|||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub headed: Option<bool>,
|
pub headed: Option<bool>,
|
||||||
pub json: Option<bool>,
|
pub json: Option<bool>,
|
||||||
pub full: Option<bool>,
|
|
||||||
pub debug: Option<bool>,
|
pub debug: Option<bool>,
|
||||||
pub session: Option<String>,
|
pub session: Option<String>,
|
||||||
pub session_name: Option<String>,
|
pub session_name: Option<String>,
|
||||||
@@ -95,7 +94,6 @@ impl Config {
|
|||||||
Config {
|
Config {
|
||||||
headed: other.headed.or(self.headed),
|
headed: other.headed.or(self.headed),
|
||||||
json: other.json.or(self.json),
|
json: other.json.or(self.json),
|
||||||
full: other.full.or(self.full),
|
|
||||||
debug: other.debug.or(self.debug),
|
debug: other.debug.or(self.debug),
|
||||||
session: other.session.or(self.session),
|
session: other.session.or(self.session),
|
||||||
session_name: other.session_name.or(self.session_name),
|
session_name: other.session_name.or(self.session_name),
|
||||||
@@ -267,7 +265,6 @@ pub fn load_config(args: &[String]) -> Result<Config, String> {
|
|||||||
|
|
||||||
pub struct Flags {
|
pub struct Flags {
|
||||||
pub json: bool,
|
pub json: bool,
|
||||||
pub full: bool,
|
|
||||||
pub headed: bool,
|
pub headed: bool,
|
||||||
pub debug: bool,
|
pub debug: bool,
|
||||||
pub session: String,
|
pub session: String,
|
||||||
@@ -342,7 +339,6 @@ pub fn parse_flags(args: &[String]) -> Flags {
|
|||||||
|
|
||||||
let mut flags = Flags {
|
let mut flags = Flags {
|
||||||
json: env_var_is_truthy("AGENT_BROWSER_JSON") || config.json.unwrap_or(false),
|
json: env_var_is_truthy("AGENT_BROWSER_JSON") || config.json.unwrap_or(false),
|
||||||
full: env_var_is_truthy("AGENT_BROWSER_FULL") || config.full.unwrap_or(false),
|
|
||||||
headed: env_var_is_truthy("AGENT_BROWSER_HEADED") || config.headed.unwrap_or(false),
|
headed: env_var_is_truthy("AGENT_BROWSER_HEADED") || config.headed.unwrap_or(false),
|
||||||
debug: env_var_is_truthy("AGENT_BROWSER_DEBUG") || config.debug.unwrap_or(false),
|
debug: env_var_is_truthy("AGENT_BROWSER_DEBUG") || config.debug.unwrap_or(false),
|
||||||
session: env::var("AGENT_BROWSER_SESSION")
|
session: env::var("AGENT_BROWSER_SESSION")
|
||||||
@@ -447,13 +443,6 @@ pub fn parse_flags(args: &[String]) -> Flags {
|
|||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"--full" | "-f" => {
|
|
||||||
let (val, consumed) = parse_bool_arg(args, i);
|
|
||||||
flags.full = val;
|
|
||||||
if consumed {
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"--headed" => {
|
"--headed" => {
|
||||||
let (val, consumed) = parse_bool_arg(args, i);
|
let (val, consumed) = parse_bool_arg(args, i);
|
||||||
flags.headed = val;
|
flags.headed = val;
|
||||||
@@ -722,7 +711,6 @@ pub fn clean_args(args: &[String]) -> Vec<String> {
|
|||||||
// Boolean flags that optionally take true/false
|
// Boolean flags that optionally take true/false
|
||||||
const GLOBAL_BOOL_FLAGS: &[&str] = &[
|
const GLOBAL_BOOL_FLAGS: &[&str] = &[
|
||||||
"--json",
|
"--json",
|
||||||
"--full",
|
|
||||||
"--headed",
|
"--headed",
|
||||||
"--debug",
|
"--debug",
|
||||||
"--ignore-https-errors",
|
"--ignore-https-errors",
|
||||||
@@ -776,7 +764,7 @@ pub fn clean_args(args: &[String]) -> Vec<String> {
|
|||||||
i += 1;
|
i += 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if GLOBAL_BOOL_FLAGS.contains(&arg.as_str()) || arg == "-f" {
|
if GLOBAL_BOOL_FLAGS.contains(&arg.as_str()) {
|
||||||
if let Some(v) = args.get(i + 1) {
|
if let Some(v) = args.get(i + 1) {
|
||||||
if matches!(v.as_str(), "true" | "false") {
|
if matches!(v.as_str(), "true" | "false") {
|
||||||
i += 1;
|
i += 1;
|
||||||
@@ -1024,7 +1012,6 @@ mod tests {
|
|||||||
let json = r#"{
|
let json = r#"{
|
||||||
"headed": true,
|
"headed": true,
|
||||||
"json": true,
|
"json": true,
|
||||||
"full": true,
|
|
||||||
"debug": true,
|
"debug": true,
|
||||||
"session": "test-session",
|
"session": "test-session",
|
||||||
"sessionName": "my-app",
|
"sessionName": "my-app",
|
||||||
@@ -1047,7 +1034,6 @@ mod tests {
|
|||||||
let config: Config = serde_json::from_str(json).unwrap();
|
let config: Config = serde_json::from_str(json).unwrap();
|
||||||
assert_eq!(config.headed, Some(true));
|
assert_eq!(config.headed, Some(true));
|
||||||
assert_eq!(config.json, Some(true));
|
assert_eq!(config.json, Some(true));
|
||||||
assert_eq!(config.full, Some(true));
|
|
||||||
assert_eq!(config.debug, Some(true));
|
assert_eq!(config.debug, Some(true));
|
||||||
assert_eq!(config.session.as_deref(), Some("test-session"));
|
assert_eq!(config.session.as_deref(), Some("test-session"));
|
||||||
assert_eq!(config.session_name.as_deref(), Some("my-app"));
|
assert_eq!(config.session_name.as_deref(), Some("my-app"));
|
||||||
@@ -1323,36 +1309,6 @@ mod tests {
|
|||||||
assert!(!flags.auto_connect);
|
assert!(!flags.auto_connect);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_full_bare_defaults_true() {
|
|
||||||
let flags = parse_flags(&args("--full open example.com"));
|
|
||||||
assert!(flags.full);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_full_false() {
|
|
||||||
let flags = parse_flags(&args("--full false open example.com"));
|
|
||||||
assert!(!flags.full);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_full_short_flag() {
|
|
||||||
let flags = parse_flags(&args("-f open example.com"));
|
|
||||||
assert!(flags.full);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_clean_args_removes_full_with_value() {
|
|
||||||
let cleaned = clean_args(&args("--full false open example.com"));
|
|
||||||
assert_eq!(cleaned, vec!["open", "example.com"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_clean_args_removes_short_full() {
|
|
||||||
let cleaned = clean_args(&args("-f open example.com"));
|
|
||||||
assert_eq!(cleaned, vec!["open", "example.com"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_clean_args_removes_bool_flag_with_value() {
|
fn test_clean_args_removes_bool_flag_with_value() {
|
||||||
let cleaned = clean_args(&args("--headed false --debug true open example.com"));
|
let cleaned = clean_args(&args("--headed false --debug true open example.com"));
|
||||||
|
|||||||
@@ -2655,7 +2655,6 @@ Environment:
|
|||||||
AGENT_BROWSER_EXTENSIONS Comma-separated browser extension paths
|
AGENT_BROWSER_EXTENSIONS Comma-separated browser extension paths
|
||||||
AGENT_BROWSER_HEADED Show browser window (not headless)
|
AGENT_BROWSER_HEADED Show browser window (not headless)
|
||||||
AGENT_BROWSER_JSON JSON output
|
AGENT_BROWSER_JSON JSON output
|
||||||
AGENT_BROWSER_FULL Full page screenshot
|
|
||||||
AGENT_BROWSER_ANNOTATE Annotated screenshot with numbered labels and legend
|
AGENT_BROWSER_ANNOTATE Annotated screenshot with numbered labels and legend
|
||||||
AGENT_BROWSER_DEBUG Debug output
|
AGENT_BROWSER_DEBUG Debug output
|
||||||
AGENT_BROWSER_IGNORE_HTTPS_ERRORS Ignore HTTPS certificate errors
|
AGENT_BROWSER_IGNORE_HTTPS_ERRORS Ignore HTTPS certificate errors
|
||||||
|
|||||||
Reference in New Issue
Block a user