aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_controller/template_assertions.rb
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib/action_controller/template_assertions.rb')
-rw-r--r--actionpack/lib/action_controller/template_assertions.rb188
1 files changed, 188 insertions, 0 deletions
diff --git a/actionpack/lib/action_controller/template_assertions.rb b/actionpack/lib/action_controller/template_assertions.rb
new file mode 100644
index 0000000000..304012d24d
--- /dev/null
+++ b/actionpack/lib/action_controller/template_assertions.rb
@@ -0,0 +1,188 @@
+module ActionController
+ module TemplateAssertions
+ extend ActiveSupport::Concern
+
+ included do
+ setup :setup_subscriptions
+ teardown :teardown_subscriptions
+ end
+
+ RENDER_TEMPLATE_INSTANCE_VARIABLES = %w{partials templates layouts files}.freeze
+
+ def setup_subscriptions
+ RENDER_TEMPLATE_INSTANCE_VARIABLES.each do |instance_variable|
+ instance_variable_set("@_#{instance_variable}", Hash.new(0))
+ end
+
+ @_subscribers = []
+
+ @_subscribers << ActiveSupport::Notifications.subscribe("render_template.action_view") do |_name, _start, _finish, _id, payload|
+ path = payload[:layout]
+ if path
+ @_layouts[path] += 1
+ if path =~ /^layouts\/(.*)/
+ @_layouts[$1] += 1
+ end
+ end
+ end
+
+ @_subscribers << ActiveSupport::Notifications.subscribe("!render_template.action_view") do |_name, _start, _finish, _id, payload|
+ if virtual_path = payload[:virtual_path]
+ partial = virtual_path =~ /^.*\/_[^\/]*$/
+
+ if partial
+ @_partials[virtual_path] += 1
+ @_partials[virtual_path.split("/").last] += 1
+ end
+
+ @_templates[virtual_path] += 1
+ else
+ path = payload[:identifier]
+ if path
+ @_files[path] += 1
+ @_files[path.split("/").last] += 1
+ end
+ end
+ end
+ end
+
+ def teardown_subscriptions
+ @_subscribers.each do |subscriber|
+ ActiveSupport::Notifications.unsubscribe(subscriber)
+ end
+ end
+
+ def process(*args)
+ reset_template_assertion
+ super
+ end
+
+ def reset_template_assertion
+ RENDER_TEMPLATE_INSTANCE_VARIABLES.each do |instance_variable|
+ ivar_name = "@_#{instance_variable}"
+ if instance_variable_defined?(ivar_name)
+ instance_variable_get(ivar_name).clear
+ end
+ end
+ end
+
+ # Asserts that the request was rendered with the appropriate template file or partials.
+ #
+ # # assert that the "new" view template was rendered
+ # assert_template "new"
+ #
+ # # assert that the exact template "admin/posts/new" was rendered
+ # assert_template %r{\Aadmin/posts/new\Z}
+ #
+ # # assert that the layout 'admin' was rendered
+ # assert_template layout: 'admin'
+ # assert_template layout: 'layouts/admin'
+ # assert_template layout: :admin
+ #
+ # # assert that no layout was rendered
+ # assert_template layout: nil
+ # assert_template layout: false
+ #
+ # # assert that the "_customer" partial was rendered twice
+ # assert_template partial: '_customer', count: 2
+ #
+ # # assert that no partials were rendered
+ # assert_template partial: false
+ #
+ # # assert that a file was rendered
+ # assert_template file: "README.rdoc"
+ #
+ # # assert that no file was rendered
+ # assert_template file: nil
+ # assert_template file: false
+ #
+ # In a view test case, you can also assert that specific locals are passed
+ # to partials:
+ #
+ # # assert that the "_customer" partial was rendered with a specific object
+ # assert_template partial: '_customer', locals: { customer: @customer }
+ def assert_template(options = {}, message = nil)
+ # Force body to be read in case the template is being streamed.
+ response.body
+
+ case options
+ when NilClass, Regexp, String, Symbol
+ options = options.to_s if Symbol === options
+ rendered = @_templates
+ msg = message || sprintf("expecting <%s> but rendering with <%s>",
+ options.inspect, rendered.keys)
+ matches_template =
+ case options
+ when String
+ !options.empty? && rendered.any? do |t, num|
+ options_splited = options.split(File::SEPARATOR)
+ t_splited = t.split(File::SEPARATOR)
+ t_splited.last(options_splited.size) == options_splited
+ end
+ when Regexp
+ rendered.any? { |t,num| t.match(options) }
+ when NilClass
+ rendered.blank?
+ end
+ assert matches_template, msg
+ when Hash
+ options.assert_valid_keys(:layout, :partial, :locals, :count, :file)
+
+ if options.key?(:layout)
+ expected_layout = options[:layout]
+ msg = message || sprintf("expecting layout <%s> but action rendered <%s>",
+ expected_layout, @_layouts.keys)
+
+ case expected_layout
+ when String, Symbol
+ assert_includes @_layouts.keys, expected_layout.to_s, msg
+ when Regexp
+ assert(@_layouts.keys.any? {|l| l =~ expected_layout }, msg)
+ when nil, false
+ assert(@_layouts.empty?, msg)
+ else
+ raise ArgumentError, "assert_template only accepts a String, Symbol, Regexp, nil or false for :layout"
+ end
+ end
+
+ if options[:file]
+ assert_includes @_files.keys, options[:file]
+ elsif options.key?(:file)
+ assert @_files.blank?, "expected no files but #{@_files.keys} was rendered"
+ end
+
+ if expected_partial = options[:partial]
+ if expected_locals = options[:locals]
+ if defined?(@_rendered_views)
+ view = expected_partial.to_s.sub(/^_/, '').sub(/\/_(?=[^\/]+\z)/, '/')
+
+ partial_was_not_rendered_msg = "expected %s to be rendered but it was not." % view
+ assert_includes @_rendered_views.rendered_views, view, partial_was_not_rendered_msg
+
+ msg = 'expecting %s to be rendered with %s but was with %s' % [expected_partial,
+ expected_locals,
+ @_rendered_views.locals_for(view)]
+ assert(@_rendered_views.view_rendered?(view, options[:locals]), msg)
+ else
+ warn "the :locals option to #assert_template is only supported in a ActionView::TestCase"
+ end
+ elsif expected_count = options[:count]
+ actual_count = @_partials[expected_partial]
+ msg = message || sprintf("expecting %s to be rendered %s time(s) but rendered %s time(s)",
+ expected_partial, expected_count, actual_count)
+ assert(actual_count == expected_count.to_i, msg)
+ else
+ msg = message || sprintf("expecting partial <%s> but action rendered <%s>",
+ options[:partial], @_partials.keys)
+ assert_includes @_partials, expected_partial, msg
+ end
+ elsif options.key?(:partial)
+ assert @_partials.empty?,
+ "Expected no partials to be rendered"
+ end
+ else
+ raise ArgumentError, "assert_template only accepts a String, Symbol, Hash, Regexp, or nil"
+ end
+ end
+ end
+end