Last active
August 27, 2023 19:25
-
-
Save dfl/58c00d8732b633b869b8686c8db1696e to your computer and use it in GitHub Desktop.
should_respond_with shoulda helper
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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