module Webrat class Field def self.class_for_element(element) if element.name == "input" if %w[submit image].include?(element["type"]) field_class = "button" else field_class = element["type"] || "text" end else field_class = element.name end Webrat.const_get("#{field_class.capitalize}Field") rescue NameError raise "Invalid field element: #{element.inspect}" end def initialize(form, element) @form = form @element = element @value = default_value end def label_text return nil if labels.empty? labels.first.text end def matches_id?(id) @element["id"] == id.to_s end def matches_name?(name) @element["name"] == name.to_s end def matches_label?(label_text) return false if labels.empty? labels.any? { |label| label.matches_text?(label_text) } end def matches_alt?(alt) @element["alt"] =~ /^\W*#{Regexp.escape(alt.to_s)}/i end def to_param value = @value.to_s.gsub('&', '%26') param_parser.parse_query_parameters("#{name}=#{value}") end def set(value) @value = value end def unset @value = default_value end protected def id @element["id"] end def name @element["name"] end def labels @labels ||= label_elements.map { |element| Label.new(self, element) } end def label_elements return @label_elements unless @label_elements.nil? @label_elements = [] parent = @element.parent while parent.respond_to?(:parent) if parent.name == 'label' @label_elements.push parent break end parent = parent.parent end unless id.blank? @label_elements += @form.element / "label[@for=#{id}]" end @label_elements end def default_value @element["value"] end def param_parser if defined?(CGIMethods) CGIMethods else require "action_controller" require "action_controller/integration" ActionController::AbstractRequest end end def replace_param_value(params, oval, nval) output = Hash.new params.each do |key, value| case value when Hash value = replace_param_value(value, oval, nval) when Array value = value.map { |o| o == oval ? nval : oval } when oval value = nval end output[key] = value end output end end class ButtonField < Field def matches_text?(text) @element.innerHTML =~ /#{Regexp.escape(text.to_s)}/i end def matches_value?(value) @element["value"] =~ /^\W*#{Regexp.escape(value.to_s)}/i || matches_text?(value) || matches_alt?(value) end def to_param return nil if @value.nil? super end def default_value nil end def click set(@element["value"]) unless @element["name"].blank? @form.submit end end class HiddenField < Field def to_param if collection_name? super else checkbox_with_same_name = @form.find_field(name, CheckboxField) if checkbox_with_same_name.to_param.nil? super else nil end end end protected def collection_name? name =~ /\[\]/ end end class CheckboxField < Field def to_param return nil if @value.nil? super end def check set(@element["value"] || "on") end def uncheck set(nil) end protected def default_value if @element["checked"] == "checked" @element["value"] || "on" else nil end end end class PasswordField < Field end class RadioField < Field def to_param return nil if @value.nil? super end def choose other_options.each do |option| option.set(nil) end set(@element["value"] || "on") end protected def other_options @form.fields.select { |f| f.name == name } end def default_value if @element["checked"] == "checked" @element["value"] || "on" else nil end end end class TextareaField < Field protected def default_value @element.inner_html end end class FileField < Field attr_accessor :content_type def set(value, content_type = nil) super(value) @content_type = content_type end def to_param if @value.nil? super else replace_param_value(super, @value, test_uploaded_file) end end protected def test_uploaded_file if content_type ActionController::TestUploadedFile.new(@value, content_type) else ActionController::TestUploadedFile.new(@value) end end end class TextField < Field end class ResetField < Field end class SelectField < Field def find_option(text) options.detect { |o| o.matches_text?(text) } end protected def default_value selected_options = @element / "option[@selected='selected']" selected_options = @element / "option:first" if selected_options.empty? selected_options.map do |option| return "" if option.nil? option["value"] || option.innerHTML end end def options option_elements.map { |oe| SelectOption.new(self, oe) } end def option_elements (@element / "option") end end end