lib/watir/elements/select.rb in watir-7.0.0.beta1 vs lib/watir/elements/select.rb in watir-7.0.0.beta2

- old
+ new

@@ -3,11 +3,11 @@ # # Clears all selected options. # def clear - raise Exception::Error, 'you can only clear multi-selects' unless multiple? + raise Exception::Error, 'you can only clear multi-selects' unless multiple_select_list? selected_options.each(&:click) end # @@ -16,42 +16,46 @@ # @param [String, Regexp] str_or_rx # @return [Boolean] # def include?(str_or_rx) - option(text: str_or_rx).exist? || option(label: str_or_rx).exist? + option(text: str_or_rx).exist? || option(label: str_or_rx).exist? || option(value: str_or_rx).exist? end # # Select the option whose text or label matches the given string. # # @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) - if str_or_rx.size > 1 || str_or_rx.first.is_a?(Array) - str_or_rx.flatten.map { |v| select_all_by v }.first + def select(*str_or_rx, text: nil, value: nil, label: nil) + key, value = parse_select_args(str_or_rx, text, value, label) + + if value.size > 1 || value.first.is_a?(Array) + value.flatten.map { |v| select_all_by key, v }.first else - found = find_options(:value, str_or_rx.flatten.first).first - select_matching([found]) + select_matching([find_option(key, value.flatten.first)]) end end + alias set select # # Uses JavaScript to select the option whose text matches the given string. # # @param [String, Regexp] str_or_rx # @raise [Watir::Exception::NoValueFoundException] if the value does not exist. # - def select!(*str_or_rx) - if str_or_rx.size > 1 || str_or_rx.first.is_a?(Array) - str_or_rx.flatten.map { |v| select_by! v, :multiple }.first + def select!(*str_or_rx, text: nil, value: nil, label: nil) + key, value = parse_select_args(str_or_rx, text, value, label) + + if value.size > 1 || value.first.is_a?(Array) + value.flatten.map { |v| select_by! key, v, :multiple }.first else - str_or_rx.flatten.map { |v| select_by! v, :single }.first + value.flatten.map { |v| select_by! key, v, :single }.first end end # # Returns true if any of the selected options' text or label matches the given value. @@ -59,44 +63,37 @@ # @param [String, Regexp] str_or_rx # @raise [Watir::Exception::UnknownObjectException] if the options do not exist # @return [Boolean] # - def selected?(str_or_rx) - by_text = options(text: str_or_rx) - return true if by_text.find(&:selected?) + def selected?(*str_or_rx, text: nil, value: nil, label: nil) + key, value = parse_select_args(str_or_rx, text, value, label) - by_label = options(label: str_or_rx) - return true if by_label.find(&:selected?) - - return false unless (by_text.size + by_label.size).zero? - - raise(UnknownObjectException, "Unable to locate option matching #{str_or_rx.inspect}") + find_option(key, value.first).selected? 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 - option = selected_options.first - option&.value + selected_options.first&.value end # # Returns the text of the first selected option in the select list. # Returns nil if no option is selected. # # @return [String, nil] # + # TODO: What is default behavior without #first ? def text - option = selected_options.first - option&.text + selected_options.first&.text end # Returns an array of currently selected options. # # @return [Array<Watir::Option>] @@ -106,14 +103,33 @@ element_call { execute_js :selectedOptions, self } end private - def select_by!(str_or_rx, number) + def multiple_select_list? + @multiple_select = @multiple_select.nil? ? multiple? : @multiple_select + end + + def parse_select_args(str_or_rx, text, value, label) + selectors = {} + selectors[:any] = str_or_rx unless str_or_rx.empty? + selectors[:text] = Array[text] if text + selectors[:value] = Array[value] if value + selectors[:label] = Array[label] if label + + raise ArgumentError, "too many arguments used for Select#select: #{selectors}" if selectors.size > 1 + + selectors.first + end + + def select_by!(key, str_or_rx, number) + str_or_rx = type_check(str_or_rx) + js_rx = process_str_or_rx(str_or_rx) + approaches = key == :any ? %w[Text Label Value] : [key.to_s.capitalize] - %w[Text Label Value].each do |approach| + approaches.each do |approach| element_call { execute_js("selectOptions#{approach}", self, js_rx, number.to_s) } return selected_options.first.text if matching_option?(approach.downcase, str_or_rx) end raise_no_value_found(str_or_rx) @@ -145,37 +161,44 @@ raise ObjectDisabledException, "option matching #{what} by #{how} on #{inspect} is disabled" end false end - def select_all_by(str_or_rx) - raise Error, 'you can only use #select_all on multi-selects' unless multiple? + def select_all_by(key, str_or_rx) + raise Error, 'you can only use #select_all on multi-selects' unless multiple_select_list? - select_matching(find_options(:text, str_or_rx)) + select_matching(find_options(key, str_or_rx)) end - def find_options(how, str_or_rx) - msg = "expected String, Numeric or Regexp, got #{str_or_rx.inspect}:#{str_or_rx.class}" - raise TypeError, msg unless [String, Numeric, Regexp].any? { |k| str_or_rx.is_a?(k) } + def find_option(key, str_or_rx) + val = type_check(str_or_rx) - wait_while do - @found = how == :value ? options(value: str_or_rx) : [] - @found = options(text: str_or_rx) if @found.empty? - @found = options(label: str_or_rx) if @found.empty? - @found.empty? - end - @found + option(key => val).wait_until(&:exists?) rescue Wait::TimeoutError raise_no_value_found(str_or_rx) end + def find_options(key, str_or_rx) + val = type_check(str_or_rx) + + options(key => val).wait_until(&:exists?) + rescue Wait::TimeoutError + raise_no_value_found(str_or_rx) + end + + def type_check(str_or_rx) + str_or_rx = str_or_rx.to_s if str_or_rx.is_a?(Numeric) + return str_or_rx if [String, Regexp].any? { |k| str_or_rx.is_a?(k) } + + raise TypeError, "expected String, Numeric or Regexp, got #{str_or_rx.inspect}:#{str_or_rx.class}" + end + # TODO: Consider locating the Select List before throwing the exception def raise_no_value_found(str_or_rx) raise NoValueFoundException, "#{str_or_rx.inspect} not found in #{inspect}" end def select_matching(elements) - elements = [elements.first] unless multiple? elements.each { |e| e.click unless e.selected? } elements.first.exists? ? elements.first.text : '' end end # Select