1use allwright_surface_mobile::{ConnectOptions, LaunchOptions, MobilePlatform};
2use allwright_surface_mobile_android::{click_element, connect, launch_app};
3use serde_json::json;
4use std::env;
5use std::error::Error;
6
7fn main() -> Result<(), Box<dyn Error>> {
8 let args = parse_args(env::args().skip(1).collect())?;
9
10 let session = connect(&ConnectOptions {
11 platform: MobilePlatform::Android,
12 device: args.device,
13 adb_endpoint: args.adb_endpoint,
14 preserve_app_state: false,
15 timeout_ms: Some(10_000),
16 })?;
17
18 let page = launch_app(
19 &session.browser_session,
20 &LaunchOptions {
21 apk_path: Some(args.apk_path),
22 app_id: args.app_id,
23 launch_activity: args.activity,
24 stop_before_launch: args.stop_before_launch,
25 timeout_ms: Some(30_000),
26 },
27 )?;
28
29 let clicked = click_element(
30 &session.browser_session,
31 &page.page_session,
32 &args.selector,
33 Some(10_000),
34 )?;
35
36 println!(
37 "{}",
38 serde_json::to_string_pretty(&json!({
39 "session": session,
40 "page": page,
41 "click": clicked,
42 }))?
43 );
44
45 Ok(())
46}
47
48#[derive(Debug, Default)]
49struct SmokeArgs {
50 apk_path: String,
51 selector: String,
52 app_id: Option<String>,
53 activity: Option<String>,
54 device: Option<String>,
55 adb_endpoint: Option<String>,
56 stop_before_launch: bool,
57}
58
59fn parse_args(args: Vec<String>) -> Result<SmokeArgs, Box<dyn Error>> {
60 let mut parsed = SmokeArgs::default();
61 let mut index = 0usize;
62 while index < args.len() {
63 match args[index].as_str() {
64 "--apk" => {
65 index += 1;
66 parsed.apk_path = required_value(&args, index, "--apk")?;
67 }
68 "--selector" => {
69 index += 1;
70 parsed.selector = required_value(&args, index, "--selector")?;
71 }
72 "--app-id" => {
73 index += 1;
74 parsed.app_id = Some(required_value(&args, index, "--app-id")?);
75 }
76 "--activity" => {
77 index += 1;
78 parsed.activity = Some(required_value(&args, index, "--activity")?);
79 }
80 "--device" => {
81 index += 1;
82 parsed.device = Some(required_value(&args, index, "--device")?);
83 }
84 "--adb-endpoint" => {
85 index += 1;
86 parsed.adb_endpoint = Some(required_value(&args, index, "--adb-endpoint")?);
87 }
88 "--stop-before-launch" => parsed.stop_before_launch = true,
89 "--help" | "-h" => {
90 print_usage();
91 std::process::exit(0);
92 }
93 other => {
94 return Err(format!("unknown argument `{other}`").into());
95 }
96 }
97 index += 1;
98 }
99
100 if parsed.apk_path.trim().is_empty() {
101 return Err("missing required `--apk <path>` argument".into());
102 }
103 if parsed.selector.trim().is_empty() {
104 return Err("missing required `--selector <selector>` argument".into());
105 }
106
107 Ok(parsed)
108}
109
110fn required_value(args: &[String], index: usize, flag: &str) -> Result<String, Box<dyn Error>> {
111 args.get(index)
112 .cloned()
113 .ok_or_else(|| format!("missing value for `{flag}`").into())
114}
115
116fn print_usage() {
117 eprintln!(
118 "Usage: cargo run -p allwright-surface-mobile-android --example smoke -- \
119 --apk /path/to/app.apk \
120 --selector 'xpath=//*[@text=\"Login\"]' \
121 [--app-id dev.allwright.sample] \
122 [--activity .MainActivity] \
123 [--device emulator-5554] \
124 [--adb-endpoint 192.168.1.10:5555] \
125 [--stop-before-launch]"
126 );
127}