Path: blob/trunk/rb/lib/selenium/webdriver/support/select.rb
1865 views
# frozen_string_literal: true12# Licensed to the Software Freedom Conservancy (SFC) under one3# or more contributor license agreements. See the NOTICE file4# distributed with this work for additional information5# regarding copyright ownership. The SFC licenses this file6# to you under the Apache License, Version 2.0 (the7# "License"); you may not use this file except in compliance8# with the License. You may obtain a copy of the License at9#10# http://www.apache.org/licenses/LICENSE-2.011#12# Unless required by applicable law or agreed to in writing,13# software distributed under the License is distributed on an14# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY15# KIND, either express or implied. See the License for the16# specific language governing permissions and limitations17# under the License.1819module Selenium20module WebDriver21module Support22class Select23#24# @param [Element] element The select element to use25#2627def initialize(element)28tag_name = element.tag_name2930raise ArgumentError, "unexpected tag name #{tag_name.inspect}" unless tag_name.casecmp('select').zero?3132@element = element33@multi = ![nil, 'false'].include?(element.dom_attribute(:multiple))34end3536#37# Does this select element support selecting multiple options?38#39# @return [Boolean]40#4142def multiple?43@multi44end4546#47# Get all options for this select element48#49# @return [Array<Element>]50#5152def options53@element.find_elements tag_name: 'option'54end5556#57# Get all selected options for this select element58#59# @return [Array<Element>]60#6162def selected_options63options.select(&:selected?)64end6566#67# Get the first selected option in this select element68#69# @raise [Error::NoSuchElementError] if no options are selected70# @return [Element]71#7273def first_selected_option74option = options.find(&:selected?)75return option if option7677raise Error::NoSuchElementError, 'no options are selected'78end7980#81# Select options by visible text, index or value.82#83# When selecting by :text, selects options that display text matching the argument. That is, when given "Bar" this84# would select an option like:85#86# <option value="foo">Bar</option>87#88# When selecting by :value, selects all options that have a value matching the argument. That is, when given "foo" this89# would select an option like:90#91# <option value="foo">Bar</option>92#93# When selecting by :index, selects the option at the given index. This is done by examining the "index" attribute of an94# element, and not merely by counting.95#96# @param [:text, :index, :value] how How to find the option97# @param [String] what What value to find the option by.98#99100def select_by(how, what)101case how102when :text103select_by_text what104when :index105select_by_index what106when :value107select_by_value what108else109raise ArgumentError, "can't select options by #{how.inspect}"110end111end112113#114# Deselect options by visible text, index or value.115#116# @param [:text, :index, :value] how How to find the option117# @param [String] what What value to find the option by.118# @raise [Error::UnsupportedOperationError] if the element does not support multiple selections.119#120# @see Select#select_by121#122123def deselect_by(how, what)124case how125when :text126deselect_by_text what127when :value128deselect_by_value what129when :index130deselect_by_index what131else132raise ArgumentError, "can't deselect options by #{how.inspect}"133end134end135136#137# Select all unselected options. Only valid if the element supports multiple selections.138#139# @raise [Error::UnsupportedOperationError] if the element does not support multiple selections.140#141142def select_all143raise Error::UnsupportedOperationError, 'you may only select all options of a multi-select' unless multiple?144145options.each { |e| select_option e }146end147148#149# Deselect all selected options. Only valid if the element supports multiple selections.150#151# @raise [Error::UnsupportedOperationError] if the element does not support multiple selections.152#153154def deselect_all155raise Error::UnsupportedOperationError, 'you may only deselect all options of a multi-select' unless multiple?156157options.each { |e| deselect_option e }158end159160private161162def select_by_text(text)163opts = find_by_text text164165return select_options(opts) unless opts.empty?166167raise Error::NoSuchElementError, "cannot locate element with text: #{text.inspect}"168end169170def select_by_index(index)171opts = find_by_index index172173return select_option(opts.first) unless opts.empty?174175raise Error::NoSuchElementError, "cannot locate element with index: #{index.inspect}"176end177178def select_by_value(value)179opts = find_by_value value180181return select_options(opts) unless opts.empty?182183raise Error::NoSuchElementError, "cannot locate option with value: #{value.inspect}"184end185186def deselect_by_text(text)187raise Error::UnsupportedOperationError, 'you may only deselect option of a multi-select' unless multiple?188189opts = find_by_text text190191return deselect_options(opts) unless opts.empty?192193raise Error::NoSuchElementError, "cannot locate element with text: #{text.inspect}"194end195196def deselect_by_value(value)197raise Error::UnsupportedOperationError, 'you may only deselect option of a multi-select' unless multiple?198199opts = find_by_value value200201return deselect_options(opts) unless opts.empty?202203raise Error::NoSuchElementError, "cannot locate option with value: #{value.inspect}"204end205206def deselect_by_index(index)207raise Error::UnsupportedOperationError, 'you may only deselect option of a multi-select' unless multiple?208209opts = find_by_index index210211return deselect_option(opts.first) unless opts.empty?212213raise Error::NoSuchElementError, "cannot locate option with index: #{index}"214end215216def select_option(option)217raise Error::UnsupportedOperationError, 'You may not select a disabled option' unless option.enabled?218219option.click unless option.selected?220end221222def deselect_option(option)223option.click if option.selected?224end225226def select_options(opts)227if multiple?228opts.each { |o| select_option o }229else230select_option opts.first231end232end233234def deselect_options(opts)235if multiple?236opts.each { |o| deselect_option o }237else238deselect_option opts.first239end240end241242def find_by_text(text)243xpath = ".//option[normalize-space(.) = #{Escaper.escape text}]"244opts = @element.find_elements(xpath: xpath)245246return opts unless opts.empty? && /\s+/.match?(text)247248longest_word = text.split(/\s+/).max_by(&:length)249if longest_word.empty?250candidates = options251else252xpath = ".//option[contains(., #{Escaper.escape longest_word})]"253candidates = @element.find_elements(xpath: xpath)254end255256return Array(candidates.find { |option| text == option.text }) unless multiple?257258candidates.select { |option| text == option.text }259end260261def find_by_index(index)262options.select { |option| option.property(:index) == index }263end264265def find_by_value(value)266@element.find_elements(xpath: ".//option[@value = #{Escaper.escape value}]")267end268end # Select269end # Support270end # WebDriver271end # Selenium272273274