diff --git a/.gitignore b/.gitignore index 8893ac41..e51eeaff 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ email.txt log .project .loadpath +*.swp diff --git a/History.txt b/History.txt index 5d7853b8..18990222 100644 --- a/History.txt +++ b/History.txt @@ -1,5 +1,9 @@ == Trunk +* Major enhancements + + * Added selects_time, selects_date, and selects_datetime to API. (Ben Mabey, Ticket #36) + * Minor enhancements * Allow clicking links by id and id regexp (gaffo) diff --git a/lib/webrat/core/field.rb b/lib/webrat/core/field.rb index 7aeae828..b6236cb1 100644 --- a/lib/webrat/core/field.rb +++ b/lib/webrat/core/field.rb @@ -34,8 +34,16 @@ def label_text labels.first.text end + def id + @element["id"] + end + def matches_id?(id) - @element["id"] == id.to_s + if id.is_a?(Regexp) + @element["id"] =~ id + else + @element["id"] == id.to_s + end end def matches_name?(name) @@ -83,10 +91,6 @@ def unset protected - def id - @element["id"] - end - def name @element["name"] end diff --git a/lib/webrat/core/form.rb b/lib/webrat/core/form.rb index b07247fe..c2eb168f 100644 --- a/lib/webrat/core/form.rb +++ b/lib/webrat/core/form.rb @@ -44,6 +44,10 @@ def fields end end + def labels + @labels ||= element.search("label").map { |element| Label.new(nil, element) } + end + def submit @session.request_page(form_action, form_method, params) end @@ -65,6 +69,10 @@ def field_labeled(label, *field_types) end matching_fields.min { |a, b| a.label_text.length <=> b.label_text.length } end + + def label_matching(label_text) + labels.detect { |label| label.matches_text?(label_text) } + end protected @@ -127,4 +135,4 @@ def merge_hash_values(a, b) # :nodoc: end end -end \ No newline at end of file +end diff --git a/lib/webrat/core/label.rb b/lib/webrat/core/label.rb index 7107f10a..5557108c 100644 --- a/lib/webrat/core/label.rb +++ b/lib/webrat/core/label.rb @@ -17,6 +17,10 @@ def text str.squeeze!(" ") str end + + def for_id + @element['for'] + end end -end \ No newline at end of file +end diff --git a/lib/webrat/core/locators.rb b/lib/webrat/core/locators.rb index 0a7c108b..72cff9ba 100644 --- a/lib/webrat/core/locators.rb +++ b/lib/webrat/core/locators.rb @@ -87,6 +87,15 @@ def find_link(text_or_title_or_id) #:nodoc: flunk("Could not find link with text or title or id #{text_or_title_or_id.inspect}") end end + + def find_field_id_for_label(label_text) + label = forms.detect_mapped { |form| form.label_matching(label_text) } + if label + label.for_id + else + flunk("Could not find the label with text #{label_text}") + end + end end -end \ No newline at end of file +end diff --git a/lib/webrat/core/scope.rb b/lib/webrat/core/scope.rb index a9fb7256..3edf5d0f 100644 --- a/lib/webrat/core/scope.rb +++ b/lib/webrat/core/scope.rb @@ -97,6 +97,114 @@ def selects(option_text, options = {}) alias_method :select, :selects + DATE_TIME_SUFFIXES = {:rails => {:year => '1i', :month => '2i', :day => '3i', + :hour => '4i', :minute => '5i'}, + :full_words => {:year => 'year', :month => 'month', :day => 'day', + :hour => 'hour', :minute => 'minute'} + } + + # Verifies that date elements (year, month, day) exist on the current page + # with the specified values. You can optionally restrict the search to a specific + # date's elements by assigning options[:from] the value of the date's + # label. Selects all the date elements with date provided. The date provided may + # be a string or a Date/Time object. + # + # By default Rail's convention is used for detecting the date elements, but this + # may be overriden by assinging a options[:suffix_convention] or + # options[:suffixes]. In all cases all elements are assumed to have a + # shared prefix. For example, a birthday date on a form might have the following: + # 'birthday_Year', 'birthday_Month', 'birthday_Day'. You may also specify the + # prefix by assigning options[:id_prefix]. + # + # Examples: + # selects_date "January 23, 2004" + # selects_date "April 26, 1982", :from => "Birthday" + # selects_date Date.parse("December 25, 2000"), :from => "Event" + # selects_date "April 26, 1982", :suffix_convention => :full_words + # selects_date "April 26, 1982", :id_prefix => 'birthday', + # :suffixes => {:year => 'Year', :month => 'Mon', :day => 'Day'} + def selects_date(date_to_select, options ={}) + date = date_to_select.is_a?(Date) || date_to_select.is_a?(Time) ? + date_to_select : Date.parse(date_to_select) + + suffixes = extract_date_time_suffixes(options) + + id_prefix = locate_id_prefix(options) do + year_field = find_field_with_id(/(.*?)_#{suffixes[:year]}$/) + flunk("No date fields were found") unless year_field && year_field.id =~ /(.*?)_1i/ + $1 + end + + selects date.year, :from => "#{id_prefix}_#{suffixes[:year]}" + selects date.strftime('%B'), :from => "#{id_prefix}_#{suffixes[:month]}" + selects date.day, :from => "#{id_prefix}_#{suffixes[:day]}" + end + + alias_method :select_date, :selects_date + + # Verifies that time elements (hour, minute) exist on the current page + # with the specified values. You can optionally restrict the search to a specific + # time's elements by assigning options[:from] the value of the time's + # label. Selects all the time elements with date provided. The time provided may + # be a string or a Time object. + # + # By default Rail's convention is used for detecting the time elements, but this + # may be overriden by assinging a options[:suffix_convention] or + # options[:suffixes]. In all cases all elements are assumed to have a + # shared prefix. For example, an appointment time on a form might have the + # following: 'appt_time_hour', 'appt_time_min'. You may also specify the + # prefix by assigning options[:id_prefix]. + # + # Note: Just like Rails' time_select helper this assumes the form is using + # 24 hour select boxes, and not 12 hours with AM/PM. + # + # Examples: + # selects_time "9:30" + # selects_date "3:30PM", :from => "Party Time" + # selects_date Time.parse("10:00PM"), :from => "Event" + # selects_date "8:30", :suffix_convention => :full_words + # selects_date "10:30AM", :id_prefix => 'meeting', + # :suffixes => {:hour => 'Hour', :min => 'Min'} + def selects_time(time_to_select, options ={}) + time = time_to_select.is_a?(Time) ? time_to_select : Time.parse(time_to_select) + + suffixes = extract_date_time_suffixes(options) + + id_prefix = locate_id_prefix(options) do + hour_field = find_field_with_id(/(.*?)_#{suffixes[:hour]}$/) + flunk("No time fields were found") unless hour_field && hour_field.id =~ /(.*?)_4i/ + $1 + end + + selects time.hour.to_s.rjust(2,'0'), :from => "#{id_prefix}_#{suffixes[:hour]}" + selects time.min.to_s.rjust(2,'0'), :from => "#{id_prefix}_#{suffixes[:minute]}" + end + + + alias_method :select_time, :selects_time + + # Verifies and selects all the date and time elements on the current page. + # See #selects_time and #selects_date for more details and available options. + # + # Examples: + # selects_datetime "January 23, 2004 10:30AM" + # selects_datetime "April 26, 1982 7:00PM", :from => "Birthday" + # selects_datetime Time.parse("December 25, 2000 15:30"), :from => "Event" + # selects_datetime "April 26, 1982 5:50PM", :suffix_convention => :full_words + # selects_datetime "April 26, 1982 5:50PM", :id_prefix => 'birthday', + # :suffixes => {:year => 'Year', :month => 'Mon', :day => 'Day', + # :hour => 'Hour', :minute => 'Min'} + def selects_datetime(time_to_select, options ={}) + time = time_to_select.is_a?(Time) ? time_to_select : Time.parse(time_to_select) + + options[:id_prefix] ||= (options[:from] ? find_field_with_id(options[:from]) : nil) + + selects_date time, options + selects_time time, options + end + + alias_method :select_datetime, :selects_datetime + # Verifies that an input file field exists on the current page and sets # its value to the given +file+, so that the file will be uploaded # along with the form. An optional content_type may be given. @@ -196,6 +304,17 @@ def locate_field(field_locator, *field_types) #:nodoc: end end + def locate_id_prefix(options, &location_strategy) #:nodoc: + return options[:id_prefix] if options[:id_prefix] + id_prefix = options[:from] ? find_field_id_for_label(options[:from]) : yield + end + + def extract_date_time_suffixes(options) #:nodoc: + options[:suffixes] || + DATE_TIME_SUFFIXES[options[:suffix_convention]] || + DATE_TIME_SUFFIXES[:rails] + end + def areas #:nodoc: Webrat::XML.css_search(dom, "area").map do |element| Area.new(@session, element) @@ -217,4 +336,4 @@ def forms #:nodoc: end end -end \ No newline at end of file +end diff --git a/lib/webrat/core/session.rb b/lib/webrat/core/session.rb index c1fec8ef..47fa949f 100644 --- a/lib/webrat/core/session.rb +++ b/lib/webrat/core/session.rb @@ -184,6 +184,9 @@ def page_scope #:nodoc: def_delegators :current_scope, :uncheck, :unchecks def_delegators :current_scope, :choose, :chooses def_delegators :current_scope, :select, :selects + def_delegators :current_scope, :select_datetime, :selects_datetime + def_delegators :current_scope, :select_date, :selects_date + def_delegators :current_scope, :select_time, :selects_time def_delegators :current_scope, :attach_file, :attaches_file def_delegators :current_scope, :click_area, :clicks_area def_delegators :current_scope, :click_link, :clicks_link diff --git a/lib/webrat/ruby_html_unit.rb b/lib/webrat/ruby_html_unit.rb new file mode 100644 index 00000000..511c4e5a --- /dev/null +++ b/lib/webrat/ruby_html_unit.rb @@ -0,0 +1,41 @@ +$:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__))) + +module RubyHtmlUnit + Jars = Dir[File.dirname(__FILE__) + '/ruby_html_unit/htmlunit/*.jar'] +end + +if RUBY_PLATFORM =~ /java/ + require 'java' + RubyHtmlUnit::Jars.each { |jar| require(jar) } + + module HtmlUnit + include_package 'com.gargoylesoftware.htmlunit' + end + JavaString = java.lang.String +else + raise "RubyHtmlUnit only works on JRuby at the moment." +end + + +Dir[File.join(File.dirname(__FILE__), "ruby_html_unit", "*.rb")].each do |file| + require File.expand_path(file) +end + + +# Deal with the test server +if `uname`.chomp == "Darwin" + TEST_SERVER_PORT = '3001' + + if `ps ax`.match(/^\s*(\d*).*-e test -p #{TEST_SERVER_PORT}/) + puts "A test server was found running on port #{TEST_SERVER_PORT} (PID #{$1})" + else + puts "A test server was not found running (looking for -e test -p #{TEST_SERVER_PORT})" + puts "Please start a new test server using the command below:" + puts + + command_string = "ruby #{RAILS_ROOT}/script/server -e test -p #{TEST_SERVER_PORT}" + + puts command_string + exit + end +end \ No newline at end of file diff --git a/lib/webrat/ruby_html_unit/alert_handler.rb b/lib/webrat/ruby_html_unit/alert_handler.rb new file mode 100644 index 00000000..016e1c1b --- /dev/null +++ b/lib/webrat/ruby_html_unit/alert_handler.rb @@ -0,0 +1,13 @@ +module RubyHtmlUnit + + class AlertHandler + def handleAlert(html_unit_page, alert_message) + alert_messages << alert_message + end + + def alert_messages + @alert_messages ||= [] + end + end + +end \ No newline at end of file diff --git a/lib/webrat/ruby_html_unit/element.rb b/lib/webrat/ruby_html_unit/element.rb new file mode 100644 index 00000000..29988b55 --- /dev/null +++ b/lib/webrat/ruby_html_unit/element.rb @@ -0,0 +1,119 @@ +module RubyHtmlUnit + + class Element + def initialize(element, page) + @html_unit_element = element + @page = page + end + + def elem_type + @html_unit_element.node_name + end + + def id_attribute + @html_unit_element.id_attribute + end + + def matches_text?(text) + @html_unit_element.as_text =~ text_as_regexp(text) + end + + def matches_attribute?(attribute, text) + @html_unit_element.get_attribute(attribute) =~ text_as_regexp(text) + end + + def ancestors + current_element = self + [].tap do |ancs| + while current_element.parent && current_element.elem_type != 'html' + ancs << current_element + current_element = current_element.parent + end + end + end + + def label_matches?(label_text) + ancestors.each do |anc| + return true if anc.elem_type == 'label' && anc.matches_text?(label_text) + end + + if id_attribute.blank? + return nil + else + label_tag = @page.elements_by_xpath("//label[@for='#{id_attribute}']").first + return label_tag if label_tag && label_tag.matches_text?(label_text) + end + end + + def click + @html_unit_element.click + end + + def fill_in_with(value) + clear + type_string(value) + end + + def clear + case @html_unit_element.getTagName + when 'textarea' then @html_unit_element.setText('') + when 'input' then @html_unit_element.setValueAttribute('') + end + end + + def select(option_text) + @html_unit_element.getOptions.select { |e| e.asText =~ text_as_regexp(option_text) }.each do |option| + option.click + #@container.update_page(option.click) + end + end + + def set(value = true) + @html_unit_element.setChecked(value) + #value ? @html_unit_element.click : @html_unit_element.setChecked(value) + end + + def includes_option?(option_text) + !!@html_unit_element.getByXPath("//option").detect {|opt| opt.as_text =~ text_as_regexp(option_text) } + end + + def to_s + @html_unit_element.as_text + end + + def hidden? + !!ancestors.detect { |elem| elem.style =~ /display\s*:[\s'"]*none/ } + end + + def visible? + !hidden? + end + + def parent + @html_unit_element.respond_to?(:parent_node) ? Element.new(@html_unit_element.parent_node, @page) : nil + end + + def class_name + @html_unit_element.class_attribute + end + + def method_missing(name, *args) + return @html_unit_element.send("#{name}_attribute") if @html_unit_element.respond_to?("#{name}_attribute") + super + end + + protected + def type_string(value) + last_page = nil + JavaString.new(value.to_java_bytes, @html_unit_element.getPage.getPageEncoding).toCharArray.each do |char| + last_page = @html_unit_element.type(char) + end + last_page + end + + def text_as_regexp(text) + Regexp.new(Regexp.escape(text), true) + end + end + +end \ No newline at end of file diff --git a/lib/webrat/ruby_html_unit/htmlunit/commons-codec-1.3.jar b/lib/webrat/ruby_html_unit/htmlunit/commons-codec-1.3.jar new file mode 100644 index 00000000..957b6752 Binary files /dev/null and b/lib/webrat/ruby_html_unit/htmlunit/commons-codec-1.3.jar differ diff --git a/lib/webrat/ruby_html_unit/htmlunit/commons-collections-3.2.jar b/lib/webrat/ruby_html_unit/htmlunit/commons-collections-3.2.jar new file mode 100644 index 00000000..75580be2 Binary files /dev/null and b/lib/webrat/ruby_html_unit/htmlunit/commons-collections-3.2.jar differ diff --git a/lib/webrat/ruby_html_unit/htmlunit/commons-httpclient-3.1.jar b/lib/webrat/ruby_html_unit/htmlunit/commons-httpclient-3.1.jar new file mode 100644 index 00000000..7c59774a Binary files /dev/null and b/lib/webrat/ruby_html_unit/htmlunit/commons-httpclient-3.1.jar differ diff --git a/lib/webrat/ruby_html_unit/htmlunit/commons-io-1.4.jar b/lib/webrat/ruby_html_unit/htmlunit/commons-io-1.4.jar new file mode 100644 index 00000000..133dc6cb Binary files /dev/null and b/lib/webrat/ruby_html_unit/htmlunit/commons-io-1.4.jar differ diff --git a/lib/webrat/ruby_html_unit/htmlunit/commons-lang-2.4.jar b/lib/webrat/ruby_html_unit/htmlunit/commons-lang-2.4.jar new file mode 100644 index 00000000..532939ec Binary files /dev/null and b/lib/webrat/ruby_html_unit/htmlunit/commons-lang-2.4.jar differ diff --git a/lib/webrat/ruby_html_unit/htmlunit/commons-logging-1.1.1.jar b/lib/webrat/ruby_html_unit/htmlunit/commons-logging-1.1.1.jar new file mode 100644 index 00000000..1deef144 Binary files /dev/null and b/lib/webrat/ruby_html_unit/htmlunit/commons-logging-1.1.1.jar differ diff --git a/lib/webrat/ruby_html_unit/htmlunit/cssparser-0.9.5.jar b/lib/webrat/ruby_html_unit/htmlunit/cssparser-0.9.5.jar new file mode 100644 index 00000000..9fc2767f Binary files /dev/null and b/lib/webrat/ruby_html_unit/htmlunit/cssparser-0.9.5.jar differ diff --git a/lib/webrat/ruby_html_unit/htmlunit/htmlunit-2.2.jar b/lib/webrat/ruby_html_unit/htmlunit/htmlunit-2.2.jar new file mode 100644 index 00000000..8cc878a3 Binary files /dev/null and b/lib/webrat/ruby_html_unit/htmlunit/htmlunit-2.2.jar differ diff --git a/lib/webrat/ruby_html_unit/htmlunit/htmlunit-core-js-2.2.jar b/lib/webrat/ruby_html_unit/htmlunit/htmlunit-core-js-2.2.jar new file mode 100644 index 00000000..be23a89b Binary files /dev/null and b/lib/webrat/ruby_html_unit/htmlunit/htmlunit-core-js-2.2.jar differ diff --git a/lib/webrat/ruby_html_unit/htmlunit/nekohtml-1.9.8.jar b/lib/webrat/ruby_html_unit/htmlunit/nekohtml-1.9.8.jar new file mode 100644 index 00000000..1848c0a0 Binary files /dev/null and b/lib/webrat/ruby_html_unit/htmlunit/nekohtml-1.9.8.jar differ diff --git a/lib/webrat/ruby_html_unit/htmlunit/sac-1.3.jar b/lib/webrat/ruby_html_unit/htmlunit/sac-1.3.jar new file mode 100644 index 00000000..39b92b1d Binary files /dev/null and b/lib/webrat/ruby_html_unit/htmlunit/sac-1.3.jar differ diff --git a/lib/webrat/ruby_html_unit/htmlunit/xalan-2.7.0.jar b/lib/webrat/ruby_html_unit/htmlunit/xalan-2.7.0.jar new file mode 100644 index 00000000..007be396 Binary files /dev/null and b/lib/webrat/ruby_html_unit/htmlunit/xalan-2.7.0.jar differ diff --git a/lib/webrat/ruby_html_unit/htmlunit/xercesImpl-2.8.1.jar b/lib/webrat/ruby_html_unit/htmlunit/xercesImpl-2.8.1.jar new file mode 100644 index 00000000..3b351f6e Binary files /dev/null and b/lib/webrat/ruby_html_unit/htmlunit/xercesImpl-2.8.1.jar differ diff --git a/lib/webrat/ruby_html_unit/htmlunit/xml-apis-1.0.b2.jar b/lib/webrat/ruby_html_unit/htmlunit/xml-apis-1.0.b2.jar new file mode 100644 index 00000000..ad33a5af Binary files /dev/null and b/lib/webrat/ruby_html_unit/htmlunit/xml-apis-1.0.b2.jar differ diff --git a/lib/webrat/ruby_html_unit/page.rb b/lib/webrat/ruby_html_unit/page.rb new file mode 100644 index 00000000..34b2e108 --- /dev/null +++ b/lib/webrat/ruby_html_unit/page.rb @@ -0,0 +1,114 @@ +module RubyHtmlUnit + + class Page + SAVED_PAGE_DIR = File.expand_path('.') + + def initialize(html_unit_page) + @html_unit_page = html_unit_page + end + + def body + @html_unit_page.getWebResponse.getContentAsString || '' + end + alias_method :html_document, :body + + def find_link(text) + matching_links = elements_by_tag_name('a').select{|e| e.matches_text?(text)} + + if matching_links.any? + matching_links.sort_by { |l| l.to_s.length }.first + else + flunk("Could not find link with text #{text.inspect}") + end + end + + def find_button(text = nil) + buttons = [] + field_type_xpaths = %w( //button //input[@type='submit'] //input[@type='reset'] //input[@type='image'] //input[@type='button'] ) + field_type_xpaths.each do |xpath_expr| + buttons += elements_by_xpath(xpath_expr) + end + + return buttons.first if text.nil? + + matching_buttons = buttons.select{|b| b.matches_attribute?('value', text) } + + if matching_buttons.any? + matching_buttons.sort_by { |b| b.to_s.length }.first + else + flunk("Could not find button with text #{text.inspect}") + end + end + + def find_field(id_or_name_or_label, field_type) + field_type_xpaths = case field_type + when :text then %w( //input[@type='text'] //input[@type='hidden'] //textarea ) + when :select then %w( //select ) + when :radio then %w( //input[@type='radio'] ) + when :checkbox then %w( //input[@type='checkbox'] ) + end + + field_type_xpaths.each do |xpath_expr| + elements_by_xpath(xpath_expr).each do |possible_field| + return possible_field if possible_field.id_attribute == id_or_name_or_label || + possible_field.name == id_or_name_or_label || + possible_field.label_matches?(id_or_name_or_label) + end + end + + flunk("Could not find #{field_type_xpaths.inspect}: #{id_or_name_or_label.inspect}") + end + + def find_select_list_with_option(option_text, id_or_name_or_label = nil) + if id_or_name_or_label + select_tag = find_field(id_or_name_or_label, :select) + return select_tag if select_tag && select_tag.includes_option?(option_text) + else + elements_by_xpath("//select").each do |sel| + if sel.includes_option?(option_text) + return sel + end + end + end + nil + end + + def elements_by_tag_name(tag_name) + node_list = @html_unit_page.getElementsByTagName(tag_name) + list_len = node_list.length + + [].tap do |array| + 0.upto(list_len-1) { |i| array << Element.new(node_list.item(i), self)} + end + end + + def elements_by_xpath(xpath_expression) + @html_unit_page.getByXPath(xpath_expression).to_a.map{ |e| Element.new(e, self) } + end + + def save_and_open + return unless File.exist?(SAVED_PAGE_DIR) + + filename = "#{SAVED_PAGE_DIR}/webrat-#{Time.now.to_i}.html" + + File.open(filename, "w") do |f| + f.write rewrite_css_and_image_references(body) + end + + open_in_browser(filename) + end + + def rewrite_css_and_image_references(response_html) # :nodoc + response_html.gsub(%r<"/(stylesheets|images)>, Session.rewrite_url('"/\1')) + end + + def open_in_browser(path) # :nodoc + `open #{path}` + end + + def flunk(message) + raise message + end + end + +end \ No newline at end of file diff --git a/lib/webrat/ruby_html_unit/response.rb b/lib/webrat/ruby_html_unit/response.rb new file mode 100644 index 00000000..201db0e4 --- /dev/null +++ b/lib/webrat/ruby_html_unit/response.rb @@ -0,0 +1,24 @@ +module RubyHtmlUnit + + class Response + def initialize(html_unit_response) + @html_unit_response = html_unit_response + end + + def success? + @html_unit_response.status_code / 100 == 2 + end + + def content_type + @html_unit_response.content_type + end + + def body + @html_unit_response.getContentAsString || '' + end + alias_method :text, :body + + + end + +end \ No newline at end of file diff --git a/lib/webrat/ruby_html_unit/rspec_story.rb b/lib/webrat/ruby_html_unit/rspec_story.rb new file mode 100644 index 00000000..2c9847ae --- /dev/null +++ b/lib/webrat/ruby_html_unit/rspec_story.rb @@ -0,0 +1,26 @@ +class Spec::Story::Runner::ScenarioRunner + def initialize + @listeners = [] + end +end + +module RubyHtmlUnit + + class RspecStory + include ::Spec::Matchers + include ::Spec::Rails::Matchers + + def current_session + @current_session ||= Session.new + end + + def method_missing(name, *args) + if current_session.respond_to?(name) + current_session.send(name, *args) + else + super + end + end + end + +end \ No newline at end of file diff --git a/lib/webrat/ruby_html_unit/session.rb b/lib/webrat/ruby_html_unit/session.rb new file mode 100644 index 00000000..221de840 --- /dev/null +++ b/lib/webrat/ruby_html_unit/session.rb @@ -0,0 +1,127 @@ +module RubyHtmlUnit + + class Session + attr_accessor :response, :current_page, :alert_handler + + include ActionController::UrlWriter + include ActionController::Assertions + include Test::Unit::Assertions + + + class << self + def rewrite_url(url) + if url =~ %r{^(/.*)} || url =~ %r{^https?://www.example.com(/.*)} + append_to_root_url($1) + else + url + end + end + + def append_to_root_url(path) + path = "/#{path}" unless path =~ /^\// + "http://localhost:#{TEST_SERVER_PORT}#{path}" + end + end + + def initialize() + #java.lang.System.getProperties.put("org.apache.commons.logging.simplelog.defaultlog", opts[:log_level] ? opts[:log_level].to_s : "warn") + java.lang.System.getProperties.put("org.apache.commons.logging.simplelog.defaultlog", "warn") + + browser = ::HtmlUnit::BrowserVersion::FIREFOX_2 + @webclient = ::HtmlUnit::WebClient.new(browser) + @webclient.setThrowExceptionOnScriptError(false) #unless opts[:javascript_exceptions] + @webclient.setThrowExceptionOnFailingStatusCode(false) #unless opts[:status_code_exceptions] + @webclient.setCssEnabled(false) #unless opts[:css] + @webclient.setUseInsecureSSL(true) #if opts[:secure_ssl] + + @alert_handler = AlertHandler.new + + @webclient.setAlertHandler(@alert_handler) + + @response = nil + @current_page = nil + end + + def visits(url) + update_page(@webclient.getPage(Session.rewrite_url(url))) + end + + def clicks_link(text) + link = @current_page.find_link(text) + update_page(link.click) + end + + def clicks_button(text = nil) + button = @current_page.find_button(text) + update_page(button.click) + end + + def fills_in(id_or_name_or_label, options = {}) + field = @current_page.find_field(id_or_name_or_label, :text) + update_page(field.fill_in_with(options[:with])) + end + + def selects(option_text, options = {}) + id_or_name_or_label = options[:from] + + select_tag = @current_page.find_select_list_with_option(option_text, id_or_name_or_label) + + flunk("Could not find option #{option_text.inspect}") if select_tag.nil? + select_tag.select(option_text) + end + + def chooses(label) + field = @current_page.find_field(label, :radio) + update_page(field.set) + end + + def checks(id_or_name_or_label) + field = @current_page.find_field(id_or_name_or_label, :checkbox) + update_page(field.set(true)) + end + + def unchecks(id_or_name_or_label) + field = @current_page.find_field(id_or_name_or_label, :checkbox) + update_page(field.set(false)) + end + + def get_element(dom_id) + @current_page.elements_by_xpath("//*[@id='#{dom_id}']").first + end + alias_method :get_element_by_id, :get_element + + def wait_for_result(wait_type) + # No support for different types of waiting right now + sleep(0.5) + # if wait_type == :ajax + # wait_for_ajax + # elsif wait_type == :effects + # wait_for_effects + # else + # wait_for_page_to_load + # end + end + + def update_page(html_unit_page) + @current_page = Page.new(html_unit_page) + @response = Response.new(html_unit_page.getWebResponse) + end + + def save_and_open_page + @current_page.save_and_open + end + + def respond_to?(method) + super || @current_page.respond_to?(method) + end + + def method_missing(name, *args) + if @current_page.respond_to?(name) + @current_page.send(name, *args) + else + super + end + end + end + +end \ No newline at end of file diff --git a/spec/api/selects_date_spec.rb b/spec/api/selects_date_spec.rb new file mode 100644 index 00000000..f7b3f01a --- /dev/null +++ b/spec/api/selects_date_spec.rb @@ -0,0 +1,130 @@ +require File.expand_path(File.dirname(__FILE__) + "/../spec_helper") + +describe "selects_date" do + before do + @session = Webrat::TestSession.new + end + + it "should send the values for each individual date component" do + @session.response_body = <<-EOS +
+
+ + + + +
+ EOS + @session.should_receive(:post).with("/appointments", + "appointment" => {"date(1i)" => '2003', "date(2i)" => "12", "date(3i)" => "25"}) + @session.selects_date "December 25, 2003", :from => "Date" + @session.click_button + end + + it "should select the date components with the suffix convention provided" do + @session.response_body = <<-EOS +
+
+ + + + +
+ EOS + @session.should_receive(:post).with("/appointments", + "appointment" => {"year" => '2003', "month" => "12", "day" => "25"}) + @session.selects_date "December 25, 2003 9:30", :from => "Date", :suffix_convention => :full_words + @session.click_button + end + + it "should select the date components with the suffixes provided" do + @session.response_body = <<-EOS +
+
+ + + + +
+ EOS + @session.should_receive(:post).with("/appointments", + "appointment" => {"y" => '2003', "mo" => "12", "d" => "25"}) + @session.selects_date "December 25, 2003 9:30", :from => "Date", + :suffixes => {:year => 'y', :month => 'mo', :day => 'd'} + @session.click_button + end + + + it "should accept a date object" do + @session.response_body = <<-EOS +
+
+ + + + +
+ EOS + @session.should_receive(:post).with("/appointments", + "appointment" => {"date(1i)" => '2003', "date(2i)" => "12", "date(3i)" => "25"}) + @session.selects_date Date.parse("December 25, 2003"), :from => "date" + @session.click_button + end + + it "should work when no label is specified" do + @session.response_body = <<-EOS +
+ + + + +
+ EOS + @session.should_receive(:post).with("/appointments", + "appointment" => {"date(1i)" => '2003', "date(2i)" => "12", "date(3i)" => "25"}) + @session.selects_date "December 25, 2003" + @session.click_button + end + + it "should fail if the specified label is not found" do + @session.response_body = <<-EOS +
+ + +
+ EOS + + lambda { @session.selects_date "December 25, 2003", :from => "date" }.should raise_error + end + +end diff --git a/spec/api/selects_datetime_spec.rb b/spec/api/selects_datetime_spec.rb new file mode 100644 index 00000000..3ac11463 --- /dev/null +++ b/spec/api/selects_datetime_spec.rb @@ -0,0 +1,159 @@ +require File.expand_path(File.dirname(__FILE__) + "/../spec_helper") + +describe "selects_datetime" do + before do + @session = Webrat::TestSession.new + end + + it "should send the values for each individual date and time components" do + @session.response_body = <<-EOS +
+
+ + + + + : + +
+ EOS + @session.should_receive(:post).with("/appointments", + "appointment" => {"time(1i)" => '2003', "time(2i)" => "12", "time(3i)" => "25", "time(4i)" => "09", "time(5i)" => "30"}) + @session.selects_datetime "December 25, 2003 9:30", :from => "Time" + @session.click_button + end + + it "should select the date and time components with the suffix convention provided" do + @session.response_body = <<-EOS +
+
+ + + + + : + +
+ EOS + @session.should_receive(:post).with("/appointments", + "appointment" => {"year" => '2003', "month" => "12", "day" => "25", "hour" => "09", "minute" => "30"}) + @session.selects_datetime "December 25, 2003 9:30", :from => "Time", :suffix_convention => :full_words + @session.click_button + end + + it "should select the date and time components with the suffixes provided" do + @session.response_body = <<-EOS +
+
+ + + + + : + +
+ EOS + @session.should_receive(:post).with("/appointments", + "appointment" => {"y" => '2003', "mo" => "12", "d" => "25", "h" => "09", "mi" => "30"}) + @session.selects_datetime "December 25, 2003 9:30", :from => "Time", + :suffixes => {:year => 'y', :month => 'mo', :day => 'd', :hour => 'h', :minute => 'mi'} + @session.click_button + end + + it "should accept a time object" do + @session.response_body = <<-EOS +
+
+ + + + + : + +
+ EOS + @session.should_receive(:post).with("/appointments", + "appointment" => {"time(1i)" => '2003', "time(2i)" => "12", "time(3i)" => "25", "time(4i)" => "09", "time(5i)" => "30"}) + @session.select_datetime Time.parse("December 25, 2003 9:30"), :from => "Time" + @session.click_button + end + + it "should work when no label is specified" do + @session.response_body = <<-EOS +
+ + + + + : + +
+ EOS + @session.should_receive(:post).with("/appointments", + "appointment" => {"time(1i)" => '2003', "time(2i)" => "12", "time(3i)" => "25", "time(4i)" => "09", "time(5i)" => "30"}) + @session.selects_datetime "December 25, 2003 9:30" + @session.click_button + end + + it "should fail if the specified label is not found" do + @session.response_body = <<-EOS +
+ + +
+ EOS + + lambda { @session.selects_datetime "December 25, 2003 9:30", :from => "Time" }.should raise_error + end + +end diff --git a/spec/api/selects_time_spec.rb b/spec/api/selects_time_spec.rb new file mode 100644 index 00000000..ea1bf716 --- /dev/null +++ b/spec/api/selects_time_spec.rb @@ -0,0 +1,114 @@ +require File.expand_path(File.dirname(__FILE__) + "/../spec_helper") + +describe "select_time" do + before do + @session = Webrat::TestSession.new + end + + it "should send the values for each individual time component" do + @session.response_body = <<-EOS +
+
+ + : + +
+ EOS + @session.should_receive(:post).with("/appointments", + "appointment" => {"time(4i)" => "09", "time(5i)" => "30"}) + @session.selects_time "9:30AM", :from => "Time" + @session.click_button + end + + it "should select time components with the suffix convention provided" do + @session.response_body = <<-EOS +
+
+ + : + +
+ EOS + @session.should_receive(:post).with("/appointments", + "appointment" => {"hour" => "09", "minute" => "30"}) + @session.selects_time "9:30", :from => "Time", :suffix_convention => :full_words + @session.click_button + end + + it "should select the time components with the suffixes provided" do + @session.response_body = <<-EOS +
+
+ + : + +
+ EOS + @session.should_receive(:post).with("/appointments", + "appointment" => {"h" => "09", "mi" => "30"}) + @session.selects_time "9:30", :from => "Time", + :suffixes => {:hour => 'h', :minute => 'mi'} + @session.click_button + end + + it "should accept a time object" do + @session.response_body = <<-EOS +
+
+ + : + +
+ EOS + @session.should_receive(:post).with("/appointments", + "appointment" => {"time(4i)" => "09", "time(5i)" => "30"}) + @session.select_time Time.parse("9:30AM"), :from => "Time" + @session.click_button + end + + it "should work when no label is specified" do + @session.response_body = <<-EOS +
+ + : + +
+ EOS + @session.should_receive(:post).with("/appointments", + "appointment" => {"time(4i)" => "09", "time(5i)" => "30"}) + @session.select_time "9:30" + @session.click_button + end + + it "should fail if the specified label is not found" do + @session.response_body = <<-EOS +
+ + +
+ EOS + + lambda { @session.select_time "9:30", :from => "Time" }.should raise_error + end + +end