use crate::error::{no_such_element, WebDriverError, WebDriverResult};
use crate::{By, WebElement};
fn set_selected(element: &WebElement<'_>, select: bool) -> WebDriverResult<()> {
if element.is_selected()? != select {
element.click()?;
}
Ok(())
}
pub fn escape_string(value: &str) -> String {
let contains_single = value.contains('\'');
let contains_double = value.contains('\"');
if contains_single && contains_double {
let mut result = vec![String::from("concat(")];
for substring in value.split('\"') {
result.push(format!("\"{}\"", substring));
result.push(String::from(", '\"', "));
}
result.pop();
if value.ends_with('\"') {
result.push(String::from(", '\"'"));
}
return result.join("") + ")";
}
if contains_double {
format!("'{}'", value)
} else {
format!("\"{}\"", value)
}
}
fn get_longest_token(value: &str) -> &str {
let mut longest = "";
for item in value.split(' ') {
if item.len() > longest.len() {
longest = item;
}
}
longest
}
pub struct SelectElement<'a> {
element: WebElement<'a>,
multiple: bool,
}
impl<'a> SelectElement<'a> {
pub fn new(element: &WebElement<'a>) -> WebDriverResult<SelectElement<'a>> {
let multiple = element.get_attribute("multiple")?.filter(|x| x != "false").is_some();
let element = element.clone();
Ok(SelectElement {
element,
multiple,
})
}
pub fn options(&self) -> WebDriverResult<Vec<WebElement>> {
self.element.find_elements(By::Tag("option"))
}
pub fn all_selected_options(&self) -> WebDriverResult<Vec<WebElement>> {
let mut selected = Vec::new();
for option in self.options()? {
if option.is_selected()? {
selected.push(option);
}
}
Ok(selected)
}
pub fn first_selected_option(&self) -> WebDriverResult<WebElement> {
for option in self.options()? {
if option.is_selected()? {
return Ok(option);
}
}
Err(no_such_element("No options are selected"))
}
fn set_selection_all(&self, select: bool) -> WebDriverResult<()> {
for option in self.options()? {
set_selected(&option, select)?;
}
Ok(())
}
fn set_selection_by_value(&self, value: &str, select: bool) -> WebDriverResult<()> {
let selector = format!("option[value={}]", escape_string(value));
let options = self.element.find_elements(By::Css(&selector))?;
for option in options {
set_selected(&option, select)?;
if !self.multiple {
break;
}
}
Ok(())
}
fn set_selection_by_index(&self, index: u32, select: bool) -> WebDriverResult<()> {
let str_index: String = index.to_string();
for option in self.options()? {
if option.get_attribute("index")?.filter(|i| i == &str_index).is_some() {
set_selected(&option, select)?;
return Ok(());
}
}
Err(no_such_element(&format!("Could not locate element with index {}", index)))
}
fn set_selection_by_visible_text(&self, text: &str, select: bool) -> WebDriverResult<()> {
let mut xpath = format!(".//option[normalize-space(.) = {}]", escape_string(text));
let options = match self.element.find_elements(By::XPath(&xpath)) {
Ok(elems) => elems,
Err(WebDriverError::NoSuchElement(_)) => Vec::new(),
Err(e) => return Err(e),
};
let mut matched = false;
for option in &options {
set_selected(option, select)?;
if !self.multiple {
return Ok(());
}
matched = true;
}
if options.is_empty() && text.contains(' ') {
let substring_without_space = get_longest_token(text);
let candidates = if substring_without_space.is_empty() {
self.options()?
} else {
xpath =
format!(".//option[contains(.,{})]", escape_string(substring_without_space));
self.element.find_elements(By::XPath(&xpath))?
};
for candidate in candidates {
if text == candidate.text()? {
set_selected(&candidate, select)?;
if !self.multiple {
return Ok(());
}
matched = true;
}
}
}
if !matched {
Err(no_such_element(&format!("Could not locate element with visible text: {}", text)))
} else {
Ok(())
}
}
fn set_selection_by_xpath_condition(
&self,
condition: &str,
select: bool,
) -> WebDriverResult<()> {
let xpath = format!(".//option[{}]", condition);
let options = self.element.find_elements(By::XPath(&xpath))?;
if options.is_empty() {
return Err(no_such_element(&format!(
"Could not locate element matching XPath condition: {:?}",
xpath
)));
}
for option in &options {
set_selected(option, select)?;
if !self.multiple {
break;
}
}
Ok(())
}
fn set_selection_by_exact_text(&self, text: &str, select: bool) -> WebDriverResult<()> {
let condition = format!("text() = {}", escape_string(text));
self.set_selection_by_xpath_condition(&condition, select)
}
fn set_selection_by_partial_text(&self, text: &str, select: bool) -> WebDriverResult<()> {
let condition = format!("contains(text(), {})", escape_string(text));
self.set_selection_by_xpath_condition(&condition, select)
}
pub fn select_all(&self) -> WebDriverResult<()> {
assert!(self.multiple, "You may only select all options of a multi-select");
self.set_selection_all(true)
}
pub fn select_by_value(&self, value: &str) -> WebDriverResult<()> {
self.set_selection_by_value(value, true)
}
pub fn select_by_index(&self, index: u32) -> WebDriverResult<()> {
self.set_selection_by_index(index, true)
}
pub fn select_by_visible_text(&self, text: &str) -> WebDriverResult<()> {
self.set_selection_by_visible_text(text, true)
}
pub fn select_by_xpath_condition(&self, condition: &str) -> WebDriverResult<()> {
self.set_selection_by_xpath_condition(condition, true)
}
pub fn select_by_exact_text(&self, text: &str) -> WebDriverResult<()> {
self.set_selection_by_exact_text(text, true)
}
pub fn select_by_partial_text(&self, text: &str) -> WebDriverResult<()> {
self.set_selection_by_partial_text(text, true)
}
pub fn deselect_all(&self) -> WebDriverResult<()> {
assert!(self.multiple, "You may only deselect all options of a multi-select");
self.set_selection_all(false)
}
pub fn deselect_by_value(&self, value: &str) -> WebDriverResult<()> {
assert!(self.multiple, "You may only deselect options of a multi-select");
self.set_selection_by_value(value, false)
}
pub fn deselect_by_index(&self, index: u32) -> WebDriverResult<()> {
assert!(self.multiple, "You may only deselect options of a multi-select");
self.set_selection_by_index(index, false)
}
pub fn deselect_by_visible_text(&self, text: &str) -> WebDriverResult<()> {
assert!(self.multiple, "You may only deselect options of a multi-select");
self.set_selection_by_visible_text(text, false)
}
pub fn deselect_by_xpath_condition(&self, condition: &str) -> WebDriverResult<()> {
self.set_selection_by_xpath_condition(condition, false)
}
pub fn deselect_by_exact_text(&self, text: &str) -> WebDriverResult<()> {
assert!(self.multiple, "You may only deselect options of a multi-select");
self.set_selection_by_exact_text(text, false)
}
pub fn deselect_by_partial_text(&self, text: &str) -> WebDriverResult<()> {
assert!(self.multiple, "You may only deselect options of a multi-select");
self.set_selection_by_partial_text(text, false)
}
}