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 = {} @digest_username = nil @digest_password = nil @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 120 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 162 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 187 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 226 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 175 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 136 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 143 def last_response raise Error, 'No response yet. Request a page first.' unless @last_response @last_response end
Private Instance Methods
Append a string version of the query params to the array of query params.
# File lib/rack/test.rb, line 338 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 258 def close_body(body) body.close if body.respond_to?(:close) end
# File lib/rack/test.rb, line 401 def digest_auth_configured? @digest_username end
# File lib/rack/test.rb, line 378 def digest_auth_header require_relative 'test/mock_digest_request' challenge = last_response['WWW-Authenticate'].split(' ', 2).last params = Rack::Auth::Digest::Params.parse(challenge) params.merge!('username' => @digest_username, 'nc' => '00000001', 'cnonce' => 'nonsensenonce', 'uri' => last_request.fullpath, 'method' => last_request.env['REQUEST_METHOD']) params['response'] = MockDigestRequest_.new(params).response(@digest_password) "Digest #{params}" end
Update environment to use based on given URI.
# File lib/rack/test.rb, line 291 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 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 344 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 269 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 355 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 if retry_with_digest_auth?(env) auth_env = env.merge('HTTP_AUTHORIZATION' => digest_auth_header, 'rack-test.digest_auth_retry' => true) auth_env.delete('rack.request') process_request(uri, auth_env) else yield last_response if block_given? last_response end end
# File lib/rack/test.rb, line 395 def retry_with_digest_auth?(env) last_response.status == 401 && digest_auth_configured? && !env['rack-test.digest_auth_retry'] end