module Watir
class Select < HTMLElement
include Watir::Exception
#
# Returns true if this element is enabled
#
# @return [Boolean]
#
def enabled?
!disabled?
end
#
# Clears all selected options.
#
def clear
assert_exists
raise Error, "you can only clear multi-selects" unless multiple?
options.each do |o|
o.click if o.selected?
end
end
#
# Gets all the options in the select list
#
# @return [Watir::OptionCollection]
#
def options
assert_exists
super
end
#
# Returns true if the select list has one or more options where text or label matches the given value.
#
# @param [String, Regexp] str_or_rx
# @return [Boolean]
#
def include?(str_or_rx)
assert_exists
element_call do
@element.find_elements(:tag_name, 'option').any? do |e|
str_or_rx === e.text || str_or_rx === e.attribute(:label)
end
end
end
#
# Select the option(s) whose text or label matches the given string.
# If this is a multi-select and several options match the value given, all will be selected.
#
# @param [String, Regexp] str_or_rx
# @raise [Watir::Exception::NoValueFoundException] if the value does not exist.
# @return [String] The text of the option selected. If multiple options match, returns the first match.
#
def select(str_or_rx)
select_by :text, str_or_rx
end
#
# Selects the option(s) whose value attribute matches the given string.
#
# @see +select+
#
# @param [String, Regexp] str_or_rx
# @raise [Watir::Exception::NoValueFoundException] if the value does not exist.
# @return [String] The option selected. If multiple options match, returns the first match
#
def select_value(str_or_rx)
select_by :value, str_or_rx
end
#
# Returns true if any of the selected options' text or label matches the given value.
#
# @param [String, Regexp] str_or_rx
# @raise [Watir::Exception::UnknownObjectException] if the options do not exist
# @return [Boolean]
#
def selected?(str_or_rx)
assert_exists
match_found = false
element_call do
@element.find_elements(:tag_name, 'option').each do |e|
matched = str_or_rx === e.text || str_or_rx === e.attribute(:label)
if matched
return true if e.selected?
match_found = true
end
end
end
raise(UnknownObjectException, "Unable to locate option matching #{str_or_rx.inspect}") unless match_found
false
end
#
# Returns the value of the first selected option in the select list.
# Returns nil if no option is selected.
#
# @return [String, nil]
#
def value
o = options.find { |e| e.selected? } || return
o.value
end
#
# Returns an array of currently selected options.
#
# @return [Array]
#
def selected_options
options.select { |e| e.selected? }
end
private
def select_by(how, str_or_rx)
assert_exists
case str_or_rx
when String, Numeric
select_by_string(how, str_or_rx.to_s)
when Regexp
select_by_regexp(how, str_or_rx)
else
raise TypeError, "expected String or Regexp, got #{str_or_rx.inspect}:#{str_or_rx.class}"
end
end
def select_by_string(how, string)
xpath = option_xpath_for(how, string)
if multiple?
elements = element_call do
@element.find_elements(:xpath, xpath)
end
no_value_found(string) if elements.empty?
elements.each { |e| e.click unless e.selected? }
elements.first.text
else
begin
e = element_call do
@element.find_element(:xpath, xpath)
end
rescue Selenium::WebDriver::Error::NoSuchElementError
no_value_found(string)
end
e.click unless e.selected?
safe_text(e)
end
end
def select_by_regexp(how, exp)
elements = element_call do
@element.find_elements(:tag_name, 'option')
end
no_value_found(nil, "no options in select list") if elements.empty?
if multiple?
found = elements.select do |e|
next unless matches_regexp?(how, e, exp)
e.click unless e.selected?
true
end
no_value_found(exp) if found.empty?
found.first.text
else
element = elements.find { |e| matches_regexp?(how, e, exp) }
no_value_found(exp) unless element
element.click unless element.selected?
safe_text(element)
end
end
def option_xpath_for(how, string)
string = XpathSupport.escape string
case how
when :text
".//option[normalize-space()=#{string} or @label=#{string}]"
when :value
".//option[@value=#{string}]"
else
raise Error, "unknown how: #{how.inspect}"
end
end
def matches_regexp?(how, element, exp)
case how
when :text
element.text =~ exp || element.attribute(:label) =~ exp
when :value
element.attribute(:value) =~ exp
else
raise Error, "unknown how: #{how.inspect}"
end
end
def safe_text(element)
element.text
rescue Selenium::WebDriver::Error::StaleElementReferenceError, Selenium::WebDriver::Error::UnhandledAlertError
# guard for scenario where selecting the element changes the page, making our element obsolete
''
end
def no_value_found(arg, msg = nil)
raise NoValueFoundException, msg || "#{arg.inspect} not found in select list"
end
end # Select
module Container
alias_method :select_list, :select
alias_method :select_lists, :selects
Watir.tag_to_class[:select_list] = Select
end # Container
end # Watir