module Spec
module Rails
module Example
# View Examples live in $RAILS_ROOT/spec/views/.
#
# View Specs use Spec::Rails::Example::ViewExampleGroup,
# which provides access to views without invoking any of your controllers.
# See Spec::Rails::Expectations::Matchers for information about specific
# expectations that you can set on views.
#
# == Example
#
# describe "login/login" do
# before do
# render 'login/login'
# end
#
# it "should display login form" do
# response.should have_tag("form[action=/login]") do
# with_tag("input[type=text][name=email]")
# with_tag("input[type=password][name=password]")
# with_tag("input[type=submit][value=Login]")
# end
# end
# end
class ViewExampleGroup < FunctionalExampleGroup
before(:each) do
ensure_that_flash_and_session_work_properly
end
after(:each) do
ensure_that_base_view_path_is_not_set_across_example_groups
end
def initialize(defined_description, options={}, &implementation) #:nodoc:
super
@controller_class_name = "Spec::Rails::Example::ViewExampleGroupController"
end
def ensure_that_flash_and_session_work_properly #:nodoc:
@controller.send :initialize_template_class, @response
@controller.send :assign_shortcuts, @request, @response
@session = @controller.session
@controller.class.send :public, :flash
end
def ensure_that_base_view_path_is_not_set_across_example_groups #:nodoc:
ActionView::Base.base_view_path = nil
end
def set_base_view_path(options) #:nodoc:
ActionView::Base.base_view_path = base_view_path(options)
end
def base_view_path(options) #:nodoc:
"/#{derived_controller_name(options)}/"
end
def derived_controller_name(options) #:nodoc:
parts = subject_of_render(options).split('/').reject { |part| part.empty? }
"#{parts[0..-2].join('/')}"
end
def derived_action_name(options) #:nodoc:
parts = subject_of_render(options).split('/').reject { |part| part.empty? }
"#{parts.last}".split('.').first
end
def subject_of_render(options) #:nodoc:
[:template, :partial, :file].each do |render_type|
if options.has_key?(render_type)
return options[render_type]
end
end
return ""
end
def add_helpers(options) #:nodoc:
@controller.add_helper("application")
@controller.add_helper(derived_controller_name(options))
@controller.add_helper(options[:helper]) if options[:helper]
options[:helpers].each { |helper| @controller.add_helper(helper) } if options[:helpers]
end
# Renders a template for a View Spec, which then provides access to the result
# through the +response+. Also supports render with :inline, which you can
# use to spec custom form builders, helpers, etc, in the context of a view.
#
# == Examples
#
# render('/people/list')
# render('/people/list', :helper => MyHelper)
# render('/people/list', :helpers => [MyHelper, MyOtherHelper])
# render(:partial => '/people/_address')
# render(:inline => "<% custom_helper 'argument', 'another argument' %>")
#
# See Spec::Rails::Example::ViewExampleGroup for more information.
def render(*args)
options = Hash === args.last ? args.pop : {}
options[:template] = args.first.to_s unless args.empty?
set_base_view_path(options)
add_helpers(options)
assigns[:action_name] = @action_name
@request.path_parameters = @request.path_parameters.update(
:controller => options[:controller] || derived_controller_name(options),
:action => options[:action] || derived_action_name(options)
)
defaults = { :layout => false }
options = defaults.merge options
@controller.send(:params).reverse_merge! @request.parameters
@controller.send :initialize_current_url
@controller.class.instance_eval %{
def controller_path
"#{derived_controller_name(options)}"
end
def controller_name
"#{derived_controller_name(options).split('/').last}"
end
}
@controller.send :forget_variables_added_to_assigns
@controller.send :render, options
@controller.send :process_cleanup
end
# This provides the template. Use this to set mock
# expectations for dealing with partials
#
# == Example
#
# describe "/person/new" do
# it "should use the form partial" do
# template.should_receive(:render).with(:partial => 'form')
# render "/person/new"
# end
# end
def template
@controller.template
end
Spec::Example::ExampleGroupFactory.register(:view, self)
protected
def _assigns_hash_proxy
@_assigns_hash_proxy ||= AssignsHashProxy.new self do
@response.template
end
end
end
class ViewExampleGroupController < ApplicationController #:nodoc:
include Spec::Rails::Example::RenderObserver
attr_reader :template
def add_helper_for(template_path)
add_helper(template_path.split('/')[0])
end
def add_helper(name)
begin
helper_module = "#{name}_helper".camelize.constantize
rescue
return
end
(class << template; self; end).class_eval do
include helper_module
end
end
def forget_variables_added_to_assigns
end
end
end
end
end