class Rack::Test::Session
Rack::Test::Session
handles a series of requests issued to a Rack
app. It keeps track of the cookies for the session, and allows for setting headers and a default rack environment that is used for future requests.
Rack::Test::Session's methods are most often called through Rack::Test::Methods
, which will automatically build a session when it's first used.
Constants
- DEFAULT_ENV
Attributes
The default host used for the session for when using paths for URIs.
Public Class Methods
Creates a Rack::Test::Session
for a given Rack
app or Rack::Test::BasicSession.
Note: Generally, you won't need to initialize a Rack::Test::Session
directly. Instead, you should include Rack::Test::Methods
into your testing context. (See README.rdoc for an example)
The following methods are defined via metaprogramming: get, post, put, patch, delete, options, and head. Each method submits a request with the given request method, with the given URI and optional parameters and rack environment. Examples:
# URI only: get("/") # GET / get("/?foo=bar") # GET /?foo=bar # URI and parameters get("/foo", 'bar'=>'baz') # GET /foo?bar=baz post("/foo", 'bar'=>'baz') # POST /foo (bar=baz in request body) # URI, parameters, and rack environment get("/bar", {}, 'CONTENT_TYPE'=>'foo') get("/bar", {'foo'=>'baz'}, 'HTTP_ACCEPT'=>'*')
The above methods as well as request
and custom_request
store the Rack::Request submitted in last_request
. The methods store a Rack::MockResponse based on the response in last_response
. last_response
is also returned by the methods. If a block is given, last_response
is also yielded to the block.
# File lib/rack/test.rb, line 99 def initialize(app, default_host = DEFAULT_HOST) @env = {} @app = app @after_request = [] @default_host = default_host @last_request = nil @last_response = nil clear_cookies end
Public Instance Methods
Run a block after the each request completes.
# File lib/rack/test.rb, line 118 def after_request(&block) @after_request << block end
Issue a request using the given HTTP verb for the given URI, with optional params and rack environment. Example:
custom_request "LINK", "/"
# File lib/rack/test.rb, line 160 def custom_request(verb, uri, params = {}, env = {}, &block) uri = parse_uri(uri, env) env = env_for(uri, env.merge(method: verb.to_s.upcase, params: params)) process_request(uri, env, &block) end
Set an entry in the rack environment to be included on all subsequent requests through the session. Use a value of nil to remove a previously value. Example:
env "rack.session", {:csrf => 'token'}
# File lib/rack/test.rb, line 185 def env(name, value) if value.nil? @env.delete(name) else @env[name] = value end end
Rack::Test
will not follow any redirects automatically. This method will follow the redirect returned (including setting the Referer header on the new request) in the last response. If the last response was not a redirect, an error will be raised.
# File lib/rack/test.rb, line 209 def follow_redirect! unless last_response.redirect? raise Error, 'Last response was not a redirect. Cannot follow_redirect!' end if last_response.status == 307 request_method = last_request.request_method params = last_request.params else request_method = 'GET' params = {} end # Compute the next location by appending the location header with the # last request, as per https://tools.ietf.org/html/rfc7231#section-7.1.2 # Adding two absolute locations returns the right-hand location next_location = URI.parse(last_request.url) + URI.parse(last_response['Location']) custom_request( request_method, next_location.to_s, params, 'HTTP_REFERER' => last_request.url, 'rack.session' => last_request.session, 'rack.session.options' => last_request.session_options ) end
Set a header to be included on all subsequent requests through the session. Use a value of nil to remove a previously configured header.
In accordance with the Rack
spec, headers will be included in the Rack
environment hash in HTTP_USER_AGENT form. Example:
header "user-agent", "Firefox"
# File lib/rack/test.rb, line 173 def header(name, value) name = name.upcase name.tr!('-', '_') name = "HTTP_#{name}" unless name == 'CONTENT_TYPE' || name == 'CONTENT_LENGTH' env(name, value) end
Return the last request issued in the session. Raises an error if no requests have been sent yet.
# File lib/rack/test.rb, line 134 def last_request raise Error, 'No request yet. Request a page first.' unless @last_request @last_request end
Return the last response received in the session. Raises an error if no requests have been sent yet.
# File lib/rack/test.rb, line 141 def last_response raise Error, 'No response yet. Request a page first.' unless @last_response @last_response end
Yield to the block, and restore the last request, last response, and cookie jar to the state they were prior to block execution upon exiting the block.
# File lib/rack/test.rb, line 240 def restore_state request = @last_request response = @last_response cookie_jar = @cookie_jar.dup after_request = @after_request.dup begin yield ensure @last_request = request @last_response = response @cookie_jar = cookie_jar @after_request = after_request end end
Private Instance Methods
Append a string version of the query params to the array of query params.
# File lib/rack/test.rb, line 340 def append_query_params(query_array, query_params) query_params = parse_nested_query(query_params) if query_params.is_a?(String) query_array << build_nested_query(query_params) end
# File lib/rack/test.rb, line 260 def close_body(body) body.close if body.respond_to?(:close) end
Update environment to use based on given URI.
# File lib/rack/test.rb, line 293 def env_for(uri, env) env = DEFAULT_ENV.merge(@env).merge!(env) env['HTTP_HOST'] ||= [uri.host, (uri.port if uri.port != uri.default_port)].compact.join(':') env['HTTPS'] = 'on' if URI::HTTPS === uri env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest' if env[:xhr] env['REQUEST_METHOD'] ||= env[:method] ? env[:method].to_s.upcase : 'GET' params = env.delete(:params) query_array = [uri.query] if env['REQUEST_METHOD'] == 'GET' # Treat params as query params if params append_query_params(query_array, params) end elsif !env.key?(:input) env['CONTENT_TYPE'] ||= 'application/x-www-form-urlencoded' params ||= {} multipart = env.has_key?(:multipart) ? env.delete(:multipart) : env['CONTENT_TYPE'].start_with?('multipart/') if params.is_a?(Hash) if !params.empty? && data = build_multipart(params, false, multipart) env[:input] = data env['CONTENT_LENGTH'] ||= data.length.to_s env['CONTENT_TYPE'] = "#{multipart_content_type(env)}; boundary=#{MULTIPART_BOUNDARY}" else env[:input] = build_nested_query(params) end else env[:input] = params end end if query_params = env.delete(:query_params) append_query_params(query_array, query_params) end query_array.compact! query_array.reject!(&:empty?) uri.query = query_array.join('&') set_cookie(env.delete(:cookie), uri) if env.key?(:cookie) Rack::MockRequest.env_for(uri.to_s, env) end
Return the multipart content type to use based on the environment.
# File lib/rack/test.rb, line 346 def multipart_content_type(env) requested_content_type = env['CONTENT_TYPE'] if requested_content_type.start_with?('multipart/') requested_content_type else 'multipart/form-data' end end
Normalize URI based on given URI/path and environment.
# File lib/rack/test.rb, line 271 def parse_uri(path, env) uri = URI.parse(path) uri.path = "/#{uri.path}" unless uri.path.start_with?('/') uri.host ||= @default_host uri.scheme ||= 'https' if env['HTTPS'] == 'on' uri end
Submit the request with the given URI and rack environment to the mock session. Returns and potentially yields the last response.
# File lib/rack/test.rb, line 357 def process_request(uri, env) env['HTTP_COOKIE'] ||= cookie_jar.for(uri) @last_request = Rack::Request.new(env) status, headers, body = @app.call(env).to_a @last_response = MockResponse.new(status, headers, body, env['rack.errors'].flush) close_body(body) cookie_jar.merge(last_response.headers['set-cookie'], uri) @after_request.each(&:call) @last_response.finish yield @last_response if block_given? @last_response end