mirror of
https://github.com/Tencent/BrowserSkill.git
synced 2026-09-10 13:01:23 +08:00
feat(emulate): add mobile device emulation via CDP Emulation domain (#60)
* feat(emulate): add mobile device emulation via CDP Emulation domain Add `bsk emulate` so agents can debug mobile page behaviour by emulating a device environment on a session tab: - Viewport metrics (Emulation.setDeviceMetricsOverride), User-Agent (Emulation.setUserAgentOverride, optional accept-language and UA client-hints metadata), and touch (Emulation.setTouchEmulationEnabled with maxTouchPoints). - Seven CLI-side device presets (iphone-14, iphone-14-pro-max, iphone-se, pixel-7, galaxy-s23, ipad-mini, galaxy-tab-s8) via --device; manual flags (--width/--height/--dpr/--mobile/--ua/ --accept-language/--touch/--max-touch-points) work standalone or override individual preset fields. - `bsk emulate --off` clears every override (clearDeviceMetricsOverride + touch disabled + empty UA override) and restores the real environment. - Overrides are per-tab (CDP per-target) and not inherited by new tabs; the result JSON carries a note reminding agents to re-apply. Wire: new tool.emulate method end-to-end (bsk-protocol types + JSON schemas + Method::ToolEmulate classified BrowserMutation, daemon forwarding, CLI subcommand, extension tool handler + ChromiumCdp Emulation wrappers, dispatcher registration), docs in both SKILL.md copies, and unit tests on every layer. * refine(emulate): merge per-tab state and align preset override semantics - extension: remember the applied emulation state per tab and merge each new request onto it field by field before applying, so a later --width/--height no longer resets an earlier preset's dpr/mobile; --off clears the stored state along with the CDP overrides - extension: cdp_failed errors now note that already-applied overrides were not rolled back and can be reset with --off - cli: validate width/height pairing and metric requirements on the merged preset+flags result, so --device iphone-14 --width 500 keeps the preset height - cli: add --no-mobile/--no-touch (conflicting with --mobile/--touch) to turn preset mobile/touch fields back off - docs: describe the merge behaviour, --no-mobile/--no-touch and --off state clearing in both SKILL.md copies
This commit is contained in:
@@ -323,3 +323,162 @@ fn rejects_invalid_window_resize_dimensions() {
|
||||
// width/height are required for resize.
|
||||
assert!(Cli::try_parse_from(["bsk", "window", "resize", "--session", "s1"]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_emulate_with_device_preset() {
|
||||
use bsk::cli::emulate::EmulateArgs;
|
||||
let cli = parse(&["bsk", "emulate", "--session", "s1", "--device", "iphone-14"]);
|
||||
let Command::Emulate(EmulateArgs {
|
||||
session,
|
||||
device,
|
||||
off,
|
||||
..
|
||||
}) = cli.command
|
||||
else {
|
||||
panic!("expected emulate subcommand");
|
||||
};
|
||||
assert_eq!(session, "s1");
|
||||
assert_eq!(device.as_deref(), Some("iphone-14"));
|
||||
assert!(!off);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_emulate_manual_overrides() {
|
||||
let cli = parse(&[
|
||||
"bsk",
|
||||
"emulate",
|
||||
"--session",
|
||||
"s1",
|
||||
"--width",
|
||||
"390",
|
||||
"--height",
|
||||
"844",
|
||||
"--dpr",
|
||||
"3",
|
||||
"--mobile",
|
||||
"--ua",
|
||||
"Mozilla/5.0 (iPhone)",
|
||||
"--accept-language",
|
||||
"zh-CN",
|
||||
"--touch",
|
||||
"--max-touch-points",
|
||||
"5",
|
||||
"--tab-id",
|
||||
"7",
|
||||
]);
|
||||
let Command::Emulate(args) = cli.command else {
|
||||
panic!("expected emulate subcommand");
|
||||
};
|
||||
assert_eq!(args.width, Some(390));
|
||||
assert_eq!(args.height, Some(844));
|
||||
assert_eq!(args.dpr, Some(3.0));
|
||||
assert!(args.mobile);
|
||||
assert_eq!(args.ua.as_deref(), Some("Mozilla/5.0 (iPhone)"));
|
||||
assert_eq!(args.accept_language.as_deref(), Some("zh-CN"));
|
||||
assert!(args.touch);
|
||||
assert_eq!(args.max_touch_points, Some(5));
|
||||
assert_eq!(args.tab_id, Some(7));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_emulate_off() {
|
||||
let cli = parse(&["bsk", "emulate", "--session", "s1", "--off"]);
|
||||
let Command::Emulate(args) = cli.command else {
|
||||
panic!("expected emulate subcommand");
|
||||
};
|
||||
assert!(args.off);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_emulate_no_mobile_no_touch() {
|
||||
let cli = parse(&[
|
||||
"bsk",
|
||||
"emulate",
|
||||
"--session",
|
||||
"s1",
|
||||
"--device",
|
||||
"iphone-14",
|
||||
"--no-mobile",
|
||||
"--no-touch",
|
||||
]);
|
||||
let Command::Emulate(args) = cli.command else {
|
||||
panic!("expected emulate subcommand");
|
||||
};
|
||||
assert!(args.no_mobile);
|
||||
assert!(args.no_touch);
|
||||
assert!(!args.mobile);
|
||||
assert!(!args.touch);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_conflicting_emulate_flags() {
|
||||
for extra in [
|
||||
&["--mobile", "--no-mobile"][..],
|
||||
&["--touch", "--no-touch"][..],
|
||||
] {
|
||||
let mut argv = vec!["bsk", "emulate", "--session", "s1", "--device", "iphone-14"];
|
||||
argv.extend_from_slice(extra);
|
||||
assert!(Cli::try_parse_from(argv).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_invalid_emulate_values() {
|
||||
// Zero / out-of-range dimensions.
|
||||
assert!(
|
||||
Cli::try_parse_from([
|
||||
"bsk",
|
||||
"emulate",
|
||||
"--session",
|
||||
"s1",
|
||||
"--width",
|
||||
"0",
|
||||
"--height",
|
||||
"844"
|
||||
])
|
||||
.is_err()
|
||||
);
|
||||
// Non-positive dpr.
|
||||
assert!(
|
||||
Cli::try_parse_from([
|
||||
"bsk",
|
||||
"emulate",
|
||||
"--session",
|
||||
"s1",
|
||||
"--width",
|
||||
"390",
|
||||
"--height",
|
||||
"844",
|
||||
"--dpr",
|
||||
"0"
|
||||
])
|
||||
.is_err()
|
||||
);
|
||||
assert!(
|
||||
Cli::try_parse_from([
|
||||
"bsk",
|
||||
"emulate",
|
||||
"--session",
|
||||
"s1",
|
||||
"--width",
|
||||
"390",
|
||||
"--height",
|
||||
"844",
|
||||
"--dpr",
|
||||
"abc"
|
||||
])
|
||||
.is_err()
|
||||
);
|
||||
// Zero touch points.
|
||||
assert!(
|
||||
Cli::try_parse_from([
|
||||
"bsk",
|
||||
"emulate",
|
||||
"--session",
|
||||
"s1",
|
||||
"--max-touch-points",
|
||||
"0"
|
||||
])
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user