module ThoughtBot::Shoulda::Controller::Macros

def should_assign_to(*names)

should_assign_to :user, :equals => '@user'
should_assign_to :user, :class => User
should_assign_to :user, :posts

Example:

the instance variable being checked.
* :equals - A string which is evaluated and compared for equality with
* :class - The expected class of the instance variable being checked.
Options:

each of the named instance variable(s).
Macro that creates a test asserting that the controller assigned to
def should_assign_to(*names)
  opts = names.extract_options!
  names.each do |name|
    test_name = "assign @#{name}"
    test_name << " as class #{opts[:class]}" if opts[:class]
    test_name << " which is equal to #{opts[:equals]}" if opts[:equals]
    should test_name do
      assigned_value = assigns(name.to_sym)
      assert assigned_value, "The action isn't assigning to @#{name}"
      assert_kind_of opts[:class], assigned_value if opts[:class]
      if opts[:equals]
        instantiate_variables_from_assigns do
          expected_value = eval(opts[:equals], self.send(:binding), __FILE__, __LINE__)
          assert_equal expected_value, assigned_value,
                       "Instance variable @#{name} expected to be #{expected_value}" + 
                       " but was #{assigned_value}"
        end
      end
    end
  end
end

def should_not_assign_to(*names)

should_not_assign_to :user, :posts

Example:

any of the named instance variable(s).
Macro that creates a test asserting that the controller did not assign to
def should_not_assign_to(*names)
  names.each do |name|
    should "not assign to @#{name}" do
      assert !assigns(name.to_sym), "@#{name} was visible"
    end
  end
end

def should_not_set_the_flash

@should_set_the_flash_to nil@
Macro that creates a test asserting that the flash is empty. Same as
def should_not_set_the_flash
  should_set_the_flash_to nil
end

def should_redirect_to(url)

should_redirect_to "users_url(@user)"
should_redirect_to '"/"'

Example:
set by the controller are available to the evaled string.
The given string is evaled to produce the resulting redirect path. All of the instance variables
Macro that creates a test asserting that the controller returned a redirect to the given path.
def should_redirect_to(url)
  should "redirect to #{url.inspect}" do
    instantiate_variables_from_assigns do
      assert_redirected_to eval(url, self.send(:binding), __FILE__, __LINE__)
    end
  end
end

def should_render_a_form

Macro that creates a test asserting that the rendered view contains a
element.
def should_render_a_form
  should "display a form" do
    assert_select "form", true, "The template doesn't contain a <form> element"
  end
end

def should_render_template(template)

should_render_template :new

Example:
Macro that creates a test asserting that the controller rendered the given template.
def should_render_template(template)
  should "render template #{template.inspect}" do
    assert_template template.to_s
  end
end

def should_render_with_layout(expected_layout = 'application')

should_render_with_layout 'special'

Example:
Macro that creates a test asserting that the controller rendered with the given layout.
def should_render_with_layout(expected_layout = 'application')
  if expected_layout
    should "render with #{expected_layout} layout" do
      response_layout = @response.layout.blank? ? "" : @response.layout.split('/').last
      assert_equal expected_layout, 
                   response_layout, 
                   "Expected to render with layout #{expected_layout} but was rendered with #{response_layout}"
    end
  else
    should "render without layout" do
      assert_nil @response.layout, 
                 "Expected no layout, but was rendered using #{@response.layout}"
    end
  end
end

def should_render_without_layout

Same as @should_render_with_layout false@
Macro that creates a test asserting that the controller rendered without a layout.
def should_render_without_layout
  should_render_with_layout nil
end

def should_respond_with(response)

should_respond_with :success

Example:
Macro that creates a test asserting that the controller responded with a 'response' status code.
def should_respond_with(response)
  should "respond with #{response}" do
    assert_response response
  end
end

def should_respond_with_content_type(content_type)

should_respond_with_content_type 'application/rss+xml'

Example:
Macro that creates a test asserting that the response content type was 'content_type'.
def should_respond_with_content_type(content_type)
  should "respond with content type of #{content_type}" do
    content_type = Mime::EXTENSION_LOOKUP[content_type.to_s].to_s if content_type.is_a? Symbol
    if content_type.is_a? Regexp
      assert_match content_type, @response.content_type, "Expected to match #{content_type} but was actually #{@response.content_type}"
    else
      assert_equal content_type, @response.content_type, "Expected #{content_type} but was actually #{@response.content_type}"
    end
  end
end

def should_return_from_session(key, expected)

should_return_from_session :message, '"Free stuff"'
should_return_from_session :user_id, '@user.id'

Example:
set by the controller are available to the evaled string.
The given string is evaled to produce the resulting redirect path. All of the instance variables
Macro that creates a test asserting that a value returned from the session is correct.
def should_return_from_session(key, expected)
  should "return the correct value from the session for key #{key}" do
    instantiate_variables_from_assigns do
      expected_value = eval(expected, self.send(:binding), __FILE__, __LINE__)
      assert_equal expected_value, session[key], "Expected #{expected_value.inspect} but was #{session[key]}"
    end
  end
end

def should_route(method, path, options)


should_route :get, '/posts/new', :action => :new
should_route :delete, '/posts/1', :action => :destroy, :id => 1
should_route :put, '/posts/1', :action => :update, :id => "1"
should_route :get, '/posts/1', :action => :show, :id => 1
should_route :post, '/posts', :controller => :posts, :action => :create
should_route :get, '/posts', :action => :index

Examples:

+to_param+ is called on the +options+ given.

based on the current test.
If you don't specify a :controller, it will try to guess the controller

given +options+.
+method+ on the given +path+, and asserts that it routes to the
Macro that creates a routing test. It tries to use the given HTTP
def should_route(method, path, options)
  unless options[:controller]
    options[:controller] = self.name.gsub(/ControllerTest$/, '').tableize
  end
  options[:controller] = options[:controller].to_s
  options[:action] = options[:action].to_s
  populated_path = path.dup
  options.each do |key, value|
    options[key] = value.to_param if value.respond_to? :to_param
    populated_path.gsub!(key.inspect, value.to_s)
  end
  should_name = "route #{method.to_s.upcase} #{populated_path} to/from #{options.inspect}"
  should should_name do
    assert_routing({:method => method, :path => populated_path}, options)
  end
end

def should_set_the_flash_to(val)

should_set_the_flash_to nil
should_set_the_flash_to /created/i
should_set_the_flash_to "Thank you for placing this order."

Example:

val can be a String, a Regex, or nil (indicating that the flash should not be set)
Macro that creates a test asserting that the flash contains the given value.
:section: Test macros
def should_set_the_flash_to(val)
  if val
    should "have #{val.inspect} in the flash" do
      assert_contains flash.values, val, ", Flash: #{flash.inspect}"
    end
  else
    should "not set the flash" do
      assert_equal({}, flash, "Flash was set to:\n#{flash.inspect}")
    end
  end
end