123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241 |
- # Licensed to the Software Freedom Conservancy (SFC) under one
- # or more contributor license agreements. See the NOTICE file
- # distributed with this work for additional information
- # regarding copyright ownership. The SFC licenses this file
- # to you under the Apache License, Version 2.0 (the
- # "License"); you may not use this file except in compliance
- # with the License. You may obtain a copy of the License at
- #
- # http://www.apache.org/licenses/LICENSE-2.0
- #
- # Unless required by applicable law or agreed to in writing,
- # software distributed under the License is distributed on an
- # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- # KIND, either express or implied. See the License for the
- # specific language governing permissions and limitations
- # under the License.
- from selenium.webdriver.common.by import By
- from selenium.common.exceptions import NoSuchElementException, UnexpectedTagNameException
- class Select(object):
- def __init__(self, webelement):
- """
- Constructor. A check is made that the given element is, indeed, a SELECT tag. If it is not,
- then an UnexpectedTagNameException is thrown.
- :Args:
- - webelement - element SELECT element to wrap
- Example:
- from selenium.webdriver.support.ui import Select \n
- Select(driver.find_element_by_tag_name("select")).select_by_index(2)
- """
- if webelement.tag_name.lower() != "select":
- raise UnexpectedTagNameException(
- "Select only works on <select> elements, not on <%s>" %
- webelement.tag_name)
- self._el = webelement
- multi = self._el.get_attribute("multiple")
- self.is_multiple = multi and multi != "false"
- @property
- def options(self):
- """Returns a list of all options belonging to this select tag"""
- return self._el.find_elements(By.TAG_NAME, 'option')
- @property
- def all_selected_options(self):
- """Returns a list of all selected options belonging to this select tag"""
- ret = []
- for opt in self.options:
- if opt.is_selected():
- ret.append(opt)
- return ret
- @property
- def first_selected_option(self):
- """The first selected option in this select tag (or the currently selected option in a
- normal select)"""
- for opt in self.options:
- if opt.is_selected():
- return opt
- raise NoSuchElementException("No options are selected")
- def select_by_value(self, value):
- """Select all options that have a value matching the argument. That is, when given "foo" this
- would select an option like:
- <option value="foo">Bar</option>
- :Args:
- - value - The value to match against
- throws NoSuchElementException If there is no option with specisied value in SELECT
- """
- css = "option[value =%s]" % self._escapeString(value)
- opts = self._el.find_elements(By.CSS_SELECTOR, css)
- matched = False
- for opt in opts:
- self._setSelected(opt)
- if not self.is_multiple:
- return
- matched = True
- if not matched:
- raise NoSuchElementException("Cannot locate option with value: %s" % value)
- def select_by_index(self, index):
- """Select the option at the given index. This is done by examing the "index" attribute of an
- element, and not merely by counting.
- :Args:
- - index - The option at this index will be selected
- throws NoSuchElementException If there is no option with specisied index in SELECT
- """
- match = str(index)
- for opt in self.options:
- if opt.get_attribute("index") == match:
- self._setSelected(opt)
- return
- raise NoSuchElementException("Could not locate element with index %d" % index)
- def select_by_visible_text(self, text):
- """Select all options that display text matching the argument. That is, when given "Bar" this
- would select an option like:
- <option value="foo">Bar</option>
- :Args:
- - text - The visible text to match against
- throws NoSuchElementException If there is no option with specisied text in SELECT
- """
- xpath = ".//option[normalize-space(.) = %s]" % self._escapeString(text)
- opts = self._el.find_elements(By.XPATH, xpath)
- matched = False
- for opt in opts:
- self._setSelected(opt)
- if not self.is_multiple:
- return
- matched = True
- if len(opts) == 0 and " " in text:
- subStringWithoutSpace = self._get_longest_token(text)
- if subStringWithoutSpace == "":
- candidates = self.options
- else:
- xpath = ".//option[contains(.,%s)]" % self._escapeString(subStringWithoutSpace)
- candidates = self._el.find_elements(By.XPATH, xpath)
- for candidate in candidates:
- if text == candidate.text:
- self._setSelected(candidate)
- if not self.is_multiple:
- return
- matched = True
- if not matched:
- raise NoSuchElementException("Could not locate element with visible text: %s" % text)
- def deselect_all(self):
- """Clear all selected entries. This is only valid when the SELECT supports multiple selections.
- throws NotImplementedError If the SELECT does not support multiple selections
- """
- if not self.is_multiple:
- raise NotImplementedError("You may only deselect all options of a multi-select")
- for opt in self.options:
- self._unsetSelected(opt)
- def deselect_by_value(self, value):
- """Deselect all options that have a value matching the argument. That is, when given "foo" this
- would deselect an option like:
- <option value="foo">Bar</option>
- :Args:
- - value - The value to match against
- throws NoSuchElementException If there is no option with specisied value in SELECT
- """
- if not self.is_multiple:
- raise NotImplementedError("You may only deselect options of a multi-select")
- matched = False
- css = "option[value = %s]" % self._escapeString(value)
- opts = self._el.find_elements(By.CSS_SELECTOR, css)
- for opt in opts:
- self._unsetSelected(opt)
- matched = True
- if not matched:
- raise NoSuchElementException("Could not locate element with value: %s" % value)
- def deselect_by_index(self, index):
- """Deselect the option at the given index. This is done by examing the "index" attribute of an
- element, and not merely by counting.
- :Args:
- - index - The option at this index will be deselected
- throws NoSuchElementException If there is no option with specisied index in SELECT
- """
- if not self.is_multiple:
- raise NotImplementedError("You may only deselect options of a multi-select")
- for opt in self.options:
- if opt.get_attribute("index") == str(index):
- self._unsetSelected(opt)
- return
- raise NoSuchElementException("Could not locate element with index %d" % index)
- def deselect_by_visible_text(self, text):
- """Deselect all options that display text matching the argument. That is, when given "Bar" this
- would deselect an option like:
- <option value="foo">Bar</option>
- :Args:
- - text - The visible text to match against
- """
- if not self.is_multiple:
- raise NotImplementedError("You may only deselect options of a multi-select")
- matched = False
- xpath = ".//option[normalize-space(.) = %s]" % self._escapeString(text)
- opts = self._el.find_elements(By.XPATH, xpath)
- for opt in opts:
- self._unsetSelected(opt)
- matched = True
- if not matched:
- raise NoSuchElementException("Could not locate element with visible text: %s" % text)
- def _setSelected(self, option):
- if not option.is_selected():
- option.click()
- def _unsetSelected(self, option):
- if option.is_selected():
- option.click()
- def _escapeString(self, value):
- if '"' in value and "'" in value:
- substrings = value.split("\"")
- result = ["concat("]
- for substring in substrings:
- result.append("\"%s\"" % substring)
- result.append(", '\"', ")
- result = result[0:-1]
- if value.endswith('"'):
- result.append(", '\"'")
- return "".join(result) + ")"
- if '"' in value:
- return "'%s'" % value
- return "\"%s\"" % value
- def _get_longest_token(self, value):
- items = value.split(" ")
- longest = ""
- for item in items:
- if len(item) > len(longest):
- longest = item
- return longest
|