# frozen_string_literal: true require 'sinatra/base' require 'rack' begin require 'rack/test' rescue LoadError abort 'Add rack-test to your Gemfile to use this module!' end require 'forwardable' module Sinatra Base.set :environment, :test # Helper methods to ease testing your Sinatra application. Partly extracted # from Sinatra. Testing framework agnostic. module TestHelpers include Rack::Test::Methods extend Forwardable attr_accessor :settings # @!group Instance Methods delegated to last_response # @!method body # # Body of last_response # # @see https://www.rubydoc.info/github/rack/rack/main/Rack/Response#body-instance_method # @return [String] body of the last response # @!method headers # # Headers of last_response # # @return [Hash] hash of the last response # @!method status # # HTTP status of last_response # # @return [Integer] HTTP status of the last response # @!method errors # # Errors of last_response # # @return [Array] errors of the last response def_delegators :last_response, :body, :headers, :status, :errors # @!endgroup # @!group Class Methods delegated to app # @!method configure(*envs) {|_self| ... } # @!scope class # @yieldparam _self [Sinatra::Base] the object that the method was called on # # Set configuration options for Sinatra and/or the app. Allows scoping of # settings for certain environments. # @!method set(option, value = (not_set = true), ignore_setter = false, &block) # @!scope class # Sets an option to the given value. If the value is a proc, the proc will # be called every time the option is accessed. # @raise [ArgumentError] # @!method enable(*opts) # @!scope class # # Same as calling `set :option, true` for each of the given options. # @!method disable(*opts) # @!scope class # # Same as calling `set :option, false` for each of the given options. # @!method use(middleware, *args, &block) # @!scope class # Use the specified Rack middleware # @!method helpers(*extensions, &block) # @!scope class # # Makes the methods defined in the block and in the Modules given in # `extensions` available to the handlers and templates. # @!method register(*extensions, &block) # @!scope class # Register an extension. Alternatively take a block from which an # extension will be created and registered on the fly. def_delegators :app, :configure, :set, :enable, :disable, :use, :helpers, :register # @!endgroup # @!group Instance Methods delegated to current_session # @!method env_for(uri = "", opts = {}) # # Return the Rack environment used for a request to `uri`. # # @return [Hash] def_delegators :current_session, :env_for # @!endgroup # @!group Instance Methods delegated to rack_mock_session # @!method cookie_jar # # Returns a {https://www.rubydoc.info/github/rack/rack-test/Rack/Test/CookieJar Rack::Test::CookieJar}. # # @return [Rack::Test::CookieJar] def_delegators :rack_mock_session, :cookie_jar # @!endgroup # Instantiate and configure a mock Sinatra app. # # Takes a `base` app class, or defaults to Sinatra::Base, and instantiates # an app instance. Any given code in `block` is `class_eval`'d on this new # instance before the instance is returned. # # @param base [Sinatra::Base] App base class # # @return [Sinatra] Configured mocked app def mock_app(base = Sinatra::Base, &block) inner = nil @app = Sinatra.new(base) do inner = self class_eval(&block) end @settings = inner app end # Replaces the configured app. # # @param base [Sinatra::Base] a configured app def app=(base) @app = base end alias set_app app= # Returns a Rack::Lint-wrapped Sinatra app. # # If no app has been configured, a new subclass of Sinatra::Base will be # used and stored. # # (Rack::Lint validates your application and the requests and # responses according to the Rack spec.) # # @return [Sinatra::Base] def app @app ||= Class.new Sinatra::Base Rack::Lint.new @app end unless method_defined? :options # Processes an OPTIONS request in the context of the current session. # # @param uri [String] # @param params [Hash] # @param env [Hash] def options(uri, params = {}, env = {}, &block) env = env_for(uri, env.merge(method: 'OPTIONS', params: params)) current_session.send(:process_request, uri, env, &block) end end unless method_defined? :patch # Processes a PATCH request in the context of the current session. # # @param uri [String] # @param params [Hash] # @param env [Hash] def patch(uri, params = {}, env = {}, &block) env = env_for(uri, env.merge(method: 'PATCH', params: params)) current_session.send(:process_request, uri, env, &block) end end # @return [Boolean] def last_request? last_request true rescue Rack::Test::Error false end # @raise [Rack::Test:Error] If sessions are not enabled for app # @return [Hash] Session of last request, or the empty Hash def session return {} unless last_request? raise Rack::Test::Error, 'session not enabled for app' unless last_env['rack.session'] || app.session? last_request.session end # @return The env of the last request def last_env last_request.env end end end