Skip to main content

smoke/
smoke.rs

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}