1use element::Element;
20use element_structs::{ElementResponse, ElementsResponse, ExecuteScriptResponse};
21use reqwest;
22use session_structs::{NewSessionRequest, NewSessionResponse, TitleResponse};
23use std::collections::HashMap;
24use utils::*;
25
26#[derive(Clone, Copy, Debug)]
27pub enum Browser {
28 Chrome,
29 Firefox,
30}
31
32#[derive(Clone, Copy, Debug)]
33pub enum Selector {
34 CSS,
35 LinkText,
36 PartialLinkText,
37 TagName,
38 XPath,
39 ID,
40}
41
42#[derive(Serialize, Deserialize)]
43struct ElementRequest {
44 using: String,
45 value: String,
46}
47
48impl ElementRequest {
49 pub fn new(using: String, value: String) -> ElementRequest {
50 ElementRequest { using, value }
51 }
52}
53
54#[derive(Serialize)]
55struct ExecuteScriptRequest {
56 script: String,
57 args: Vec<serde_json::Value>,
58}
59
60impl ExecuteScriptRequest {
61 pub fn new(script: String, args: Vec<serde_json::Value>) -> ExecuteScriptRequest {
62 ExecuteScriptRequest { script, args }
63 }
64}
65
66pub struct WebDriver {
70 browser: String,
71 client: reqwest::Client,
72 session_id: Option<String>,
73}
74
75impl WebDriver {
77 pub fn new(browser: Browser) -> WebDriver {
81 let browser = get_browser_string(browser);
82 WebDriver {
83 browser,
84 client: reqwest::Client::new(),
85 session_id: None,
86 }
87 }
88}
89
90impl WebDriver {
92 pub fn start_session(&mut self) -> reqwest::Result<()> {
96 let body = NewSessionRequest::new(&self.browser);
97 let url = construct_url(vec!["session/"]);
98
99 let response: NewSessionResponse = self.client
100 .post(url)
101 .json(&body)
102 .send()?
103 .error_for_status()?
104 .json()?;
105
106 self.session_id = Some(response.get_session_id());
107 Ok(())
108 }
109 pub fn get_current_url(&self) -> reqwest::Result<String> {
112 let url = construct_url(vec![
113 "session/",
114 &(self.session_id.clone().unwrap() + "/"),
115 "url",
116 ]);
117 let response: TitleResponse = self.client.get(url).send()?.error_for_status()?.json()?;
118
119 Ok(response.get_title())
120 }
121
122 pub fn get_title(&self) -> reqwest::Result<String> {
123 let url = construct_url(vec![
124 "session/",
125 &(self.session_id.clone().unwrap() + "/"),
126 "title",
127 ]);
128
129 let response: TitleResponse = self.client.get(url).send()?.error_for_status()?.json()?;
130
131 Ok(response.get_title())
132 }
133}
134
135impl WebDriver {
137 pub fn navigate(&self, url: &str) -> reqwest::Result<()> {
141 let sess_id = self.session_id.clone().unwrap();
142 let nav_url = construct_url(vec!["session/", &(sess_id + "/"), "url"]);
143 let mut payload = HashMap::new();
144 payload.insert("url", url);
145 self.client
146 .post(nav_url)
147 .json(&payload)
148 .send()?
149 .error_for_status()?;
150 Ok(())
151 }
152
153 pub fn forward(&self) -> reqwest::Result<()> {
154 let sess_id = self.session_id.clone().unwrap();
155 let nav_url = construct_url(vec!["session/", &(sess_id + "/"), "forward"]);
156 self.client.post(nav_url).send()?.error_for_status()?;
157 Ok(())
158 }
159
160 pub fn back(&self) -> reqwest::Result<()> {
161 let sess_id = self.session_id.clone().unwrap();
162 let nav_url = construct_url(vec!["session/", &(sess_id + "/"), "back"]);
163 self.client.post(nav_url).send()?.error_for_status()?;
164 Ok(())
165 }
166}
167
168impl WebDriver {
170 #[deprecated(since = "0.1.2", note = "query_element does not follow WebDriver naming convention, use find_element")]
172 pub fn query_element(&self, selector: Selector, query: &str) -> reqwest::Result<Element> {
173 self.find_element(selector, query)
174 }
175
176 pub fn find_element(&self, selector: Selector, query: &str) -> reqwest::Result<Element> {
178 let sess_id = self.session_id.clone().unwrap();
179 let url = construct_url(vec!["session/", &(sess_id + "/"), "element"]);
180 let payload = ElementRequest::new(str_for_selector(selector), query_string_for_selector(selector, query));
181 let response: ElementResponse = self.client
182 .post(url)
183 .json(&payload)
184 .send()?
185 .error_for_status()?
186 .json()?;
187 let element = response.parse_into_element(&self.client);
188 Ok(element)
189 }
190
191 #[deprecated(since = "0.1.2", note = "query_elements does not follow WebDriver naming convention, use find_elements")]
193 pub fn query_elements(&self, selector: Selector, query: &str) -> reqwest::Result<Vec<Element>> {
194 self.find_elements(selector, query)
195 }
196
197 pub fn find_elements(&self, selector: Selector, query: &str) -> reqwest::Result<Vec<Element>> {
199 let sess_id = self.session_id.clone().unwrap();
200 let url = construct_url(vec!["session/", &(sess_id + "/"), "elements"]);
201 let payload = ElementRequest::new(str_for_selector(selector), query.to_string());
202 let response: ElementsResponse = self.client
203 .post(url)
204 .json(&payload)
205 .send()?
206 .error_for_status()?
207 .json()?;
208 let elements = response.parse_into_elements(&self.client);
209 Ok(elements)
210 }
211}
212
213impl WebDriver {
215 pub fn execute_script<T: serde::de::DeserializeOwned>(&self, script: &str, args: &[serde_json::Value]) -> reqwest::Result<T> {
217 let sess_id = self.session_id.clone().unwrap();
218 let url = construct_url(vec!["session/", &(sess_id + "/"), "execute/sync"]);
219 let payload = ExecuteScriptRequest::new(script.to_string(), args.to_owned());
220 let response: ExecuteScriptResponse<T> = self.client
221 .post(url)
222 .json(&payload)
223 .send()?
224 .error_for_status()?
225 .json()?;
226 Ok(response.value)
227 }
228}
229
230impl Drop for WebDriver {
231 fn drop(&mut self) {
232 if let Some(ref id) = self.session_id {
233 let url = construct_url(vec!["session/", id]);
234 let _ = self.client.delete(url).send();
235 }
236 }
237}