Skip to content

Instantly share code, notes, and snippets.

@dfl
Last active August 27, 2023 19:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dfl/58c00d8732b633b869b8686c8db1696e to your computer and use it in GitHub Desktop.
Save dfl/58c00d8732b633b869b8686c8db1696e to your computer and use it in GitHub Desktop.
should_respond_with shoulda helper
# test helpers for integration testing written by David Lowenfels
# place in lib/internaut/shoulda_extensions.rb
require "rails-dom-testing" # for css_select method
## Example usage
module Internaut
module ShouldaExtensions
extend ActiveSupport::Concern
def assert_match_html(regex, html)
msg = message(msg) { "#{mu_pp(regex)} expected to match\n #{html}" }
assert html =~ regex, msg
end
# basic: `assert_body_matches /foo/`
# blank: `assert_body_matches /foo/ => false`
# complex: `assert_body_matches { /bad_thing/: false, "div#good": true, "p#main.foo": /bar/ }`
# interpolation: `assert_body_matches { "p#main.foo": ->{ /some {@thing}/ }, ->{/another {@interp}/ => false }`
def assert_body_matches(arg)
html = @response.body
if arg.is_a?(Hash)
# example: assert_body_matches { /match/ => false, "p#main.foo" => /bar/, "p#main.baz" => false }
arg.each do |k, v|
k = instance_exec(&k) if k.respond_to?(:call)
v = instance_exec(&v) if v.respond_to?(:call)
if k.is_a?(Regexp)
case v
when true
assert_match_html k, html
when false
msg = message(msg) { "<#{mu_pp(k)}> expected to not match\n<#{mu_pp(html)}>" }
assert k !~ html, msg
else
raise ArgumentError, "value for regex key must be boolean!"
end
else
selection = css_select(k.to_s)
case v
when true
assert !selection.blank?, "#{k.inspect} should be present in DOM #{html}"
when false
assert selection.blank?, "#{k.inspect} should not be present in DOM #{html}"
else
assert_match_html _make_regexp(v), selection.map(&:to_s).join
end
end
end
else
# example: assert_body_matches /foo/
assert_match_html _make_regexp(arg), html
end
end
## Class Methods
class_methods do
def should_respond_matching(opts)
should "match #{opts.inspect} in the response body" do
# execute @action implicitly if not already called explicitly
@action.call if @response.nil? && @action&.respond_to?(:call)
assert_response :success
assert_body_matches opts
end
end
# example: should_respond_with :success, matching: { "p#main.foo": ->{ /some {@thing}/ }, /unwanted/ => false }
def should_respond_with(code, opts = {}, &block)
# redirect: ->{ some_path }
if code.is_a?(Hash) && code[:redirect]
redirect_path = code[:redirect]
code = :redirect
end
@title = "respond with :#{code}"
if _mime = opts[:mime_type]
@title += " as #{_mime}"
end
if _template = opts[:template]
@title += " with template #{_template.inspect}"
end
if _body = opts[:matching]
if _body.is_a?(Hash)
# get proc source code if needed
str = _body.map { |k, v| "#{inspect_arg(k)}: #{inspect_arg(v)}" }.join(", ")
else
str = _body.inspect
end
@title += " matching #{str}"
end
if _flash = opts[:flash]
@title += " with flash#{_flash.inspect}"
end
should @title do
if @response.nil? && @action
# execute @action implicitly if not already called explicitly
@action.call if @action.respond_to?(:call)
else
instance_exec(&block) if block_given?
end
# evaluate remaining lambda arguments
%i[template].each do |k|
if var = opts[k]
opts[k] = instance_exec(&var) if var.respond_to?(:call)
end
end
if redirect_path
redirect_path = instance_exec(&redirect_path) if redirect_path.respond_to?(:call)
assert_redirected_to redirect_path
else
assert_response code
end
assert_equal Mime[_mime], response.media_type if _mime = opts[:mime_type]
assert_template _template if _template = opts[:template]
assert_body_matches _body if _body = opts[:matching]
_flash.each { |k, v| assert_match /#{Regexp.quote(v)}/, flash[k] } if _flash = opts[:flash]
end
end
def inspect_arg(arg)
# display source code if proc
return arg.source if arg.respond_to?(:call)
arg.inspect
end
protected
def append_title(msg, arg)
if arg.is_a?(Proc)
@title = arg.source.gsub(/^\s+/, "").gsub(/should_respond_with/, "respond with")
else
@title += " #{msg} #{arg}"
end
end
end
protected
def _make_regexp(arg)
case arg
when Array
Regexp.union(*arg)
when String
Regexp.new(Regexp.escape(arg))
when Regexp
arg
else
raise ArgumentError, "expected String, Regexp, or Array, got #{arg.inspect}"
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment