webrat/lib/webrat/core/field.rb

345 lines
6.7 KiB
Ruby

require "cgi"
require "webrat/core_extensions/blank"
require "webrat/core_extensions/nil_to_param"
module Webrat
class Field #:nodoc:
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" #default type; 'type' attribute is not mandatory
end
else
field_class = element.name
end
Webrat.const_get("#{field_class.capitalize}Field")
rescue NameError
raise "Invalid field element: #{element.inspect}"
end
attr_reader :value
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 id
@element["id"]
end
def matches_id?(id)
if id.is_a?(Regexp)
@element["id"] =~ id
else
@element["id"] == id.to_s
end
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 disabled?
@element.attributes.has_key?("disabled") && @element["disabled"] != 'false'
end
def raise_error_if_disabled
raise "Cannot interact with disabled form element (#{self})" if disabled?
end
def to_param
return nil if disabled?
key_and_value = "#{name}=#{escaped_value}"
if defined?(CGIMethods)
CGIMethods.parse_query_parameters(key_and_value)
elsif defined?(ActionController::AbstractRequest)
ActionController::AbstractRequest.parse_query_parameters(key_and_value)
elsif defined?(::Merb)
::Merb::Parse.query(key_and_value)
else
{ name => escaped_value } # For mechanize
end
end
def set(value)
@value = value
end
def unset
@value = default_value
end
protected
def name
@element["name"]
end
def escaped_value
CGI.escape(@value.to_s)
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 += Webrat::XML.css_search(@form.element, "label[@for='#{id}']")
end
@label_elements
end
def default_value
@element["value"]
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 #:nodoc:
def matches_text?(text)
@element.inner_html =~ /#{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
raise_error_if_disabled
set(@element["value"]) unless @element["name"].blank?
@form.submit
end
end
class HiddenField < Field #:nodoc:
def to_param
if collection_name?
super
else
checkbox_with_same_name = @form.field(name, CheckboxField)
if checkbox_with_same_name.to_param.blank?
super
else
nil
end
end
end
protected
def collection_name?
name =~ /\[\]/
end
end
class CheckboxField < Field #:nodoc:
def to_param
return nil if @value.nil?
super
end
def check
raise_error_if_disabled
set(@element["value"] || "on")
end
def checked?
@element["checked"] == "checked"
end
def uncheck
raise_error_if_disabled
set(nil)
end
protected
def default_value
if @element["checked"] == "checked"
@element["value"] || "on"
else
nil
end
end
end
class PasswordField < Field #:nodoc:
end
class RadioField < Field #:nodoc:
def to_param
return nil if @value.nil?
super
end
def choose
raise_error_if_disabled
other_options.each do |option|
option.set(nil)
end
set(@element["value"] || "on")
end
def checked?
@element["checked"] == "checked"
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 #:nodoc:
protected
def default_value
@element.inner_html
end
end
class FileField < Field #:nodoc:
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 #:nodoc:
end
class ResetField < Field #:nodoc:
end
class SelectField < Field #:nodoc:
def find_option(text)
options.detect { |o| o.matches_text?(text) }
end
protected
def default_value
selected_options = Webrat::XML.css_search(@element, "option[@selected='selected']")
selected_options = Webrat::XML.css_search(@element, "option:first") if selected_options.empty?
selected_options.map do |option|
return "" if option.nil?
option["value"] || option.inner_html
end
end
def options
option_elements.map { |oe| SelectOption.new(self, oe) }
end
def option_elements
Webrat::XML.css_search(@element, "option")
end
end
end