1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
//! Commands that you can issue to Appium server
//!
//! The commands in submodules are a facade to low-level `issue_cmd` ([fantoccini::Client::issue_cmd]).
//! So in most cases, you need a specific function from one of those modules (e.g. [keyboard::HidesKeyboard::hide_keyboard]).
//!
//! ## Available commands
//! **Please check all submodules if you want to learn what features are implemented in this lib.**
//! See traits in below modules to learn what you can do with the client.
//!
//! Alternatively, you can check out [crate::IOSClient] and [crate::AndroidClient] to see all traits of those clients in the docs.
//!
//! ## How to use commands
//! [AppiumCommand] is a struct used by low-level `issue_cmd` ([fantoccini::Client::issue_cmd]).
//! So unless you're implementing missing features yourself, you don't need to wory about it.
//!
//! This lib exposes both APIs to be more flexible.
//! So a rule of thumb is:
//! * use a command from submodule if it's available (in other words - use by default),
//! * use [AppiumCommand::Custom] in other cases
//!
//! ```no_run
//!# use http::Method;
//!# use serde_json::json;
//!# use appium_client::capabilities::android::AndroidCapabilities;
//!# use appium_client::capabilities::{AppCapable, UdidCapable, UiAutomator2AppCompatible};
//!# use appium_client::ClientBuilder;
//!# use appium_client::commands::AppiumCommand;
//!# use appium_client::commands::keyboard::HidesKeyboard;
//!# use appium_client::find::{AppiumFind, By};
//!
//!# #[tokio::main]
//!# async fn main() -> Result<(), Box<dyn std::error::Error>> {
//!# // create capabilities
//! let mut capabilities = AndroidCapabilities::new_uiautomator();
//!# capabilities.udid("emulator-5554");
//!# capabilities.app("/apps/sample.apk");
//!# capabilities.app_wait_activity("com.example.AppActivity");
//!
//! let client = ClientBuilder::native(capabilities)
//! .connect("http://localhost:4723/wd/hub/")
//! .await?;
//!
//! // this feature is implemented in keyboard submodule (recommended)
//! client.hide_keyboard().await?;
//!
//! // this is a low-level implementation of the same command (not recommended, unless you have a specific use case for this)
//! client.issue_cmd(AppiumCommand::Custom(
//! Method::POST,
//! "appium/device/hide_keyboard".to_string(),
//! Some(json!({})),
//! )).await?;
//!
//!# Ok(())
//!# }
//! ```
//!
pub mod rotation;
pub mod keyboard;
pub mod lock;
pub mod contexts;
pub mod location;
pub mod time;
pub mod files;
pub mod apps;
pub mod strings;
pub mod network;
pub mod android;
pub mod settings;
pub mod authentication;
pub mod recording;
pub mod clipboard;
pub mod battery;
pub mod ios;
use fantoccini::wd::WebDriverCompatibleCommand;
use http::Method;
use serde_json::Value;
use crate::find::By;
/// Basic Appium commands
///
/// Use Custom if you want to implement anything non-standard.
/// Those commands are to be used with `issue_cmd` ([fantoccini::Client::issue_cmd]).
#[derive(Debug, PartialEq)]
pub enum AppiumCommand {
FindElement(By),
FindElementWithContext(By, String),
FindElements(By),
FindElementsWithContext(By, String),
Custom(Method, String, Option<Value>),
}
impl WebDriverCompatibleCommand for AppiumCommand {
fn endpoint(
&self,
base_url: &url::Url,
session_id: Option<&str>,
) -> Result<url::Url, url::ParseError> {
let base = { base_url.join(&format!("session/{}/", session_id.as_ref().unwrap()))? };
match self {
AppiumCommand::FindElement(..) =>
base.join("element"),
AppiumCommand::FindElements(..) =>
base.join("elements"),
AppiumCommand::FindElementWithContext(.., context) =>
base.join("element")
.and_then(|url| url.join(context))
.and_then(|url| url.join("element")),
AppiumCommand::FindElementsWithContext(.., context) =>
base.join("element")
.and_then(|url| url.join(context))
.and_then(|url| url.join("elements")),
AppiumCommand::Custom(_, command, ..) =>
base.join(command),
}
}
fn method_and_body(&self, _request_url: &url::Url) -> (Method, Option<String>) {
match self {
AppiumCommand::FindElement(by)
| AppiumCommand::FindElements(by)
| AppiumCommand::FindElementWithContext(by, ..)
| AppiumCommand::FindElementsWithContext(by, ..) => {
let method = Method::POST;
let body = Some(serde_json::to_string(&by).unwrap());
(method, body)
},
AppiumCommand::Custom(method, .., value) => {
let body = value.clone()
.map(|v| v.to_string());
(method.clone(), body)
}
}
}
fn is_new_session(&self) -> bool {
false
}
fn is_legacy(&self) -> bool {
false
}
}