require "forwardable" require "ostruct" require "webrat/core/mime" require "webrat/core/save_and_open_page" module Webrat # A page load or form submission returned an unsuccessful response code (500-599) class PageLoadError < WebratError end class InfiniteRedirectError < WebratError end def self.session_class if Webrat.configuration.mode == :selenium SeleniumSession else Session end end def self.adapter_class case Webrat.configuration.mode when :rails RailsAdapter when :merb MerbAdapter when :rack RackAdapter when :rack_test warn("The :rack_test mode is deprecated. Please use :rack instead") require "webrat/rack" RackAdapter when :sinatra warn("The :sinatra mode is deprecated. Please use :rack instead") SinatraAdapter when :mechanize MechanizeAdapter else raise WebratError.new(<<-STR) Unknown Webrat mode: #{Webrat.configuration.mode.inspect} Please ensure you have a Webrat configuration block that specifies a mode in your test_helper.rb, spec_helper.rb, or env.rb (for Cucumber). This configure block supercedes the need to require "webrat/". For example: Webrat.configure do |config| config.mode = :rails end STR end end class Session extend Forwardable include Logging include SaveAndOpenPage attr_accessor :adapter attr_reader :current_url attr_reader :elements def_delegators :@adapter, :response, :response_code, :response_body, :response_headers, :response_body=, :response_code=, :get, :post, :put, :delete def initialize(adapter = nil) @adapter = adapter @http_method = :get @data = {} @default_headers = {} @custom_headers = {} @current_url = nil reset end def current_dom #:nodoc: current_scope.dom end # For backwards compatibility -- removing in 1.0 def current_page #:nodoc: warn "current_page is deprecated and will be going away in the next release. Use current_url instead." page = OpenStruct.new page.url = @current_url page.http_method = @http_method page.data = @data page end def header(key, value) @custom_headers[key] = value end def http_accept(mime_type) header('Accept', Webrat::MIME.mime_type(mime_type)) end def basic_auth(user, pass) encoded_login = ["#{user}:#{pass}"].pack("m*").gsub(/\n/, '') header('HTTP_AUTHORIZATION', "Basic #{encoded_login}") end def headers #:nodoc: @default_headers.dup.merge(@custom_headers.dup) end def request_page(url, http_method, data) #:nodoc: h = headers h['HTTP_REFERER'] = @current_url if @current_url debug_log "REQUESTING PAGE: #{http_method.to_s.upcase} #{url} with #{data.inspect} and HTTP headers #{h.inspect}" process_request(http_method, url, data, h) save_and_open_page if exception_caught? && Webrat.configuration.open_error_files? raise PageLoadError.new("Page load was not successful (Code: #{response_code.inspect}):\n#{formatted_error}") unless success_code? reset @current_url = url @http_method = http_method @data = data if internal_redirect? check_for_infinite_redirects request_page(response_location, :get, {}) end return response end def check_for_infinite_redirects if current_url == response_location @_identical_redirect_count ||= 0 @_identical_redirect_count += 1 end if infinite_redirect_limit_exceeded? raise InfiniteRedirectError.new("#{Webrat.configuration.infinite_redirect_limit} redirects to the same URL (#{current_url.inspect})") end end def infinite_redirect_limit_exceeded? Webrat.configuration.infinite_redirect_limit && (@_identical_redirect_count || 0) > Webrat.configuration.infinite_redirect_limit end def success_code? #:nodoc: (200..499).include?(response_code) end def redirect? #:nodoc: [301, 302, 303, 307].include?(response_code) end def internal_redirect? return false unless redirect? #should keep internal_redirects if the subdomain changes current_host_domain = current_host.split('.')[-2..-1].join('.') rescue current_host response_location_host_domain = response_location_host.split('.')[-2..-1].join('.') rescue response_location_host current_host_domain == response_location_host_domain end #easy helper to pull out where we were redirected to def redirected_to redirect? ? response_location : nil end def exception_caught? #:nodoc: response_body =~ /Exception caught/ end def current_scope #:nodoc: scopes.last || page_scope end # Reloads the last page requested. Note that this will resubmit forms # and their data. def reload request_page(@current_url, @http_method, @data) end webrat_deprecate :reloads, :reload # Works like click_link, but only looks for the link text within a given selector # # Example: # click_link_within "#user_12", "Vote" def click_link_within(selector, link_text) within(selector) do click_link(link_text) end end webrat_deprecate :clicks_link_within, :click_link_within def within(selector) scopes.push(Scope.from_scope(self, current_scope, selector)) ret = yield(current_scope) scopes.pop return ret end # Issues a GET request for a page, follows any redirects, and verifies the final page # load was successful. # # Example: # visit "/" def visit(url = nil, http_method = :get, data = {}) request_page(url, http_method, data) end webrat_deprecate :visits, :visit # Subclasses can override this to show error messages without html def formatted_error #:nodoc: response_body end def scopes #:nodoc: @_scopes ||= [] end def page_scope #:nodoc: @_page_scope ||= Scope.from_page(self, response, response_body) end def dom page_scope.dom end def xml_content_type? false end def simulate return if Webrat.configuration.mode == :selenium yield end def automate return unless Webrat.configuration.mode == :selenium yield end def_delegators :current_scope, :fill_in, :fills_in def_delegators :current_scope, :set_hidden_field def_delegators :current_scope, :submit_form def_delegators :current_scope, :check, :checks def_delegators :current_scope, :uncheck, :unchecks def_delegators :current_scope, :choose, :chooses def_delegators :current_scope, :select, :selects def_delegators :current_scope, :unselect, :unselects 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 def_delegators :current_scope, :click_button, :clicks_button def_delegators :current_scope, :field_labeled def_delegators :current_scope, :field_named def_delegators :current_scope, :field_by_xpath def_delegators :current_scope, :field_with_id def_delegators :current_scope, :select_option def_delegators :current_scope, :field_named private def process_request(http_method, url, data, headers) if headers.empty? send "#{http_method}", url, data || {} else send "#{http_method}", url, data || {}, headers end end def response_location response_headers['Location'] end def current_host URI.parse(current_url).host || @custom_headers["Host"] || "www.example.com" end def response_location_host URI.parse(response_location).host || "www.example.com" end def reset @elements = {} @_scopes = nil @_page_scope = nil end end end