Skip to content

Commit

Permalink
Working to get riot-rails back into shape. About 50% there
Browse files Browse the repository at this point in the history
  • Loading branch information
gus committed Nov 27, 2009
1 parent 77afa70 commit 7bec969
Show file tree
Hide file tree
Showing 10 changed files with 270 additions and 372 deletions.
164 changes: 82 additions & 82 deletions lib/riot/action_controller/assertion_macros.rb
@@ -1,88 +1,88 @@
module Riot #:nodoc:
class Assertion
# Asserts that the body of the response equals or matches the expected expression. Expects actual to
# be the controller.
#
# controller.renders("a bunch of html")
# controller.renders(/bunch of/)
assertion(:renders) do |actual, expected|
actual_body = actual.response.body
if expected.kind_of?(Regexp) && actual_body =~ expected
fail("expected response body #{actual_body.inspect} to match #{expected.inspect}")
elsif expected == actual_body
fail("expected response body #{actual_body.inspect} to equal #{expected.inspect}")
else
pass
end
class Riot::Assertion
# Asserts that the body of the response equals or matches the expected expression. Expects actual to
# be the controller.
#
# controller.renders("a bunch of html")
# controller.renders(/bunch of/)
assertion(:renders) do |actual, expected|
actual_body = actual.response.body
if (expected.kind_of?(Regexp) ? (actual_body =~ expected) : (expected == actual_body))
pass
else
verb = expected.kind_of?(Regexp) ? "match" : "equal"
fail("expected response body #{actual_body.inspect} to #{verb} #{expected.inspect}")
end
end

