require 'merb/test/fake_request' require 'merb/test/hpricot' require 'merb/test/multipart' include HpricotTestHelper module Merb module Test module Helper include Merb::GeneralControllerMixin # Create a FakeRequest suitable for passing to Controller.build def fake_request(params = {}) method = method.to_s.upcase Merb::Test::FakeRequest.with("/", {:request_method => "GET"}.merge(params)) end # For integration/functional testing # # This helper is the basis of the get, post, put, and delete helper # # By default a fake request is yielded to the block for local modification. # +opts+ takes any options that you want pass to the methods, plus some reserved ones # # ===Yielding # You can get the request helper to yield either a fake request object, or a controller # Do this with the +:yield+ option. Values can be +:request+, or +:controller+. +:request+ is default # When you yield the controller, it is available inside the block with the controller method, so you don't need to # explicitly set it in the block chute. # ====Example # request( :get, '/', :yields => :controller) do |controller| # controller.stub!(:render) # end # # You can also pass in a fake request object which may be useful if your yielding a controller. # Use the opts[:fake_request] to do this. # ====Example # request( :get, '/', :yields => :controller, :fake_request => @my_fake_request) do # controller.stub!(:render) # end def request(verb, path, opts = {}, &block) response = StringIO.new request = opts.delete(:fake_request) || Merb::Test::FakeRequest.with(path, opts.merge(:request_method => (verb.to_s.upcase rescue 'GET'))) yield_to_controller = opts.delete(:yields) if yield_to_controller == :controller request_yielding_controller(request, response, &block) else request_yielding_request(request, response, &block) end end # Makes a get request routed to +path+ with any options encoded into the # request url # Example # {{{ # get "/users", :user => {:login => "dave", :email => "email@example.com"} # }}} def get(path, opts = {}, &block) request("GET", path, opts, &block) end # Makes a post request routed to +path+ with any options encoded into the # request url # Example # {{{ # post "/users", :user => {:login => "dave", :email => "email@example.com"} # }}} def post(path, opts = {}, &block) request("POST",path, opts, &block) end # Makes a put request routed to +path+ with any options encoded into the # request url # Example # {{{ # put "/users/1", :user => {:login => "dave", :email => "email@example.com"} # }}} def put(path,opts = {}, &block) request("PUT",path, opts, &block) end # Makes a delete request routed to +path+ with any options encoded into the request url # Example # {{{ # delete "/users/1", :user => {:login => "dave", :email => "email@example.com"} # }}} def delete(path, opts= {}, &block) request("DELETE",path, opts, &block) end # Posts multipart form data to a path # pass the +path+ to send and the parameters. For file uploads, just include a file as the option value. # ===Example # multipart_post("/my_collection", :foo => "bar", :user => { :login => "joe", :image => File.open("my_image.png")}) def multipart_post(path, params = {}, &block) multipart_request(path, params.merge!(:request_method => 'POST'), &block) end # Posts multipart form data to a path # Same as +multipart_post+ but used for PUT(ting) data to the server def multipart_put(path, params = {}, &block) multipart_request(path, params.merge!(:request_method => 'PUT'), &block) end def controller @controller end [:body, :status, :params, :cookies, :headers, :session, :response, :route].each do |method| define_method method do controller ? controller.send(method) : nil end end # Checks that a route is made to the correct controller etc # # === Example # with_route("/pages/1", "PUT") do |params| # params[:controller].should == "pages" # params[:action].should == "update" # params[:id].should == "1" # end def with_route(the_path, _method = "GET") _fake_request = Merb::Test::FakeRequest.with(the_path, :request_method => _method) result = Merb::Router.match(_fake_request, {}) yield result[1] if block_given? result end def fixtures(*files) files.each do |name| klass = Kernel::const_get(Inflector.classify(Inflector.singularize(name))) entries = YAML::load_file(File.dirname(__FILE__) + "/fixtures/#{name}.yaml") entries.each do |name, entry| klass::create(entry) end end end # Dispatches an action to a controller. Defaults to index. # The opts hash, if provided will act as the params hash in the controller # and the params method in the controller is infact the provided opts hash # This controller is based on a fake_request and does not go through the router # # === Simple Example # {{{ # controller, result = dispatch_to(Pages, :show, :id => 1, :title => "blah") # }}} # # === Complex Example # By providing a block to the dispatch_to method, the controller may be stubbed or mocked prior to the # actual dispatch action being called. # {{{ # controller, result = dispatch_to(Pages, :show, :id => 1) do |controller| # controller.stub!(:render).and_return("rendered response") # end # }}} def dispatch_to(controller, action = :index, opts = {}) klass = controller.class == Class ? controller : controller.class @controller = klass.build(fake_request) @controller.stub!(:params).and_return(opts.merge(:controller => klass.name.downcase, :action => action.to_s).to_mash) yield @controller if block_given? [@controller, @controller.dispatch(action.to_sym)] end def path_with_options(path, opts) path = path << "?" << params_to_query_string(opts) unless opts.empty? path end protected def request_yielding_request(request, response, &block) # response = StringIO.new # @request = Merb::Test::FakeRequest.with(path, :request_method => (verb.to_s.upcase rescue 'GET')) @request = request yield @request if block_given? @controller, @action = Merb::Dispatcher.handle @request, response end def request_yielding_controller(request, response, &block) # response = StringIO.new # @request = Merb::Test::FakeRequest.with(path, :request_method => (verb.to_s.upcase rescue 'GET')) @request = Merb::Request.new(request) check_request_for_route(@request) dispatch_fake_request(@request, response, &block) end def multipart_request(path, params = {}, &block) response = StringIO.new request = request_with_multipart_params(path, params) check_request_for_route(request) dispatch_fake_request(request, response, &block) end def check_request_for_route(request) if request.route_params.empty? raise ControllerExceptions::BadRequest, "No routes match the request" elsif request.controller_name.nil? raise ControllerExceptions::BadRequest, "Route matched, but route did not specify a controller" end end # Used for yielding a controller with request and multipart helpers def dispatch_fake_request(request, response, status = 200, &block) klass = request.controller_class @controller = klass.build(request, response, status) @controller.send(:setup_session) @controller.stub!(:setup_session).and_return(true) yield @controller if block_given? @controller.dispatch(request.action) [@controller, request.action] rescue => exception exception = Dispatcher.send(:controller_exception, exception) @controller, @action = Dispatcher.send(:dispatch_exception, request, response, exception) end def request_with_multipart_params(path, params = {}) request_method = params.delete(:request_method) || "GET" request = Merb::Test::FakeRequest.new(:request_uri => path) m = Merb::Test::Multipart::Post.new(params) body, head = m.to_multipart request['REQUEST_METHOD'] = request_method request['CONTENT_TYPE'] = head request['CONTENT_LENGTH'] = body.length request.post_body = body Merb::Request.new(request) end end end end class Object # Checks that an object has assigned an instance variable of name # :name # # ===Example in a spec # @my_obj.assings(:my_value).should == @my_value def assigns(attr) self.instance_variable_get("@#{attr}") end end