selenium-rs
selenium-rs copied to clipboard
[CHROME] find_element() -> Error(Json (Error ("invalid type: null, expected a string", line: 3, column: 19))
hello, I'm using the code that is provided in the documentation so this one:
let mut driver = WebDriver::new(Browser::Chrome);
driver.start_session();
driver.navigate("http://google.com");
let search_bar = driver.find_element(Selector::CSS, "input[maxlength=\"2048\"]").unwrap();
search_bar.type_text("selenium-rs github");
let search_button = driver.find_element(Selector::CSS, "input[name=\"btnK\"]").unwrap();
search_button.click();
and it shows me this error:
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Error(Json(Error("invalid type: null, expected a string", line: 3, column: 19)))', src\main.rs:20:71
I'm seeing that the selenium-rs is not communicating well with the the local Selenium API. It's returning sessionId: null to selenium-rs when trying to find an element, which selenium-rs requires in the response (ElementResponse). I have a fix incoming, but it's relatively large and changes some semantics.
As an aside: does anyone have a reference to the standalone Selenium server REST API (for selenium-server-standalone-3.141.59.jar)? It would be incredibly useful for this fix. I seem to be unable to find it in ~10m of web searching.
Here's a hacky workaround I whipped up (sorry for the whitespace changes). I only updated find_elements but the same hack can be applied to other functions I'm sure.
diff --git a/src/element.rs b/src/element.rs
index 743064b..8ff48ce 100644
--- a/src/element.rs
+++ b/src/element.rs
@@ -1,13 +1,13 @@
-/*!
+/*!
Element enables most of the site interaction, and wraps user interactions such as typing text and clicking
on things. Note that each element is tied to the specific session (currently, we
can't hold on to the same element across sessions).
- # Example - Inspecting attributes of an element
+ # Example - Inspecting attributes of an element
- ```rust
+ ```rust
use selenium_rs::webdriver::{Selector, Browser, WebDriver};
use selenium_rs::element::Element;
@@ -43,6 +43,11 @@ impl<'a> Element<'a> {
client,
}
}
+
+ pub fn set_session_id(&mut self, session_id: String) {
+ self.session_id = session_id;
+ }
}
// Contains implementation for attribute interaction for the element
diff --git a/src/element_structs.rs b/src/element_structs.rs
index ff8cf36..e22a47e 100644
--- a/src/element_structs.rs
+++ b/src/element_structs.rs
@@ -1,3 +1,4 @@
+use std::collections::HashMap;
use element::Element;
use reqwest;
@@ -11,29 +12,24 @@ pub struct SelectedResponse {
#[derive(Deserialize)]
pub struct ElementResponse {
#[serde(rename = "sessionId")]
- session_id: String,
+ session_id: Option<String>,
status: i32,
- value: ElemValue,
+ value: HashMap<String, String>,
}
#[allow(dead_code)]
-#[derive(Deserialize)]
+#[derive(Deserialize, Debug)]
pub struct ElementsResponse {
#[serde(rename = "sessionId")]
- session_id: String,
+ session_id: Option<String>,
status: i32,
- value: Vec<ElemValue>,
-}
-
-#[derive(Deserialize)]
-struct ElemValue {
- #[serde(rename = "ELEMENT")]
- element_id: String,
+ value: Vec<HashMap<String, String>>,
}
impl<'a> ElementResponse {
pub fn parse_into_element(self, client: &'a reqwest::Client) -> Element<'a> {
- Element::new(self.value.element_id, self.session_id, client)
+ let element_id = self.value.values().next().expect("one element");
+ Element::new(element_id.clone(), self.session_id.as_ref().map_or("null".to_string(), |id| id.clone()), client)
}
}
@@ -42,7 +38,10 @@ impl<'a> ElementsResponse {
let session_id = self.session_id;
self.value
.into_iter()
- .map(|value| Element::new(value.element_id, session_id.clone(), client))
+ .map(|value| {
+ let element_id = value.values().next().expect("one element");
+ Element::new(element_id.clone(), session_id.as_ref().map_or("null".to_string(), |id| id.clone()), client)
+ })
.collect()
}
}
@@ -51,7 +50,7 @@ impl<'a> ElementsResponse {
#[derive(Deserialize)]
pub struct AttributeResponse {
#[serde(rename = "sessionId")]
- session_id: String,
+ session_id: Option<String>,
pub value: String,
}
diff --git a/src/webdriver.rs b/src/webdriver.rs
index ddc270e..47a0196 100644
--- a/src/webdriver.rs
+++ b/src/webdriver.rs
@@ -199,13 +199,17 @@ impl WebDriver {
let sess_id = self.session_id.clone().unwrap();
let url = construct_url(vec!["session/", &(sess_id + "/"), "elements"]);
let payload = ElementRequest::new(str_for_selector(selector), query.to_string());
let response: ElementsResponse = self.client
.post(url)
.json(&payload)
.send()?
.error_for_status()?
.json()?;
- let elements = response.parse_into_elements(&self.client);
+ let mut elements = response.parse_into_elements(&self.client);
+ for element in &mut elements {
+ element.set_session_id(self.session_id.clone().unwrap());
+ }
Ok(elements)
}
}
As an aside: does anyone have a reference to the standalone Selenium server REST API [...]?
I'm struggling without this too! The fundamental issue I don't really understand is why the sessionId field is null, or why an element needs it's own session id at all. But I'm not really that knowledgeable of Selenium, I just use it occasionally.