# Asserts that the name you provide is the basename of the rendered template. For instance, if you
# expect the rendered template is named "foo_bar.html.haml" and you pass "foo_bar" into
# renders_template, the assertion would pass. If instead you pass "foo" into renders_template, the
# assertion will fail. Using Rails' assert_template both assertions would pass
#
# controlling :things
# controller.renders_template(:index)
# controller.renders_template("index")
# controller.renders_template("index.erb") # fails even if that's the name of the template
assertion(:renders_template) do |actual, expected_name|
name = expected_name.to_s
actual_template_path = actual.response.rendered[:template].to_s
actual_template_name = File.basename(actual_template_path)
msg = "expected template #{name.inspect}, not #{actual_template_path.inspect}"
actual_template_name.to_s.match(/^#{name}(\.\w+)*$/) ? pass : fail(msg)
end
# Asserts that the name you provide is the basename of the rendered template. For instance, if you
# expect the rendered template is named "foo_bar.html.haml" and you pass "foo_bar" into
# renders_template, the assertion would pass. If instead you pass "foo" into renders_template, the
# assertion will fail. Using Rails' assert_template both assertions would pass
#
# controlling :things
# controller.renders_template(:index)
# controller.renders_template("index")
# controller.renders_template("index.erb") # fails even if that's the name of the template
assertion(:renders_template) do |actual, expected_name|
name = expected_name.to_s
actual_template_path = actual.response.rendered[:template].to_s
actual_template_name = File.basename(actual_template_path)
msg = "expected template #{name.inspect}, not #{actual_template_path.inspect}"
actual_template_name.to_s.match(/^#{name}(\.\w+)*$/) ? pass : fail(msg)
end

# Asserts that the HTTP response code equals your expectation. You can use the symbolized form of the
# status code or the integer code itself. Not currently supporting status ranges; such as: +:success+,
# +:redirect+, etc.
#
# controller.response_code(:ok)
# controller.response_code(200)
#
# controller.response_code(:not_found)
# controller.response_code(404)
#
# # A redirect
# controller.response_code(:found)
# controller.response_code(302)
#
# See +ActionController::StatusCodes+ for the list of available codes.
assertion(:response_code) do |actual, expected_code|
if expected_code.kind_of?(Symbol)
expected_code = ::ActionController::StatusCodes::SYMBOL_TO_STATUS_CODE[expected_code]
end
actual_code = actual.response.response_code
expected_code == actual_code ? pass : fail("expected response code #{expected_code}, not #{actual_code}")
# Asserts that the HTTP response code equals your expectation. You can use the symbolized form of the
# status code or the integer code itself. Not currently supporting status ranges; such as: +:success+,
# +:redirect+, etc.
#
# controller.response_code(:ok)
# controller.response_code(200)
#
# controller.response_code(:not_found)
# controller.response_code(404)
#
# # A redirect
# controller.response_code(:found)
# controller.response_code(302)
#
# See +ActionController::StatusCodes+ for the list of available codes.
assertion(:response_code) do |actual, expected_code|
if expected_code.kind_of?(Symbol)
expected_code = ::ActionController::StatusCodes::SYMBOL_TO_STATUS_CODE[expected_code]
end
actual_code = actual.response.response_code
expected_code == actual_code ? pass : fail("expected response code #{expected_code}, not #{actual_code}")
end

# Asserts that the response from an action is a redirect and that the path or URL matches your
# expectations. If the response code is not in the 300s, the assertion will fail. If the reponse code
# is fine, but the redirect-to path or URL do not exactly match your expectation, the assertion will
# fail.
#
# +redirected_to+ expects you to provide your expected path in a block. This is so you can use named
# routes, which is - as it turns out - handy. It's also what I would expect to be able to do.
#
# controlling :people
# setup do
# post :create, :person { ... }
# end
#
# controller.redirected_to { person_path(...) }
#
# PS: There is a difference between saying +named_route_path+ and +named_route_url+ and Riot Rails will
# be very strict (read: annoying) about it :)
assertion(:redirected_to) do |actual, &expectation_block|
actual_response_code = actual.response.response_code
if (300...400).member?(actual_response_code)
expected_redirect = actual.url_for(situation.instance_eval(&expectation_block))
actual_redirect = actual.url_for(actual.response.redirected_to)
msg = "expected to redirect to <#{expected_redirect}>, not <#{actual_redirect}>"
expected_redirect == actual_redirect || fail(msg)
else
fail("expected response to be a redirect, but was #{actual_response_code}")
end
# Asserts that the response from an action is a redirect and that the path or URL matches your
# expectations. If the response code is not in the 300s, the assertion will fail. If the reponse code
# is fine, but the redirect-to path or URL do not exactly match your expectation, the assertion will
# fail.
#
# +redirected_to+ expects you to provide your expected path in a lambda. This is so you can use named
# routes, which are - as it turns out - handy. It's also what I would expect to be able to do. Using
# lambdas is not ideal, so if you're smart, solve this problem :)
#
# controlling :people
# setup do
# post :create, :person { ... }
# end
#
# controller.redirected_to( lambda { person_path(...) } )
#
# PS: There is a difference between saying +named_route_path+ and +named_route_url+ and Riot Rails will
# be very strict (read: annoying) about it :)
assertion(:redirected_to) do |actual, *expectings|
expectation_block = expectings.last
actual_response_code = actual.response.response_code
if (300...400).member?(actual_response_code)
expected_redirect = actual.url_for(actual.instance_eval(&expectation_block)) # need to execute in situation somehow
actual_redirect = actual.url_for(actual.response.redirected_to)
msg = "expected to redirect to <#{expected_redirect}>, not <#{actual_redirect}>"
expected_redirect == actual_redirect ? pass : fail(msg)
else
fail("expected response to be a redirect, but was #{actual_response_code}")
end
end # Assertion
end # Riot
end

end # Riot::Assertion
12 changes: 6 additions & 6 deletions lib/riot/action_controller/context_macros.rb
Expand Up @@ -28,16 +28,16 @@ def controlling(controller_name)
# controlling :foos
# setup { get :index }
#
# controller.response_status(:found)
# controller.redirected_to { new_foo_path }
# asserts_controller.response_status(:found)
# asserts_controller.redirected_to { new_foo_path }
# end
#
# Works the same as if you wrote the following assertions:
#
# asserts("controller") { @controller }.response_status(:found)
# asserts("controller") { @controller }.redirected_to { new_foo_path }
def controller
asserts("controller") { @controller }
# asserts("controller") { controller }.response_status(:found)
# asserts("controller") { controller }.redirected_to { new_foo_path }
def asserts_controller
asserts("controller") { controller }
end
end # ContextMacros

Expand Down
180 changes: 89 additions & 91 deletions lib/riot/active_record/assertion_macros.rb
@@ -1,103 +1,101 @@
module Riot
class Assertion
class Riot::Assertion

# An ActiveRecord assertion that expects to fail when a given attribute is validated after a nil value
# is provided to it.
#
# context "a User" do
# setup { User.new }
# topic.validates_presence_of(:name)
# end
assertion(:validates_presence_of) do |actual, attribute|
msg = "expected to validate presence of #{attribute.inspect}"
error_from_writing_value(actual, attribute, nil) ? pass : fail(msg)
end
# An ActiveRecord assertion that expects to fail when a given attribute is validated after a nil value
# is provided to it.
#
# context "a User" do
# setup { User.new }
# topic.validates_presence_of(:name)
# end
assertion(:validates_presence_of) do |actual, attribute|
msg = "expected to validate presence of #{attribute.inspect}"
error_from_writing_value(actual, attribute, nil) ? pass : fail(msg)
end

# An ActiveRecord assertion that expects to pass with a given value or set of values for a given
# attribute.
#
# context "a User" do
# setup { User.new }
# topic.allows_values_for :email, "a@b.cd"
# topic.allows_values_for :email, "a@b.cd", "e@f.gh"
# end
assertion(:allows_values_for) do |actual, attribute, *values|
bad_values = []
values.each do |value|
bad_values << value if error_from_writing_value(actual, attribute, value)
end
msg = "expected #{attribute.inspect} to allow value(s) #{bad_values.inspect}"
bad_values.empty? ? pass : fail(msg)
# An ActiveRecord assertion that expects to pass with a given value or set of values for a given
# attribute.
#
# context "a User" do
# setup { User.new }
# topic.allows_values_for :email, "a@b.cd"
# topic.allows_values_for :email, "a@b.cd", "e@f.gh"
# end
assertion(:allows_values_for) do |actual, attribute, *values|
bad_values = []
values.each do |value|
bad_values << value if error_from_writing_value(actual, attribute, value)
end
msg = "expected #{attribute.inspect} to allow value(s) #{bad_values.inspect}"
bad_values.empty? ? pass : fail(msg)
end

# An ActiveRecord assertion that expects to fail with a given value or set of values for a given
# attribute.
#
# context "a User" do
# setup { User.new }
# topic.does_not_allow_values_for :email, "a"
# topic.does_not_allow_values_for :email, "a@b", "e f@g.h"
# end
assertion(:does_not_allow_values_for) do |actual, attribute, *values|
good_values = []
values.each do |value|
good_values << value unless error_from_writing_value(actual, attribute, value)
end
msg = "expected #{attribute.inspect} not to allow value(s) #{good_values.inspect}"
good_values.empty? ? pass : fail(msg)
# An ActiveRecord assertion that expects to fail with a given value or set of values for a given
# attribute.
#
# context "a User" do
# setup { User.new }
# topic.does_not_allow_values_for :email, "a"
# topic.does_not_allow_values_for :email, "a@b", "e f@g.h"
# end
assertion(:does_not_allow_values_for) do |actual, attribute, *values|
good_values = []
values.each do |value|
good_values << value unless error_from_writing_value(actual, attribute, value)
end
msg = "expected #{attribute.inspect} not to allow value(s) #{good_values.inspect}"
good_values.empty? ? pass : fail(msg)
end

# An ActiveRecord assertion that expects to fail with an attribute is not valid for record because the
# value of the attribute is not unique. Requires the topic of the context to be a created record; one
# that returns false for a call to +new_record?+.
#
# context "a User" do
# setup { User.create(:email => "a@b.cde", ... ) }
# topic.validates_uniqueness_of :email
# end
assertion(:validates_uniqueness_of) do |actual, attribute|
actual_record = actual
if actual_record.new_record?
fail("topic is not a new record when testing uniqueness of #{attribute}")
else
copied_model = actual_record.class.new
actual_record.attributes.each do |dup_attribute, dup_value|
copied_model.write_attribute(dup_attribute, dup_value)
end
copied_value = actual_record.read_attribute(attribute)
msg = "expected to fail because #{attribute.inspect} is not unique"
error_from_writing_value(copied_model, attribute, copied_value) ? pass : fail(msg)
# An ActiveRecord assertion that expects to fail with an attribute is not valid for record because the
# value of the attribute is not unique. Requires the topic of the context to be a created record; one
# that returns false for a call to +new_record?+.
#
# context "a User" do
# setup { User.create(:email => "a@b.cde", ... ) }
# topic.validates_uniqueness_of :email
# end
assertion(:validates_uniqueness_of) do |actual, attribute|
actual_record = actual
if actual_record.new_record?
fail("topic is not a new record when testing uniqueness of #{attribute}")
else
copied_model = actual_record.class.new
actual_record.attributes.each do |dup_attribute, dup_value|
copied_model.write_attribute(dup_attribute, dup_value)
end
copied_value = actual_record.read_attribute(attribute)
msg = "expected to fail because #{attribute.inspect} is not unique"
error_from_writing_value(copied_model, attribute, copied_value) ? pass : fail(msg)
end
end

# An ActiveRecord assertion macro that expects to pass when a given attribute is defined as +has_many+
# association. Will fail if an association is not defined for the attribute and if the association is
# not +has_many.
#
# context "a Room" do
# setup { Room.new }
#
# topic.has_many(:doors)
# topic.has_many(:floors) # should probably fail given our current universe :)
# end
assertion(:has_many) do |actual, attribute|
reflection = actual.class.reflect_on_association(attribute)
static_msg = "expected #{attribute.inspect} to be a has_many association, but was "
if reflection.nil?
fail(static_msg + "not")
elsif "has_many" != reflection.macro.to_s
fail(static_msg + "a #{reflection.macro} instead")
else
pass
end
# An ActiveRecord assertion macro that expects to pass when a given attribute is defined as +has_many+
# association. Will fail if an association is not defined for the attribute and if the association is
# not +has_many.jekyll
#
# context "a Room" do
# setup { Room.new }
#
# topic.has_many(:doors)
# topic.has_many(:floors) # should probably fail given our current universe :)
# end
assertion(:has_many) do |actual, attribute|
reflection = actual.class.reflect_on_association(attribute)
static_msg = "expected #{attribute.inspect} to be a has_many association, but was "
if reflection.nil?
fail(static_msg + "not")
elsif "has_many" != reflection.macro.to_s
fail(static_msg + "a #{reflection.macro} instead")
else
pass
end
private
end
private

def error_from_writing_value(model, attribute, value)
model.write_attribute(attribute, value)
model.valid?
model.errors.on(attribute)
end
def error_from_writing_value(model, attribute, value)
model.write_attribute(attribute, value)
model.valid?
model.errors.on(attribute)
end

end # Assertion
end # Riot
end # Riot::Assertion
2 changes: 1 addition & 1 deletion riot_rails.gemspec
Expand Up @@ -9,7 +9,7 @@ Gem::Specification.new do |s|

s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Justin 'Gus' Knowlden"]
s.date = %q{2009-10-18}
s.date = %q{2009-11-22}
s.description = %q{Riot specific test support for Rails apps. Protest the slow app.}
s.email = %q{gus@gusg.us}
s.extra_rdoc_files = [
Expand Down

0 comments on commit 7bec969

Please sign in to comment.