From f38ed1570471be5fc7fba9dec89434790a8760cf Mon Sep 17 00:00:00 2001 From: Bryan Helmkamp Date: Mon, 7 Apr 2008 05:52:49 +0100 Subject: [PATCH] More work on OOP implementation --- lib/webrat.rb | 4 +- lib/webrat/button_field.rb | 25 +++++ lib/webrat/button_finder.rb | 30 ------ lib/webrat/checkbox_field.rb | 30 ++++++ lib/webrat/field.rb | 200 +++++++++++++++++++++++++++++++++++ lib/webrat/field_finder.rb | 82 -------------- lib/webrat/form.rb | 148 +++++++++++++------------- lib/webrat/hidden_field.rb | 27 +++++ lib/webrat/label.rb | 18 ++++ lib/webrat/link.rb | 63 +++++++++-- lib/webrat/link_finder.rb | 32 ------ lib/webrat/page.rb | 159 +++++++++++++--------------- lib/webrat/password_field.rb | 6 ++ lib/webrat/radio_field.rb | 17 +++ lib/webrat/reset_field.rb | 6 ++ lib/webrat/select_field.rb | 29 +++++ lib/webrat/select_option.rb | 25 +++++ lib/webrat/text_field.rb | 6 ++ lib/webrat/textarea_field.rb | 13 +++ test/checks_test.rb | 25 +++-- test/chooses_test.rb | 12 ++- test/clicks_button_test.rb | 18 ++-- test/clicks_link_test.rb | 22 ++-- test/fills_in_test.rb | 2 +- test/selects_test.rb | 22 ++-- 25 files changed, 667 insertions(+), 354 deletions(-) create mode 100644 lib/webrat/button_field.rb delete mode 100644 lib/webrat/button_finder.rb create mode 100644 lib/webrat/checkbox_field.rb create mode 100644 lib/webrat/field.rb delete mode 100644 lib/webrat/field_finder.rb create mode 100644 lib/webrat/hidden_field.rb create mode 100644 lib/webrat/label.rb delete mode 100644 lib/webrat/link_finder.rb create mode 100644 lib/webrat/password_field.rb create mode 100644 lib/webrat/radio_field.rb create mode 100644 lib/webrat/reset_field.rb create mode 100644 lib/webrat/select_field.rb create mode 100644 lib/webrat/select_option.rb create mode 100644 lib/webrat/text_field.rb create mode 100644 lib/webrat/textarea_field.rb diff --git a/lib/webrat.rb b/lib/webrat.rb index 8bad61b..41e9eaf 100644 --- a/lib/webrat.rb +++ b/lib/webrat.rb @@ -27,7 +27,7 @@ module ActionController @current_page = Webrat::Page.new(self, *args) end - [:fills_in, :clicks_button, :selects, :chooses, :checks, :unchecks].each do |method_name| + [:reloads, :fills_in, :clicks_button, :selects, :chooses, :checks, :unchecks, :clicks_link, :clicks_put_link, :clicks_get_link, :clicks_post_link, :clicks_delete_link].each do |method_name| define_method(method_name) do |*args| current_page.send(method_name, *args) end @@ -37,5 +37,3 @@ module ActionController end end - - diff --git a/lib/webrat/button_field.rb b/lib/webrat/button_field.rb new file mode 100644 index 0000000..cb2ac78 --- /dev/null +++ b/lib/webrat/button_field.rb @@ -0,0 +1,25 @@ +require File.expand_path(File.join(File.dirname(__FILE__), "field")) + +module Webrat + class ButtonField < Field + + def matches_value?(value) + @element["value"] =~ /^\W*#{value}\b/i + 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 +end \ No newline at end of file diff --git a/lib/webrat/button_finder.rb b/lib/webrat/button_finder.rb deleted file mode 100644 index d0f1da0..0000000 --- a/lib/webrat/button_finder.rb +++ /dev/null @@ -1,30 +0,0 @@ -require File.expand_path(File.join(File.dirname(__FILE__), "field_finder")) - -module Webrat - class ButtonFinder < FieldFinder - def initialize(root, name = nil) - @root = root - @id_or_name_or_label = name - @element_types = %w[input] - @input_types = %w[submit image] - - @candidates = nil - end - - def find - if @id_or_name_or_label.nil? - candidates.first - else - find_by_id(@id_or_name_or_label) || - find_by_name || - find_by_value - end - end - - protected - - def find_by_value - candidates.detect { |el| el.attributes["value"] == @id_or_name_or_label } - end - end -end \ No newline at end of file diff --git a/lib/webrat/checkbox_field.rb b/lib/webrat/checkbox_field.rb new file mode 100644 index 0000000..8ef526c --- /dev/null +++ b/lib/webrat/checkbox_field.rb @@ -0,0 +1,30 @@ +require File.expand_path(File.join(File.dirname(__FILE__), "field")) + +module Webrat + 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 +end \ No newline at end of file diff --git a/lib/webrat/field.rb b/lib/webrat/field.rb new file mode 100644 index 0000000..c913069 --- /dev/null +++ b/lib/webrat/field.rb @@ -0,0 +1,200 @@ +module Webrat + class Field + + def self.class_for_element(element) + case element.name + when "select" + SelectField + when "textarea" + TextareaField + when "input" + case element["type"] + when "checkbox" + CheckboxField + when "radio" + RadioField + when "text" + TextField + when "hidden" + HiddenField + when "password" + PasswordField + when "reset" + ResetField + when "submit" + ButtonField + when "image" + ButtonField + else + raise "Invalid field element: #{element.inspect}" + end + else + raise "Invalid field element: #{element.inspect}" + end + end + + def initialize(form, element) + @form = form + @element = element + + @value = default_value + end + + def label_text + return nil unless label + label.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 unless label + label.matches_text?(label_text) + end + + def to_param + 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 label + return nil if label_element.nil? + @label ||= Label.new(self, label_element) + end + + def label_element + @label_element ||= begin + parent = @element.parent + while parent.respond_to?(:parent) + return parent if parent.name == "label" + parent = parent.parent + end + + if id.blank? + nil + else + @form.element.at("label[@for=#{id}]") + end + end + end + + def default_value + @element["value"] + end + + def param_parser + if defined?(CGIMethods) + CGIMethods + else + ActionController::AbstractRequest + end + end + + end +end + +# module Webrat +# class FieldFinder +# def initialize(root, id_or_name_or_label, element_types, input_types = nil) +# @root = root +# @id_or_name_or_label = id_or_name_or_label.to_s +# @element_types = Array(element_types) +# @input_types = Array(input_types) +# +# @candidates = nil +# end +# +# def find +# find_by_id(@id_or_name_or_label) || +# find_by_name || +# find_by_label +# end +# +# protected +# +# # def find_select_list_by_name_or_label(name_or_label) # :nodoc: +# # select = find_element_by_name("select", name_or_label) +# # return select if select +# # +# # label = find_form_label(name_or_label) +# # label ? field_for_label(label) : nil +# # end +# # +# # def find_option_by_value(option_value, select=nil) # :nodoc: +# # options = select.nil? ? (dom / "option") : (select / "option") +# # options.detect { |el| el.innerHTML == option_value } +# # end +# +# def field_for_label(label) +# inputs_within_label = canidates_within(label) +# +# if inputs_within_label.any? +# inputs_within_label.first +# else +# find_by_id(label["for"]) +# end +# end +# +# def find_by_id(id) +# candidates.detect { |el| el["id"] == id } +# end +# +# def find_by_name +# candidates.detect { |el| el["name"] == @id_or_name_or_label } +# end +# +# def find_by_label +# label = canididate_labels.sort_by { |el| el.innerText.strip.size }.first +# label ? field_for_label(label) : nil +# end +# +# def canididate_labels +# (@root / "label").select { |el| el.innerText =~ /^\W*#{Regexp.escape(@id_or_name_or_label)}\b/i } +# end +# +# def candidates +# return @candidates if @candidates +# @candidates = canidates_within(@root) +# end +# +# def canidates_within(root) +# candidates = [] +# +# @element_types.each do |element_type| +# if "input" == element_type && @input_types.any? +# @input_types.each do |input_type| +# candidates += (root / "input[@type=#{input_type}]") +# end +# else +# candidates += (root / element_type) +# end +# end +# +# candidates +# end +# +# end +# end \ No newline at end of file diff --git a/lib/webrat/field_finder.rb b/lib/webrat/field_finder.rb deleted file mode 100644 index d2c01c3..0000000 --- a/lib/webrat/field_finder.rb +++ /dev/null @@ -1,82 +0,0 @@ -module Webrat - class FieldFinder - def initialize(root, id_or_name_or_label, element_types, input_types = nil) - @root = root - @id_or_name_or_label = id_or_name_or_label.to_s - @element_types = Array(element_types) - @input_types = Array(input_types) - - @candidates = nil - end - - def find - find_by_id(@id_or_name_or_label) || - find_by_name || - find_by_label - end - - protected - - # def find_select_list_by_name_or_label(name_or_label) # :nodoc: - # select = find_element_by_name("select", name_or_label) - # return select if select - # - # label = find_form_label(name_or_label) - # label ? field_for_label(label) : nil - # end - # - # def find_option_by_value(option_value, select=nil) # :nodoc: - # options = select.nil? ? (dom / "option") : (select / "option") - # options.detect { |el| el.innerHTML == option_value } - # end - - def field_for_label(label) - inputs_within_label = canidates_within(label) - - if inputs_within_label.any? - inputs_within_label.first - else - find_by_id(label.attributes["for"]) - end - end - - def find_by_id(id) - candidates.detect { |el| el.attributes["id"] == id } - end - - def find_by_name - candidates.detect { |el| el.attributes["name"] == @id_or_name_or_label } - end - - def find_by_label - label = canididate_labels.sort_by { |el| el.innerText.strip.size }.first - label ? field_for_label(label) : nil - end - - def canididate_labels - (@root / "label").select { |el| el.innerText =~ /^\W*#{Regexp.escape(@id_or_name_or_label)}\b/i } - end - - def candidates - return @candidates if @candidates - @candidates = canidates_within(@root) - end - - def canidates_within(root) - candidates = [] - - @element_types.each do |element_type| - if "input" == element_type && @input_types.any? - @input_types.each do |input_type| - candidates += (root / "input[@type=#{input_type}]") - end - else - candidates += (root / element_type) - end - end - - candidates - end - - end -end \ No newline at end of file diff --git a/lib/webrat/form.rb b/lib/webrat/form.rb index 2873f9f..19b5bc1 100644 --- a/lib/webrat/form.rb +++ b/lib/webrat/form.rb @@ -1,110 +1,110 @@ module Webrat class Form - def initialize(page, form) - @page = page - @form = form - @params = default_params - end + attr_reader :element - def set(input_element, value) - new_param = param_parser.parse_query_parameters("#{input_element.attributes["name"]}=#{value}") - merge(new_param) - end - - def unset(input_element) - @params.delete(input_element.attributes['name']) + def initialize(page, element) + @page = page + @element = element + @fields = nil end - def find_field(*args) - FieldFinder.new(@form, *args).find + def find_field(id_or_name_or_label, *field_types) + possible_fields = fields_by_type(field_types) + + find_field_by_id(possible_fields, id_or_name_or_label) || + find_field_by_name(possible_fields, id_or_name_or_label) || + find_field_by_label(possible_fields, id_or_name_or_label) || + nil end - - def find_button(name = nil) - ButtonFinder.new(@form, name).find + + def find_button(value = nil) + return fields_by_type([ButtonField]).first if value.nil? + + possible_buttons = fields_by_type([ButtonField]) + + possible_buttons.each do |possible_button| + return possible_button if possible_button.matches_value?(value) + end + + nil end def submit - Page.new(@page.session, form_action, form_method, @params) + Page.new(@page.session, form_action, form_method, params) end protected - - # def find_field_by_name(name) # :nodoc: - # find_element_by_name("input", name) || find_element_by_name("textarea", name) - # end - # - # def add_default_params_for(form) # :nodoc: - # add_default_params_from_inputs_for(form) - # add_default_params_from_checkboxes_for(form) - # add_default_params_from_radio_buttons_for(form) - # add_default_params_from_textareas_for(form) - # add_default_params_from_selects_for(form) - # end - def default_params - {} + def find_field_by_id(possible_fields, id) + possible_fields.each do |possible_field| + return possible_field if possible_field.matches_id?(id) + end + + nil + end + + def find_field_by_name(possible_fields, name) + possible_fields.each do |possible_field| + return possible_field if possible_field.matches_name?(name) + end + + nil + end + + def find_field_by_label(possible_fields, label) + matching_fields = [] + + possible_fields.each do |possible_field| + matching_fields << possible_field if possible_field.matches_label?(label) + end + + matching_fields.sort_by { |f| f.label_text.length }.first end - def add_default_params_from_radio_buttons_for(form) # :nodoc: - (form / "input[@type='radio][@checked='checked']").each do |input| - add_form_data(input, input.attributes["value"]) - end + def fields_by_type(field_types) + fields.select { |f| field_types.include?(f.class) } end - def add_default_params_from_checkboxes_for(form) # :nodoc: - (form / "input[@type='checkbox][@checked='checked']").each do |input| - add_form_data(input, input.attributes["value"] || "on") + def fields + return @fields if @fields + + @fields = [] + + (@element / "input, textarea, select").each do |field_element| + @fields << Field.class_for_element(field_element).new(self, field_element) end + + @fields end - def add_default_params_from_selects_for(form) # :nodoc: - (form / "select").each do |select| - selected_options = select / "option[@selected='selected']" - selected_options = select / "option:first" if selected_options.empty? - selected_options.each do |option| - add_form_data(select, option.attributes["value"] || option.innerHTML) - end + def params + all_params = {} + + fields.each do |field| + next if field.to_param.nil? + merge(all_params, field.to_param) end + + all_params end - def add_default_params_from_inputs_for(form) # :nodoc: - (form / "input").each do |input| - next unless %w[text password hidden].include?(input.attributes["type"]) - add_form_data(input, input.attributes["value"]) - end - end - - def add_default_params_from_textareas_for(form) # :nodoc: - (form / "textarea").each do |input| - add_form_data(input, input.inner_html) - end - end - def form_method - @form.attributes["method"].blank? ? :get : @form.attributes["method"].downcase + @element["method"].blank? ? :get : @element["method"].downcase end def form_action - @form.attributes["action"].blank? ? current_url : @form.attributes["action"] + @element["action"].blank? ? @page.url : @element["action"] end - def param_parser - if defined?(CGIMethods) - CGIMethods - else - ActionController::AbstractRequest - end - end - - def merge(new_param) + def merge(all_params, new_param) new_param.each do |key, value| - case @params[key] + case all_params[key] when Hash, HashWithIndifferentAccess - merge_hash_values(@params[key], value) + merge_hash_values(all_params[key], value) when Array - @params[key] += value + all_params[key] += value else - @params[key] = value + all_params[key] = value end end end diff --git a/lib/webrat/hidden_field.rb b/lib/webrat/hidden_field.rb new file mode 100644 index 0000000..dab8472 --- /dev/null +++ b/lib/webrat/hidden_field.rb @@ -0,0 +1,27 @@ +require File.expand_path(File.join(File.dirname(__FILE__), "field")) + +module Webrat + 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 +end \ No newline at end of file diff --git a/lib/webrat/label.rb b/lib/webrat/label.rb new file mode 100644 index 0000000..9bf15b7 --- /dev/null +++ b/lib/webrat/label.rb @@ -0,0 +1,18 @@ +module Webrat + class Label + + def initialize(field, element) + @field = field + @element = element + end + + def matches_text?(label_text) + text =~ /^\W*#{Regexp.escape(label_text.to_s)}\b/i + end + + def text + @element.innerText + end + + end +end \ No newline at end of file diff --git a/lib/webrat/link.rb b/lib/webrat/link.rb index 14bb552..3960de2 100644 --- a/lib/webrat/link.rb +++ b/lib/webrat/link.rb @@ -1,14 +1,32 @@ module Webrat class Link - def initialize(link_element) + def initialize(page, element) + @page = page + @element = element end - def click + def click(method = nil) + method ||= http_method + return if href =~ /^#/ && method == :get + + Page.new(@page.session, href, method, authenticity_token.blank? ? {} : {"authenticity_token" => authenticity_token}) + end + + def matches_text?(link_text) + text =~ /#{Regexp.escape(link_text.to_s)}/i + end + + def text + @element.innerHTML end protected - + + def href + @element["href"] + end + def authenticity_token return unless onclick && onclick.include?("s.setAttribute('name', 'authenticity_token');") && onclick =~ /s\.setAttribute\('value', '([a-f0-9]{40})'\);/ @@ -16,28 +34,26 @@ module Webrat end def onclick + @element["onclick"] end def http_method - end - - def http_method_from_js(onclick) if !onclick.blank? && onclick.include?("f.submit()") - http_method_from_js_form(onclick) + http_method_from_js_form else :get end end - def http_method_from_js_form(onclick) + def http_method_from_js_form if onclick.include?("m.setAttribute('name', '_method')") - http_method_from_fake_method_param(onclick) + http_method_from_fake_method_param else :post end end - def http_method_from_fake_method_param(onclick) + def http_method_from_fake_method_param if onclick.include?("m.setAttribute('value', 'delete')") :delete elsif onclick.include?("m.setAttribute('value', 'put')") @@ -48,4 +64,29 @@ module Webrat end end -end \ No newline at end of file +end + +# def clicks_link_with_method(link_text, http_method) # :nodoc: +# link = all_links.detect { |el| el.innerHTML =~ /#{link_text}/i } +# flunk("No link with text #{link_text.inspect} was found") if link.nil? +# request_page(http_method, link["href"]) +# end +# +# def find_shortest_matching_link(links, link_text) +# candidates = links.select { |el| el.innerHTML =~ /#{link_text}/i } +# candidates.sort_by { |el| el.innerText.strip.size }.first +# end +# +# def clicks_one_link_of(links, link_text) +# link = find_shortest_matching_link(links, link_text) +# +# flunk("No link with text #{link_text.inspect} was found") if link.nil? +# +# onclick = link["onclick"] +# href = link["href"] +# +# http_method = http_method_from_js(onclick) +# authenticity_token = authenticity_token_value(onclick) +# +# request_page(http_method, href, authenticity_token.blank? ? {} : {"authenticity_token" => authenticity_token}) +# end \ No newline at end of file diff --git a/lib/webrat/link_finder.rb b/lib/webrat/link_finder.rb deleted file mode 100644 index 14d7ea7..0000000 --- a/lib/webrat/link_finder.rb +++ /dev/null @@ -1,32 +0,0 @@ -# def clicks_link_with_method(link_text, http_method) # :nodoc: -# link = all_links.detect { |el| el.innerHTML =~ /#{link_text}/i } -# flunk("No link with text #{link_text.inspect} was found") if link.nil? -# request_page(http_method, link.attributes["href"]) -# end -# -# def find_shortest_matching_link(links, link_text) -# candidates = links.select { |el| el.innerHTML =~ /#{link_text}/i } -# candidates.sort_by { |el| el.innerText.strip.size }.first -# end -# -# def clicks_one_link_of(links, link_text) -# link = find_shortest_matching_link(links, link_text) -# -# flunk("No link with text #{link_text.inspect} was found") if link.nil? -# -# onclick = link.attributes["onclick"] -# href = link.attributes["href"] -# -# http_method = http_method_from_js(onclick) -# authenticity_token = authenticity_token_value(onclick) -# -# request_page(http_method, href, authenticity_token.blank? ? {} : {"authenticity_token" => authenticity_token}) unless href =~ /^#/ && http_method == :get -# end -# -# def all_links # :nodoc: -# (dom / "a[@href]") -# end -# -# def links_within(selector) # :nodoc: -# (dom / selector / "a[@href]") -# end \ No newline at end of file diff --git a/lib/webrat/page.rb b/lib/webrat/page.rb index c4aea1b..ef83fc9 100644 --- a/lib/webrat/page.rb +++ b/lib/webrat/page.rb @@ -6,6 +6,7 @@ module Webrat include Logging attr_reader :session + attr_reader :url def initialize(session, url = nil, method = :get, data = {}) @session = session @@ -27,25 +28,10 @@ module Webrat # The field value is required, and must be specified in options[:with]. # field can be either the value of a name attribute (i.e. user[email]) # or the text inside a element that points at the field. - def fills_in(field, options = {}) - value = options[:with] - flunk("No value was provided") if value.nil? - - form_with_input = nil - found_input = nil - - forms.each do |form| - found_input = form.find_field(field, %w[input textarea], %w[text password]) - - if found_input - form_with_input = form - break - end - end - - flunk("Could not find input #{field.inspect}") if found_input.nil? - - form_with_input.set(found_input, value) + def fills_in(id_or_name_or_label, options = {}) + field = find_field(id_or_name_or_label, TextField, TextareaField) + flunk("Could not find text or password input or textarea #{id_or_name_or_label.inspect}") if field.nil? + field.set(options[:with]) end # Verifies that an input checkbox exists on the current page and marks it @@ -53,11 +39,10 @@ module Webrat # # Example: # checks 'Remember Me' - def checks(field) - checkbox = find_field_by_name_or_label(field) - flunk("Could not find checkbox #{field.inspect}") if checkbox.nil? - flunk("Input #{checkbox.inspect} is not a checkbox") unless checkbox.attributes['type'] == 'checkbox' - add_form_data(checkbox, checkbox.attributes["value"] || "on") + def checks(id_or_name_or_label) + field = find_field(id_or_name_or_label, CheckboxField) + flunk("Could not find checkbox #{id_or_name_or_label.inspect}") if field.nil? + field.check end # Verifies that an input checkbox exists on the current page and marks it @@ -65,16 +50,10 @@ module Webrat # # Example: # unchecks 'Remember Me' - def unchecks(field) - checkbox = find_field_by_name_or_label(field) - flunk("Could not find checkbox #{field.inspect}") if checkbox.nil? - flunk("Input #{checkbox.inspect} is not a checkbox") unless checkbox.attributes['type'] == 'checkbox' - remove_form_data(checkbox) - - (form_for_node(checkbox) / "input").each do |input| - next unless input.attributes["type"] == "hidden" && input.attributes["name"] == checkbox.attributes["name"] - add_form_data(input, input.attributes["value"]) - end + def unchecks(id_or_name_or_label) + field = find_field(id_or_name_or_label, CheckboxField) + flunk("Could not find checkbox #{id_or_name_or_label.inspect}") if field.nil? + field.uncheck end # Verifies that an input radio button exists on the current page and marks it @@ -85,8 +64,8 @@ module Webrat def chooses(field) radio = find_field_by_name_or_label(field) flunk("Could not find radio button #{field.inspect}") if radio.nil? - flunk("Input #{radio.inspect} is not a radio button") unless radio.attributes['type'] == 'radio' - add_form_data(radio, radio.attributes["value"] || "on") + flunk("Input #{radio.inspect} is not a radio button") unless radio['type'] == 'radio' + add_form_data(radio, radio["value"] || "on") end # Verifies that a an option element exists on the current page with the specified @@ -99,18 +78,12 @@ module Webrat # selects "February", :from => "event_month" # selects "February", :from => "Event Month" def selects(option_text, options = {}) - if options[:from] - select = find_select_list_by_name_or_label(options[:from]) - flunk("Could not find select list #{options[:from].inspect}") if select.nil? - option_node = find_option_by_value(option_text, select) - flunk("Could not find option #{option_text.inspect}") if option_node.nil? - else - option_node = find_option_by_value(option_text) - flunk("Could not find option #{option_text.inspect}") if option_node.nil? - select = option_node.parent - end - add_form_data(select, option_node.attributes["value"] || option_node.innerHTML) - # TODO - Set current form + id_or_name_or_label = options[:from] + field = find_field(id_or_name_or_label, SelectField) + flunk("Could not find select #{id_or_name_or_label.inspect}") if field.nil? + option = field.find_option(option_text) + flunk("Could not find option #{option_text.inspect}") if option.nil? + option.choose end # Saves the currently loaded page out to RAILS_ROOT/tmp/ and opens it in the default @@ -138,23 +111,25 @@ module Webrat # Example: # clicks_link "Sign up" def clicks_link(link_text) - clicks_one_link_of(all_links, link_text) + link = find_link(link_text) + link.click end - # Works like clicks_link, but only looks for the link text within a given selector - # - # Example: - # clicks_link_within "#user_12", "Vote" - def clicks_link_within(selector, link_text) - clicks_one_link_of(links_within(selector), link_text) - end + # # Works like clicks_link, but only looks for the link text within a given selector + # # + # # Example: + # # clicks_link_within "#user_12", "Vote" + # def clicks_link_within(selector, link_text) + # clicks_one_link_of(links_within(selector), link_text) + # end # Works like clicks_link, but forces a GET request # # Example: # clicks_get_link "Log out" def clicks_get_link(link_text) - clicks_link_with_method(link_text, :get) + link = find_link(link_text) + link.click(:get) end # Works like clicks_link, but issues a DELETE request instead of a GET @@ -162,7 +137,8 @@ module Webrat # Example: # clicks_delete_link "Log out" def clicks_delete_link(link_text) - clicks_link_with_method(link_text, :delete) + link = find_link(link_text) + link.click(:delete) end # Works like clicks_link, but issues a POST request instead of a GET @@ -170,7 +146,8 @@ module Webrat # Example: # clicks_post_link "Vote" def clicks_post_link(link_text) - clicks_link_with_method(link_text, :post) + link = find_link(link_text) + link.click(:post) end # Works like clicks_link, but issues a PUT request instead of a GET @@ -178,7 +155,8 @@ module Webrat # Example: # clicks_put_link "Update profile" def clicks_put_link(link_text) - clicks_link_with_method(link_text, :put) + link = find_link(link_text) + link.click(:put) end # Verifies that a submit button exists for the form, then submits the form, follows @@ -191,21 +169,15 @@ module Webrat # The URL and HTTP method for the form submission are automatically read from the # action and method attributes of the
element. def clicks_button(value = nil) - form_with_button = nil - found_button = nil + button = nil forms.each do |form| - found_button = form.find_button(value) - - if found_button - form_with_button = form - break - end + button = form.find_button(value) + break if button end - flunk("Could not find button #{value.inspect}") if found_button.nil? - form_with_button.set(found_button, found_button.attributes["value"]) unless found_button.attributes["name"].blank? - form_with_button.submit + flunk("Could not find button #{value.inspect}") if button.nil? + button.click end # Reloads the last page requested. Note that this will resubmit forms @@ -221,7 +193,31 @@ module Webrat end protected - + + def find_link(text) + + matching_links = [] + + links.each do |possible_link| + matching_links << possible_link if possible_link.matches_text?(text) + end + + if matching_links.any? + matching_links.sort_by { |l| l.text.length }.first + else + flunk("Could not find link with text #{text.inspect}") + end + end + + def find_field(id_or_name_or_label, *field_types) + forms.each do |form| + result = form.find_field(id_or_name_or_label, *field_types) + return result if result + end + + nil + end + def request_page(url, method, data) debug_log "REQUESTING PAGE: #{method.to_s.upcase} #{url} with #{data.inspect}" @@ -241,21 +237,16 @@ module Webrat def reset_dom @dom = nil + @links = nil @forms = nil end - def find_button(value = nil) # :nodoc: - return nil unless value - - (dom / "input[@type='submit']").detect do |el| - el.attributes["value"] =~ /^\W*#{value}\b/i - end - end - - def form_for_node(node) # :nodoc: - return node if node.name == "form" - node = node.parent until node.name == "form" - node + def links + return @links if @links + + @links = (dom / "a[@href]").map do |link_element| + Link.new(self, link_element) + end end def forms diff --git a/lib/webrat/password_field.rb b/lib/webrat/password_field.rb new file mode 100644 index 0000000..c2b5a8f --- /dev/null +++ b/lib/webrat/password_field.rb @@ -0,0 +1,6 @@ +require File.expand_path(File.join(File.dirname(__FILE__), "field")) + +module Webrat + class PasswordField < Field + end +end \ No newline at end of file diff --git a/lib/webrat/radio_field.rb b/lib/webrat/radio_field.rb new file mode 100644 index 0000000..2ce48a2 --- /dev/null +++ b/lib/webrat/radio_field.rb @@ -0,0 +1,17 @@ +require File.expand_path(File.join(File.dirname(__FILE__), "field")) + +module Webrat + class RadioField < Field + + protected + + def default_value + if @element["checked"] == "checked" + @element["value"] + else + nil + end + end + + end +end \ No newline at end of file diff --git a/lib/webrat/reset_field.rb b/lib/webrat/reset_field.rb new file mode 100644 index 0000000..0237026 --- /dev/null +++ b/lib/webrat/reset_field.rb @@ -0,0 +1,6 @@ +require File.expand_path(File.join(File.dirname(__FILE__), "field")) + +module Webrat + class ResetField < Field + end +end \ No newline at end of file diff --git a/lib/webrat/select_field.rb b/lib/webrat/select_field.rb new file mode 100644 index 0000000..8aadbcd --- /dev/null +++ b/lib/webrat/select_field.rb @@ -0,0 +1,29 @@ +require File.expand_path(File.join(File.dirname(__FILE__), "field")) + +module Webrat + 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| + 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 \ No newline at end of file diff --git a/lib/webrat/select_option.rb b/lib/webrat/select_option.rb new file mode 100644 index 0000000..a60aca1 --- /dev/null +++ b/lib/webrat/select_option.rb @@ -0,0 +1,25 @@ +module Webrat + class SelectOption + + def initialize(select, element) + @select = select + @element = element + end + + def matches_text?(text) + # require "rubygems"; require "ruby-debug"; Debugger.start; debugger + @element.innerHTML =~ /^\W*#{Regexp.escape(text.to_s)}\b/i + end + + def choose + @select.set(value) + end + + protected + + def value + @element["value"] || @element.innerHTML + end + + end +end \ No newline at end of file diff --git a/lib/webrat/text_field.rb b/lib/webrat/text_field.rb new file mode 100644 index 0000000..441ebd0 --- /dev/null +++ b/lib/webrat/text_field.rb @@ -0,0 +1,6 @@ +require File.expand_path(File.join(File.dirname(__FILE__), "field")) + +module Webrat + class TextField < Field + end +end \ No newline at end of file diff --git a/lib/webrat/textarea_field.rb b/lib/webrat/textarea_field.rb new file mode 100644 index 0000000..1a66a47 --- /dev/null +++ b/lib/webrat/textarea_field.rb @@ -0,0 +1,13 @@ +require File.expand_path(File.join(File.dirname(__FILE__), "field")) + +module Webrat + class TextareaField < Field + + protected + + def default_value + @element.inner_html + end + + end +end \ No newline at end of file diff --git a/test/checks_test.rb b/test/checks_test.rb index 7fcd70f..7d3113b 100644 --- a/test/checks_test.rb +++ b/test/checks_test.rb @@ -13,8 +13,10 @@ class ChecksTest < Test::Unit::TestCase
EOS - @session.expects(:flunk) - @session.checks "remember_me" + + assert_raises RuntimeError do + @session.checks "remember_me" + end end def test_should_fail_if_input_is_not_a_checkbox @@ -23,8 +25,10 @@ class ChecksTest < Test::Unit::TestCase EOS - @session.expects(:flunk) - @session.checks "remember_me" + + assert_raises RuntimeError do + @session.checks "remember_me" + end end def test_should_check_rails_style_checkboxes @@ -79,8 +83,10 @@ class UnchecksTest < Test::Unit::TestCase
EOS - @session.expects(:flunk) - @session.unchecks "remember_me" + + assert_raises RuntimeError do + @session.unchecks "remember_me" + end end def test_should_fail_if_input_is_not_a_checkbox @@ -89,8 +95,10 @@ class UnchecksTest < Test::Unit::TestCase EOS - @session.expects(:flunk) - @session.unchecks "remember_me" + + assert_raises RuntimeError do + @session.unchecks "remember_me" + end end def test_should_uncheck_rails_style_checkboxes @@ -103,6 +111,7 @@ class UnchecksTest < Test::Unit::TestCase EOS @session.expects(:get_via_redirect).with("/login", "user" => {"tos" => "0"}) + @session.checks "TOS" @session.unchecks "TOS" @session.clicks_button end diff --git a/test/chooses_test.rb b/test/chooses_test.rb index 4357939..c81071c 100644 --- a/test/chooses_test.rb +++ b/test/chooses_test.rb @@ -14,8 +14,10 @@ class ChoosesTest < Test::Unit::TestCase
EOS - @session.expects(:flunk) - @session.chooses "first option" + + assert_raises RuntimeError do + @session.chooses "first option" + end end def test_should_fail_if_input_is_not_a_radio_button @@ -24,8 +26,10 @@ class ChoosesTest < Test::Unit::TestCase EOS - @session.expects(:flunk) - @session.chooses "first_option" + + assert_raises RuntimeError do + @session.chooses "first_option" + end end def test_should_check_rails_style_radio_buttons diff --git a/test/clicks_button_test.rb b/test/clicks_button_test.rb index c6bb5d4..35a576b 100644 --- a/test/clicks_button_test.rb +++ b/test/clicks_button_test.rb @@ -5,6 +5,8 @@ class ClicksButtonTest < Test::Unit::TestCase @session = ActionController::Integration::Session.new @session.stubs(:assert_response) @session.stubs(:get_via_redirect) + @page = Webrat::Page.new(@session) + @session.stubs(:current_page).returns(@page) @response = mock @session.stubs(:response).returns(@response) end @@ -13,8 +15,10 @@ class ClicksButtonTest < Test::Unit::TestCase @response.stubs(:body).returns(<<-EOS)
EOS - @session.expects(:flunk) - @session.clicks_button + + assert_raises RuntimeError do + @session.clicks_button + end end def test_should_fail_if_input_is_not_a_submit_button @@ -23,8 +27,10 @@ class ClicksButtonTest < Test::Unit::TestCase EOS - @session.expects(:flunk) - @session.clicks_button + + assert_raises RuntimeError do + @session.clicks_button + end end def test_should_default_to_get_method @@ -53,7 +59,7 @@ class ClicksButtonTest < Test::Unit::TestCase EOS - @session.expects(:current_url).returns("/current") + @page.stubs(:url).returns("/current") @session.expects(:get_via_redirect).with("/current", {}) @session.clicks_button end @@ -225,7 +231,7 @@ class ClicksButtonTest < Test::Unit::TestCase EOS @session.expects(:post_via_redirect).with("/login", - "options" => ["soda", "soda", "dessert", "burger", "fries"], + "options" => ["burger", "fries", "soda", "soda", "dessert"], "response" => { "choices" => [{"selected" => "one"}, {"selected" => "two"}, {"selected" => "two"}]}) @session.clicks_button end diff --git a/test/clicks_link_test.rb b/test/clicks_link_test.rb index b8c87f6..7b5007b 100644 --- a/test/clicks_link_test.rb +++ b/test/clicks_link_test.rb @@ -170,17 +170,17 @@ class ClicksLinkTest < Test::Unit::TestCase @session.clicks_link "Link" end - def test_should_click_link_within_a_selector - @response.stubs(:body).returns(<<-EOS) - Link -
- Link -
- EOS - - @session.expects(:get_via_redirect).with("/page2", {}) - @session.clicks_link_within "#container", "Link" - end + # def test_should_click_link_within_a_selector + # @response.stubs(:body).returns(<<-EOS) + # Link + #
+ # Link + #
+ # EOS + # + # @session.expects(:get_via_redirect).with("/page2", {}) + # @session.clicks_link_within "#container", "Link" + # end def test_should_not_make_request_when_link_is_local_anchor @response.stubs(:body).returns(<<-EOS) diff --git a/test/fills_in_test.rb b/test/fills_in_test.rb index 3b4c01a..842e61b 100644 --- a/test/fills_in_test.rb +++ b/test/fills_in_test.rb @@ -45,7 +45,7 @@ class FillsInTest < Test::Unit::TestCase @session.clicks_button end - def test_should_choose_the_closest_label_match + def test_should_choose_the_shortest_label_match @response.stubs(:body).returns(<<-EOS)
diff --git a/test/selects_test.rb b/test/selects_test.rb index 93e1bd2..23bca17 100644 --- a/test/selects_test.rb +++ b/test/selects_test.rb @@ -14,8 +14,10 @@ class SelectsTest < Test::Unit::TestCase
EOS - @session.expects(:flunk) - @session.selects "February" + + assert_raises RuntimeError do + @session.selects "February", :from => "month" + end end def test_should_fail_if_option_not_found_in_list_specified_by_element_name @@ -25,8 +27,10 @@ class SelectsTest < Test::Unit::TestCase EOS - @session.expects(:flunk) - @session.selects "February", :from => "year" + + assert_raises RuntimeError do + @session.selects "February", :from => "year" + end end def test_should_fail_if_specified_list_not_found @@ -35,8 +39,10 @@ class SelectsTest < Test::Unit::TestCase EOS - @session.expects(:flunk) - @session.selects "February", :from => "year" + + assert_raises RuntimeError do + @session.selects "February", :from => "year" + end end def test_should_send_value_from_option @@ -47,7 +53,7 @@ class SelectsTest < Test::Unit::TestCase EOS @session.expects(:post_via_redirect).with("/login", "month" => "1") - @session.selects "January" + @session.selects "January", :from => "month" @session.clicks_button end @@ -87,7 +93,7 @@ class SelectsTest < Test::Unit::TestCase EOS @session.expects(:post_via_redirect).with("/login", "month" => "January") - @session.selects "January" + @session.selects "January", :from => "month" @session.clicks_button end end