From feeca283b43041e9f7b503f843571fb32b6664e4 Mon Sep 17 00:00:00 2001 From: Eric Allen Date: Mon, 4 May 2009 00:06:46 -0400 Subject: [PATCH] Upgrade skinny_spec to deal with newer version of RSpec due to unfreezing. --- vendor/plugins/skinny_spec/README.rdoc | 39 +-- .../additional/helper_overrides.txt | 2 +- .../skinny_scaffold_generator.rb | 9 + .../templates/controller_spec.rb | 23 -- .../templates/form.html_spec.rb | 7 +- .../templates/index.html_spec.rb | 4 +- .../templates/index_partial.html_spec.rb | 3 +- .../skinny_scaffold/templates/model_spec.rb | 2 +- .../templates/show.html_spec.rb | 3 +- .../lib/lucky_sneaks/common_spec_helpers.rb | 41 ++- .../controller_request_helpers.rb | 8 +- .../lucky_sneaks/controller_spec_helpers.rb | 192 ++++++++++++-- .../lucky_sneaks/controller_stub_helpers.rb | 141 ++++++---- .../lib/lucky_sneaks/model_spec_helpers.rb | 240 +++++++++++++++--- .../lib/lucky_sneaks/view_spec_helpers.rb | 173 +++++++++++-- .../lib/lucky_sneaks/view_stub_helpers.rb | 15 ++ vendor/plugins/skinny_spec/lib/skinny_spec.rb | 4 +- 17 files changed, 703 insertions(+), 203 deletions(-) create mode 100644 vendor/plugins/skinny_spec/lib/lucky_sneaks/view_stub_helpers.rb diff --git a/vendor/plugins/skinny_spec/README.rdoc b/vendor/plugins/skinny_spec/README.rdoc index 49cfbf60..118b58e1 100644 --- a/vendor/plugins/skinny_spec/README.rdoc +++ b/vendor/plugins/skinny_spec/README.rdoc @@ -39,12 +39,6 @@ specs) to see what's new and different with Skinny Spec. Let's look at the controller specs. describe UsersController do - def valid_attributes(args = {}) - { - # Add valid attributes for the your params[:user] here! - }.merge(args) - end - describe "GET :index" do before(:each) do @users = stub_index(User) @@ -68,12 +62,7 @@ Let's look at the controller specs. # ... -First thing you should see is a method definition for -valid_attributes. This will be used later by the create and update -specs to more accurately represent how the controller works in actual practice by supplying -somewhat real data for the params coming from the HTML forms. - -Next we find an example group for GET :index. That stub_index method there +First thing you should see is an example group for GET :index. That stub_index method there does a lot of work behind the curtain. I'll leave it up to you to check the documentation for it (and its brothers and sister methods like stub_new) but I will point out that the methods named stub_controller_method should only be used for stubbing and @@ -82,8 +71,16 @@ use stub_find_all, stub_find_one, and stub_initialize for this is because the former methods actually save us a step by defining an implicit controller method request. If you add a new method to your resource routing, you'll want to use the helper method define_request in those example groups to define an explicit -request. You can also define a method called shared_request to "share a -define_request" across shared describe blocks, like so: +request, like so: + + describe "PUT :demote" do + define_request { put :demote } + + # ... + end + +You can also define a method called shared_request to "share" a +define_request across nested describe blocks, like so: describe "POST :create" do def shared_request @@ -100,16 +97,22 @@ request. You can also define a method called shared_request to "share a end Note: When you're adding longer, more complicated controller specs you can still leverage -implicit and shared requests by calling do_request in your spec as in the following +implicit and explicit requests by calling do_request in your spec as in the following example: - # Let's assume this controller is _not_ CategoriesController + # Note this controller is UsersController and _not_ CategoriesController # and that loading the categories isn't part of the default actions - describe "GET :index" do + # and cannot use the stub_controller_method helpers + # [which create implicit requests based on the controller method in the name] + # but uses stub_find_all instead + describe "GET :new" do before(:each) do + @user = stub_new(User) @categories = stub_find_all(Category) end + # ... + it "should preload categories" do Category.should_receive(:find).with(:all) do_request @@ -264,4 +267,4 @@ rspec_on_rails_on_crack[http://github.com/technoweenie/rspec_on_rails_on_crack/t Also thanks and props to Hampton Catlin and Nathan Weizenbaum for the lovely and imminently useable Haml and make_resourceful. Also also praises and glory to David Chelimsky and the Rspec crew. -Also thanks to Don Petersen for his suggestions and fixes. \ No newline at end of file +Also thanks to Don Petersen, Nicolas Mérouze, Mikkel Malmberg, and Brandan Lennox for their suggestions and fixes. \ No newline at end of file diff --git a/vendor/plugins/skinny_spec/additional/helper_overrides.txt b/vendor/plugins/skinny_spec/additional/helper_overrides.txt index a15ec2d3..7717e079 100644 --- a/vendor/plugins/skinny_spec/additional/helper_overrides.txt +++ b/vendor/plugins/skinny_spec/additional/helper_overrides.txt @@ -33,7 +33,7 @@ def button_to(name, options = {}, html_options = {}) html_options.merge!("type" => "submit", "value" => name) - "
" + + "
" + method_tag + content_tag("button", name, html_options) + request_token_tag + "
" end diff --git a/vendor/plugins/skinny_spec/generators/skinny_scaffold/skinny_scaffold_generator.rb b/vendor/plugins/skinny_spec/generators/skinny_scaffold/skinny_scaffold_generator.rb index 7f6f2b25..3cde3564 100644 --- a/vendor/plugins/skinny_spec/generators/skinny_scaffold/skinny_scaffold_generator.rb +++ b/vendor/plugins/skinny_spec/generators/skinny_scaffold/skinny_scaffold_generator.rb @@ -6,6 +6,8 @@ class SkinnyScaffoldGenerator < Rails::Generator::NamedBase alias_method :controller_singular_name, :controller_file_name alias_method :controller_table_name, :controller_plural_name + default_options :skip_migration => false + def initialize(runtime_args, runtime_options = {}) super @@ -86,6 +88,13 @@ protected def banner "Usage: #{$0} skinny_scaffold ModelName [field:type, field:type]" end + + def add_options!(opt) + opt.separator '' + opt.separator 'Options:' + opt.on("--skip-migration", + "Don't generate a migration file for this model") { |v| options[:skip_migration] = v } + end def model_name class_name.demodulize diff --git a/vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/controller_spec.rb b/vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/controller_spec.rb index dc6d5599..ff1b6b29 100644 --- a/vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/controller_spec.rb +++ b/vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/controller_spec.rb @@ -1,29 +1,6 @@ require File.dirname(__FILE__) + '/../spec_helper' describe <%= controller_class_name %>Controller do - def valid_attributes(args = {}) - { -<% if attributes.empty? -%> - # Add valid attributes for the your params[:<%= singular_name %>] here! -<% else -%> - <%- attributes.each_with_index do |attribute, index| -%> - <%- case attribute.type -%> - <%- when :string, :text -%> - "<%= attribute.name %>" => "foo"<%= index < attributes.size - 1 ? "," : "" %> - <%- when :integer, :float, :decimal -%> - "<%= attribute.name %>" => 815<%= index < attributes.size - 1 ? "," : "" %> - <%- when :boolean -%> - "<%= attribute.name %>" => false<%= index < attributes.size - 1 ? "," : "" %> - <%- when :date, :datetime, :time, :timestamp -%> - "<%= attribute.name %>" => 1.week.ago<%= index < attributes.size - 1 ? "," : "" %> - <%- else -%> - "<%= attribute.name %>" => nil<%= index < attributes.size - 1 ? "," : "" %> # Could not determine valid attribute - <%- end -%> - <%- end -%> -<% end -%> - }.merge(args) - end - describe "GET :index" do before(:each) do @<%= plural_name %> = stub_index(<%= class_name %>) diff --git a/vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/form.html_spec.rb b/vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/form.html_spec.rb index acdf2d01..bc6f6f24 100644 --- a/vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/form.html_spec.rb +++ b/vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/form.html_spec.rb @@ -5,11 +5,10 @@ describe "<%= File.join(controller_class_path, controller_singular_name) %>/form @<%= singular_name %> = mock_and_assign(<%= model_name %>, :stub => { <% if attributes.blank? -%> # Add your stub attributes and return values here like: - # :name => "Foo", :created_at => 1.week.ago, :updated_at => nil + # :name => "Foo", :address => "815 Oceanic Drive" <% else -%> <%- attributes.each_with_index do |attribute, index| -%> - <%- case attribute.type -%> - <%- when :string, :text -%> + <%- case attribute.type when :string, :text -%> :<%= attribute.name %> => "foo"<%= index < attributes.size - 1 ? "," : "" %> <%- when :integer, :float, :decimal -%> :<%= attribute.name %> => 815<%= index < attributes.size - 1 ? "," : "" %> @@ -21,7 +20,7 @@ describe "<%= File.join(controller_class_path, controller_singular_name) %>/form :<%= attribute.name %> => nil<%= index < attributes.size - 1 ? "," : "" %> # Could not determine valid attribute <%- end -%> <%- end -%> -<% end -%> +<% end -%> }) end diff --git a/vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/index.html_spec.rb b/vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/index.html_spec.rb index 1b3e09d0..cfe213f5 100644 --- a/vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/index.html_spec.rb +++ b/vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/index.html_spec.rb @@ -3,11 +3,11 @@ require File.dirname(__FILE__) + '<%= '/..' * controller_class_nesting_depth %>/ describe "<%= File.join(controller_class_path, controller_singular_name) %>/index.html.<%= template_language %>" do before(:each) do @<%= plural_name %> = mock_and_assign_collection(<%= model_name %>) - template.stub_render :partial => @<%= plural_name %> + template.stub! :render end it "should render :partial => @<%= plural_name %>" do - template.expect_render :partial => @<%= plural_name %> + template.should_receive(:render).with(:partial => @<%= plural_name %>) do_render end diff --git a/vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/index_partial.html_spec.rb b/vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/index_partial.html_spec.rb index 3f112e5e..2a10afb1 100644 --- a/vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/index_partial.html_spec.rb +++ b/vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/index_partial.html_spec.rb @@ -8,8 +8,7 @@ describe "<%= File.join(controller_class_path, controller_singular_name) %>/_<%= # :name => "Foo", :created_at => 1.week.ago, :updated_at => nil <% else -%> <%- attributes.each_with_index do |attribute, index| -%> - <%- case attribute.type -%> - <%- when :string, :text -%> + <%- case attribute.type when :string, :text -%> :<%= attribute.name %> => "foo"<%= index < attributes.size - 1 ? "," : "" %> <%- when :integer, :float, :decimal -%> :<%= attribute.name %> => 815<%= index < attributes.size - 1 ? "," : "" %> diff --git a/vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/model_spec.rb b/vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/model_spec.rb index 119349fc..ed6a1945 100644 --- a/vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/model_spec.rb +++ b/vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/model_spec.rb @@ -12,7 +12,7 @@ describe <%= class_name %> do end after(:each) do - @<%= singular_name %>.destroy unless @<%= singular_name %>.new_record? + @<%= singular_name %>.destroy end # Add your model specs here, please! diff --git a/vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/show.html_spec.rb b/vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/show.html_spec.rb index 1c6c7aa8..aefbbe17 100644 --- a/vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/show.html_spec.rb +++ b/vendor/plugins/skinny_spec/generators/skinny_scaffold/templates/show.html_spec.rb @@ -7,8 +7,7 @@ describe "<%= File.join(controller_class_path, controller_singular_name) %>/show <% else -%> @<%= singular_name %> = mock_and_assign(<%= model_name %>, :stub => { <%- attributes.each_with_index do |attribute, index| -%> - <%- case attribute.type -%> - <%- when :string, :text -%> + <%- case attribute.type when :string, :text -%> :<%= attribute.name %> => "foo"<%= index < attributes.size - 1 ? "," : "" %> <%- when :integer, :float, :decimal -%> :<%= attribute.name %> => 815<%= index < attributes.size - 1 ? "," : "" %> diff --git a/vendor/plugins/skinny_spec/lib/lucky_sneaks/common_spec_helpers.rb b/vendor/plugins/skinny_spec/lib/lucky_sneaks/common_spec_helpers.rb index 2506accb..0a7acff3 100644 --- a/vendor/plugins/skinny_spec/lib/lucky_sneaks/common_spec_helpers.rb +++ b/vendor/plugins/skinny_spec/lib/lucky_sneaks/common_spec_helpers.rb @@ -2,13 +2,24 @@ module LuckySneaks # These methods are mostly just called internally by various other spec helper # methods but you're welcome to use them as needed in your own specs. module CommonSpecHelpers + # Stubs out Time.now and returns value to use when comparing it. Example: + # + # time_now = stub_time_now + # @foo.some_method_that_resets_updated_at + # @foo.updated_at.should == time_now + def stub_time_now + returning Time.now do |now| + Time.stub!(:now).and_return(now) + end + end + # Returns class for the specified name. Example: # # class_for("foo") # => Foo def class_for(name) name.to_s.constantize rescue NameError - name.to_s.classify.constantize + name.to_s.pluralize.classify.constantize # Let any other error rise! end @@ -42,5 +53,31 @@ module LuckySneaks end end end + + # Returns class description text + def class_description_text + if self.class.respond_to?(:description_text) + # Old school + self.class.description_text + else + # New school + self.class.description + end + end + + # Returns description text + def self_description_text + if respond_to?(:description_text) + # Old school + description_text + else + # New school + description + end + end + + def described_type + self.class.described_type + end end -end \ No newline at end of file +end diff --git a/vendor/plugins/skinny_spec/lib/lucky_sneaks/controller_request_helpers.rb b/vendor/plugins/skinny_spec/lib/lucky_sneaks/controller_request_helpers.rb index a7cd5b94..4aeffaf4 100644 --- a/vendor/plugins/skinny_spec/lib/lucky_sneaks/controller_request_helpers.rb +++ b/vendor/plugins/skinny_spec/lib/lucky_sneaks/controller_request_helpers.rb @@ -27,9 +27,9 @@ module LuckySneaks alias do_request eval_request def try_shared_request_definition - shared_request - rescue NameError - if @implicit_request + if defined?(shared_request) == "method" + shared_request + elsif @implicit_request try_implicit_request else error_message = "Could not determine request definition for 'describe' context. " @@ -64,4 +64,4 @@ module LuckySneaks end end end -end \ No newline at end of file +end diff --git a/vendor/plugins/skinny_spec/lib/lucky_sneaks/controller_spec_helpers.rb b/vendor/plugins/skinny_spec/lib/lucky_sneaks/controller_spec_helpers.rb index 43210f93..20bca87e 100644 --- a/vendor/plugins/skinny_spec/lib/lucky_sneaks/controller_spec_helpers.rb +++ b/vendor/plugins/skinny_spec/lib/lucky_sneaks/controller_spec_helpers.rb @@ -41,28 +41,23 @@ module LuckySneaks private def create_ar_class_expectation(name, method, argument = nil, options = {}) args = [] - if [:create, :update].include?(@controller_method) - args << (argument.nil? ? valid_attributes : argument) - else + unless options.delete(:only_method) args << argument unless argument.nil? + args << hash_including(options) unless options.empty? end - args << options unless options.empty? + method = options.delete(:find_method) if options[:find_method] if args.empty? - return_value = class_for(name).send(method) - class_for(name).should_receive(method).and_return(return_value) + class_for(name).should_receive(method).and_return(instance_for(name)) else - return_value = class_for(name).send(method, *args) - class_for(name).should_receive(method).with(*args).and_return(return_value) + class_for(name).should_receive(method).with(*args).and_return(instance_for(name)) end end def create_positive_ar_instance_expectation(name, method, *args) instance = instance_for(name) if args.empty? - return_value = instance.send(method) instance.should_receive(method).and_return(true) else - return_value = instance.send(method, *args) instance.should_receive(method).with(*args).and_return(true) end end @@ -73,12 +68,14 @@ module LuckySneaks # Creates an expectation that the controller method calls ActiveRecord::Base.find. # Examples: # - # it_should_find :foos # => Foo.should_receive(:find).with(:all) - # it_should_find :foos, :all # An explicit version of the above - # it_should_find :foos, :conditions => {:foo => "bar"} # => Foo.should_receive(:find).with(:all, :conditions => {"foo" => "bar"} - # it_should_find :foo # => Foo.should_recieve(:find).with(@foo.id.to_s) - # it_should_find :foo, :params => "id" # => Foo.should_receive(:find).with(params[:id].to_s) - # it_should_find :foo, 2 # => Foo.should_receive(:find).with("2") + # it_should_find :foos # => Foo.should_receive(:find).with(:all) + # it_should_find :foos, :all # An explicit version of the above + # it_should_find :foos, :conditions => {:foo => "bar"} # => Foo.should_receive(:find).with(:all, :conditions => {"foo" => "bar"} + # it_should_find :foos, "joe", :method => :find_all_by_name # Foo.should_receive(:find_all_by_name).with("joe") + # it_should_find :foo # => Foo.should_recieve(:find).with(@foo.id.to_s) + # it_should_find :foo, :params => "id" # => Foo.should_receive(:find).with(params[:id].to_s) + # it_should_find :foo, 2 # => Foo.should_receive(:find).with("2") + # it_should_find :foo, "joe", :method => :find_by_name # => Foo.should_recieve(:find_by_name).with("joe") # # Note: All params (key and value) will be strings if they come from a form element and are handled # internally with this expectation. @@ -103,7 +100,27 @@ module LuckySneaks :all end end - create_ar_class_expectation name, :find, argument, options + find_method = options.delete(:method) || :find + create_ar_class_expectation name, find_method, argument, options + eval_request + end + end + + # Negative version of it_should_find. This creates an expectation that + # the class never receives find at all. + def it_should_not_find(name) + name_string = name.to_s + name_message = if name_string == name_string.singularize + "a #{name}" + else + name + end + it "should not find #{name_message}" do + if name_string == name_string.singularize + class_for(name).should_not_receive(:find) + else + class_for(name).should_not_receive(:find).with(:all) + end eval_request end end @@ -121,6 +138,15 @@ module LuckySneaks end end + # Negative version of it_should_initialize. This creates an expectation + # that the class never recieves new at all. + def it_should_not_initialize(name) + it "should initialize a #{name}" do + class_for(name).should_not_receive(:new) + eval_request + end + end + # Creates an expectation that the controller method calls ActiveRecord::Base#save on the # named instance. Example: # @@ -136,6 +162,15 @@ module LuckySneaks end end + # Negative version of it_should_update. This creates an expectation + # that the instance never receives save at all. + def it_should_not_save(name) + it "should not save the #{name}" do + instance_for(name).should_not_receive(:save) + eval_request + end + end + # Creates an expectation that the controller method calls ActiveRecord::Base#update_attributes # on the named instance. Takes optional argument for params to specify in the # expectation. Examples: @@ -148,7 +183,16 @@ module LuckySneaks # for the inevitable re-rendering of the form template. def it_should_update(name, options = {}) it "should update the #{name}" do - create_positive_ar_instance_expectation name, :update_attributes, params[options[:params]] + create_positive_ar_instance_expectation name, :update_attributes, params[name] + eval_request + end + end + + # Negative version of it_should_update. This creates an expectation + # that the instance never receives update_attributes at all. + def it_should_not_update(name) + it "should not update the #{name}" do + instance_for(name).should_not_receive(:update_attributes) eval_request end end @@ -168,6 +212,15 @@ module LuckySneaks end end + # Negative version of it_should_destroy. This creates an expectation + # that the instance never receives destroy at all. + def it_should_not_destroy(name) + it "should not destroy the #{name}" do + instance_for(name).should_not_receive(:destroy) + eval_request + end + end + # Creates expectation[s] that the controller method should assign the specified # instance variables along with any specified values. Examples: # @@ -192,16 +245,36 @@ module LuckySneaks end end + # Essentially shorthand for it_should_assign name => :nil. This method can take multiple + # instance variable names, creating this shorthand for each name. See the docs for + # it_should_assign for more information. + def it_should_not_assign(*names) + names.each do |name| + # Assuming name is a symbol + it_should_assign name => :nil + end + end + # Wraps the separate expectations it_should_find and it_should_assign # for simple cases. If you need more control over the parameters of the find, this # isn't the right helper method and you should write out the two expectations separately. def it_should_find_and_assign(*names) names.each do |name| - it_should_find name + it_should_find name, :only_method => true it_should_assign name end end + # Negative version of it_should_find_and_assign. This creates an + # expectation that the class never receives find at all and that + # no matching instance variable is ever created. + def it_should_not_find_and_assign(*names) + names.each do |name| + it_should_not_find name + it_should_assign name => :nil + end + end + # Wraps the separate expectations it_should_initialize and it_should_assign # for simple cases. If you need more control over the parameters of the initialization, this # isn't the right helper method and you should write out the two expectations separately. @@ -212,11 +285,21 @@ module LuckySneaks # please use it_should_initialize_and_save. def it_should_initialize_and_assign(*names) names.each do |name| - it_should_initialize name + it_should_initialize name, :only_method => true it_should_assign name end end + # Negative version of it_should_initialize_and_assign. This creates an + # expectation that the class never receives new at all and that + # no matching instance variable is ever created. + def it_should_not_initialize_and_assign(*names) + names.each do |name| + it_should_not_initialize name + it_should_assign name => :nil + end + end + # Wraps the separate expectations it_should_initialize and it_should_save # for simple cases. If you need more control over the parameters of the initialization, this # isn't the right helper method and you should write out the two expectations separately. @@ -226,7 +309,7 @@ module LuckySneaks # but not saved, just use it_should_initialize_and_assign. def it_should_initialize_and_save(*names) names.each do |name| - it_should_initialize name + it_should_initialize name, :only_method => true it_should_save name end end @@ -240,7 +323,7 @@ module LuckySneaks # instance is found but not saved, just use it_should_find_and_assign. def it_should_find_and_update(*names) names.each do |name| - it_should_find name + it_should_find name, :only_method => true it_should_update name end end @@ -250,16 +333,16 @@ module LuckySneaks # isn't the right helper method and you should write out the two expectations separately. def it_should_find_and_destroy(*names) names.each do |name| - it_should_find name + it_should_find name, :only_method => true it_should_destroy name end end - # Creates an expectation that the specified collection (flash or session) - # contains the specified key and value. To specify that the collection should be set - # to nil, specify the value as :nil instead. + # Creates an expectation that the specified collection (flash, session, + # params, cookies) contains the specified key and value. To specify that + # the collection should be set to nil, specify the value as :nil instead. def it_should_set(collection, key, value = nil, &block) - it "should set #{collection}[:#{key}]" do + it "should set #{collection}[:#{key}]#{' with ' + value.inspect if value}" do # Allow flash.now[:foo] to remain in the flash flash.stub!(:sweep) if collection == :flash eval_request @@ -270,7 +353,7 @@ module LuckySneaks self.send(collection)[key].should == value end elsif block_given? - self.send(collection)[key].should == block.call + self.send(collection)[key].should == instance_eval(&block) else self.send(collection)[key].should_not be_nil end @@ -282,6 +365,11 @@ module LuckySneaks def it_should_set_flash(name, value = nil, &block) it_should_set :flash, name, value, &block end + + # Wraps it_should_set :flash, :nil. + def it_should_not_set_flash(name) + it_should_set :flash, name, :nil + end # Wraps it_should_set :session. To specify that the collection should be set # to nil, specify the value as :nil instead. @@ -289,6 +377,33 @@ module LuckySneaks it_should_set :session, name, value, &block end + # Wraps it_should_set :session, :nil. + def it_should_not_set_session(name) + it_should_set :session, name, :nil + end + + # Wraps it_should_set :params. To specify that the collection should be set + # to nil, specify the value as :nil instead. + def it_should_set_params(name, value = nil, &block) + it_should_set :params, name, value, &block + end + + # Wraps it_should_set :params, :nil. + def it_should_not_set_params(name) + it_should_set :params, name, :nil + end + + # Wraps it_should_set :cookies. To specify that the collection should be set + # to nil, specify the value as :nil instead. + def it_should_set_cookies(name, value = nil, &block) + it_should_set :cookies, name, value, &block + end + + # Wraps it_should_set :cookies, :nil. + def it_should_not_set_cookies(name) + it_should_set :cookies, name, :nil + end + # Wraps the various it_should_render_foo methods: # it_should_render_template, it_should_render_partial, # it_should_render_xml, it_should_render_json, @@ -407,6 +522,27 @@ module LuckySneaks end end + # Negative version of it_should_redirect_to. + def it_should_not_redirect_to(hint = nil, &route) + if hint.nil? && route.respond_to?(:to_ruby) + hint = route.to_ruby.gsub(/(^proc \{)|(\}$)/, '').strip + end + it "should not redirect to #{(hint || route)}" do + eval_request + response.should_not redirect_to(instance_eval(&route)) + end + end + + # Creates an expectation that the controller method redirects back to the previous page + def it_should_redirect_to_referer + it "should redirect to the referring page" do + request.env["HTTP_REFERER"] = "http://test.host/referer" + eval_request + response.should redirect_to("http://test.host/referer") + end + end + alias it_should_redirect_to_referrer it_should_redirect_to_referer + private def it_should_assign_instance_variable(name, value) expectation_proc = case value diff --git a/vendor/plugins/skinny_spec/lib/lucky_sneaks/controller_stub_helpers.rb b/vendor/plugins/skinny_spec/lib/lucky_sneaks/controller_stub_helpers.rb index 950c7a17..6d78ce54 100644 --- a/vendor/plugins/skinny_spec/lib/lucky_sneaks/controller_stub_helpers.rb +++ b/vendor/plugins/skinny_spec/lib/lucky_sneaks/controller_stub_helpers.rb @@ -6,26 +6,13 @@ module LuckySneaks # :nodoc: # file even more. You are encouraged to use these methods to setup the basic calls for your # resources and only resort to the other methods when mocking and stubbing secondary objects # and calls. - # - # Both stub_create and stub_update benefit from having a valid_attributes - # method defined at the top level of your example groups, ie the top-most "describe" block - # of the spec file. If you did not generate your specs with skinny_scaffold or - # skinny_resourceful generators, you can simply write a method like the following - # for yourself: - # - # def valid_attributes - # { - # "foo" => "bar", - # "baz" => "quux" - # } - # end - # - # Note this method employs strings as both the key and values to best replicate the way - # they are used in actual controllers where the params will come from a form. module ControllerStubHelpers # Stubs out find :all and returns a collection of mock_model # instances of that class. Accepts the following options: # + # :find_method:: Method to use as finder call. Default is :find. + # Note: When specifying the method, the call is stubbed + # to accept any arguments. Caveat programmer. # :format:: Format of the request. Used to only add to_xml and # to_json when actually needed. # :size:: Number of instances to return in the result. Default is 3. @@ -40,10 +27,13 @@ module LuckySneaks # :nodoc: stub_formatted collection, format params[:format] = format end - if options.empty? - klass.stub!(:find).with(:all).and_return(collection) + if find_method = options[:find_method] + # Not stubbing specific arguments here + # If you need more specificity, write a custom example + klass.stub!(find_method).and_return(collection) else - klass.stub!(:find).with(:all, options).and_return(collection) + klass.stub!(:find).with(:all).and_return(collection) + klass.stub!(:find).with(:all, hash_including(options)).and_return(collection) end end end @@ -71,9 +61,11 @@ module LuckySneaks # :nodoc: params[:format] = format end klass.stub!(:new).and_return(member) + if options[:params] + klass.stub!(:new).with(hash_including(options[:params])).and_return(member) + end if options[:stub_save] stub_ar_method member, :save, options[:return] - klass.stub!(:new).with(params[options[:params]]).and_return(member) else member.stub!(:new_record?).and_return(true) member.stub!(:id).and_return(nil) @@ -89,32 +81,25 @@ module LuckySneaks # :nodoc: # Alias for stub_initialize which additionally defines an implicit request post :create. # - # Note: If stub_create is provided an optional :params hash - # or the method valid_attributes is defined within its scope, - # those params will be added to the example's params object. If neither - # are provided an ArgumentError will be raised. + # Note: If stub_create is provided an optional :params hash, + # those params will be added to the example's params object. def stub_create(klass, options = {}) define_implicit_request :create - if options[:params].nil? - if self.respond_to?(:valid_attributes) - params[klass.name.underscore.to_sym] = valid_attributes - options[:params] = valid_attributes - else - error_message = "Params for creating #{klass} could not be determined. " - error_message << "Please define valid_attributes method in the base 'describe' block " - error_message << "or manually set params in the before block." - raise ArgumentError, error_message - end - end + class_name = klass.name.underscore + options[:params] ||= params[class_name] stub_initialize klass, options.merge(:stub_save => true) end # Stubs out find and returns a single mock_model # instances of that class. Accepts the following options: # - # :format:: Format of the request. Used to only add to_xml and - # to_json when actually needed. - # :stub:: Additional methods to stub on the instances + # :find_method:: Method to use as finder call. Default is :find. + # :format:: Format of the request. Used to only add to_xml and + # to_json when actually needed. + # :stub:: Additional methods to stub on the instances + # :current_object:: If set to true, find will set params[:id] + # using the id of the mock_model instance + # and use that value as an argument when stubbing find # # Any additional options will be passed as arguments to find.You will want # to make sure to pass those arguments to the it_should_find spec as well. @@ -125,17 +110,55 @@ module LuckySneaks # :nodoc: def stub_find_one(klass, options = {}) returning mock_model(klass) do |member| stub_out member, options.delete(:stub) - if options[:format] - stub_formatted member, options[:format] - params[:format] = options[:format] + if format = options.delete(:format) + stub_formatted member, format + params[:format] = format end - if options[:current_object] + if options.delete(:current_object) params[:id] = member.id - if options[:stub_ar] - stub_ar_method member, options[:stub_ar], options[:return] + if ar_stub = options.delete(:stub_ar) + stub_ar_method member, ar_stub, options.delete(:return), options.delete(:update_params) end end - klass.stub!(:find).with(member.id.to_s).and_return(member) + if find_method = options.delete(:find_method) + klass.stub!(find_method).and_return(member) + else + # Stubbing string and non-string just to be safe + klass.stub!(:find).with(member.id).and_return(member) + klass.stub!(:find).with(member.id.to_s).and_return(member) + unless options.empty? + klass.stub!(:find).with(member.id, hash_including(options)).and_return(member) + klass.stub!(:find).with(member.id.to_s, hash_including(options)).and_return(member) + end + end + end + end + + # Note: Use of this method with :child options (to mock + # association) is deprecated. Please use stub_association. + # + # Same as stub_find_one but setups the instance as the parent + # of the specified association. Example: + # + # stub_parent(Document, :child => :comments) + # + # This stubs Document.find, @document.comments (which + # will return Comment class), as well as params[:document_id]. + # This method is meant to be used in the controller for the specified child + # (CommentsController in this instance) in situations like: + # + # def index + # @document = Document.find(params[:document_id]) + # @comments = @document.comments.find(:all) + # end + def stub_parent(klass, options = {}) + returning stub_find_one(klass, options) do |member| + params[klass.name.foreign_key] = member.id + if offspring = options.delete(:child) + puts "stub_parent with :child option has been marked for deprecation" + puts "please use stub_association to create the mock instead" + member.stub!(offspring).and_return(class_for(offspring)) + end end end @@ -154,10 +177,8 @@ module LuckySneaks # :nodoc: # Alias for stub_find_one which additionally defines an implicit request put :update # and stubs out the update_attribute method on the instance as well. # - # Note: If stub_update is provided an optional :params hash - # or the method valid_attributes is defined within its scope, - # those params will be added to the example's params object. If neither - # are provided an ArgumentError will be raised. + # Note: If stub_update is provided an optional :params hash, + # those params will be added to the example's params object. def stub_update(klass, options = {}) define_implicit_request :update stub_find_one klass, options.merge(:current_object => true, :stub_ar => :update_attributes) @@ -176,6 +197,20 @@ module LuckySneaks # :nodoc: object.stub!("to_#{format}").and_return("#{object.class} formatted as #{format}") end + # Creates a mock object representing an association proxy, stubs the appropriate + # method on the parent object and returns that association proxy. + # Accepts the following option: + # + # :stub:: Additional methods to stub on the mock proxy object + def stub_association(object, association, options = {}) + # I know options isn't implemented anywhere + object_name = instance_variables.select{|name| instance_variable_get(name) == object} + returning mock("Association proxy for #{object_name}.#{association}") do |proxy| + stub_out proxy, options[:stub] if options[:stub] + object.stub!(association).and_return(proxy) + end + end + private # Stubs out multiple methods. You shouldn't be calling this yourself and if you do # you should be able to understand the code yourself, right? @@ -192,8 +227,12 @@ module LuckySneaks # :nodoc: # Stubs out ActiveRecord::Base methods like #save, #update_attributes, etc # that may be called on a found or instantiated mock_model instance. - def stub_ar_method(object, method, return_value) - object.stub!(method).and_return(return_value ? false : true) + def stub_ar_method(object, method, return_value, params = {}) + if params.blank? + object.stub!(method).and_return(return_value ? false : true) + else + object.stub!(method).with(hash_including(params)).and_return(return_value ? false : true) + end end end end diff --git a/vendor/plugins/skinny_spec/lib/lucky_sneaks/model_spec_helpers.rb b/vendor/plugins/skinny_spec/lib/lucky_sneaks/model_spec_helpers.rb index 471e8c2c..4119acc6 100644 --- a/vendor/plugins/skinny_spec/lib/lucky_sneaks/model_spec_helpers.rb +++ b/vendor/plugins/skinny_spec/lib/lucky_sneaks/model_spec_helpers.rb @@ -6,25 +6,51 @@ module LuckySneaks # to make your model specs a little more DRY. You might also be interested # in checking out the example block [read: "describe"] level versions in of these # methods which can DRY things up even more: - # LuckySneaks::ModelSpecHelpers::ExampleGroupLevelMethods + # LuckySneaks::ModelSpecHelpers::ExampleGroupLevelMethods. # - # Note: The validation matchers are only meant to be used for simple validation checking - # not as a one-size-fits-all solution. + # Also check out the methods in LuckySneaks::ModelSpecHelpers::AssociationMatcher + # for some helpful matcher helper methods to use with these methods if you want to spec + # options on your association setups. module ModelSpecHelpers include LuckySneaks::CommonSpecHelpers def self.included(base) # :nodoc: base.extend ExampleGroupLevelMethods end - - class AssociationMatcher # :nodoc: - def initialize(associated, macro) + + # These methods cannot be used alone but are used in compliment with the association + # matchers in LuckySneaks::ModelSpecHelpers like have_many. Example: + # + # describe User do + # it "should have many memberships" do + # User.should have_many(:memberships) + # end + # + # it "should have many sites through memberships" do + # User.should have_many(:sites).through(:memberships) + # end + # + # it "should belong to a manager" do + # User.should belong_to(:manager).with_counter_cache + # end + # end + # + # Note: To spec these sorts of options using the example block helpers like + # it_should_have_many, just add them as options directly. This will use + # with_options rather than any specific matcher helpers but will have the same + # effects. Example: + # + # describe User do + # it_should_have_many :sites, :through => :memberships + # end + class AssociationMatcher + def initialize(associated, macro) # :nodoc: @associated = associated @macro = macro @options = {} end - def matches?(main_model) + def matches?(main_model) # :nodoc: unless main_model.respond_to?(:reflect_on_association) if main_model.class.respond_to?(:reflect_on_association) main_model = main_model.class @@ -39,7 +65,7 @@ module LuckySneaks end end - def failure_message + def failure_message # :nodoc: if @not_model " expected: #{@not_model} to be a subclass of ActiveRecord::Base class, but was not" elsif @association @@ -49,7 +75,7 @@ module LuckySneaks end end - def negative_failure_message + def negative_failure_message # :nodoc: if @association " expected: #{association_with(@options)}\n got: #{association_with(@association.options)}" else @@ -77,7 +103,7 @@ module LuckySneaks self end - def with_counter_cache(counter_cache = false) + def with_counter_cache(counter_cache = true) if counter_cache @options[:counter_cache] = counter_cache end @@ -169,86 +195,208 @@ module LuckySneaks private def class_or_instance - @model_spec_class_or_instance ||= class_for(self.class.description_text) || instance + @model_spec_class_or_instance ||= class_for(described_type) || instance end def instance - @model_spec_instance ||= instance_for(self.class.description_text) + @model_spec_instance ||= instance_for(described_type) end # These methods are designed to be used at the example group [read: "describe"] level - # to simplify and DRY up common expectations. Most of these methods are wrappers for + # to simplify and DRY up common expectations. Some of these methods are wrappers for # matchers which can also be used on the example level [read: within an "it" block]. See # LuckySneaks::ModelSpecHelpers for more information. + # + # Note: The validation matchers are only meant to be used for simple validation checking + # not as a one-size-fits-all solution. module ExampleGroupLevelMethods - # Creates an expectation that the current model being spec'd has a belongs_to - # association with the specified model. + # Creates an expectation that the current model being spec'd has a belong_to + # association with the specified model. Accepts optional arguments which are appended to + # the belong_to spec like this: + # + # it_should_belong_to :document, :counter_cache => true + # + # which is the same as writing out: + # + # it "should belong to document" do + # Comment.should belong_to(:document).with_options(:counter_cache => true) + # end + # + # If you want a more detailed spec description text, feel free to write this out in the long + # form and use belong_to and its related matcher helpers. # # Note: The argument should be a symbol as in the model's association definition # and not the model's class name. - def it_should_belong_to(model) + def it_should_belong_to(model, options = {}) it "should belong to a #{model}" do - class_or_instance.should belong_to(model) + if options.empty? + class_or_instance.should belong_to(model) + else + class_or_instance.should belong_to(model).with_options(options) + end end end # Creates an expectation that the current model being spec'd has a have_one - # association with the specified model. + # association with the specified model. Accepts optional arguments which are appended to + # the have_one spec like this: + # + # it_should_have_one :last_comment, :class_name => "Comment", :order => "created_at DESC" + # + # which is the same as writing out: + # + # it "should have one document" do + # Document.should have_one(:last_comment).with_options(:class_name => "Comment", :order => "created_at DESC") + # end + # + # If you want a more detailed spec description text, feel free to write this out in the long + # form and use have_one and its related matcher helpers. # # Note: The argument should be a symbol as in the model's association definition # and not the model's class name. - def it_should_have_one(model) + def it_should_have_one(model, options = {}) it "should have one #{model}" do - class_or_instance.should have_one(model) + if options.empty? + class_or_instance.should have_one(model) + else + class_or_instance.should have_one(model).with_options(options) + end end end # Creates an expectation that the current model being spec'd has a have_many - # association with the specified model. + # association with the specified model. Accepts optional arguments which are appended to + # the have_many spec like this: + # + # it_should_have_many :memberships, :through => :sites + # + # which is the same as writing out: + # + # it "should have many memberships" do + # User.should have_many(:memberships).with_options(:through => :sites) + # end + # + # If you want a more detailed spec description text, feel free to write this out in the long + # form and use have_many and its related matcher helpers. # # Note: The argument should be a symbol as in the model's association definition # and not the model's class name. - def it_should_have_many(models) + def it_should_have_many(models, options = {}) it "should have many #{models}" do - class_or_instance.should have_many(models) + if options.empty? + class_or_instance.should have_many(models) + else + class_or_instance.should have_many(models).with_options(options) + end end end # Creates an expectation that the current model being spec'd has a have_and_belong_to_many - # association with the specified model. + # association with the specified model. Accepts optional arguments which are appended to + # the have_and_belong_to_many spec like this: + # + # it_should_have_and_belong_to_many :documents, :include => :attachments + # + # which is the same as writing out: + # + # it "should belong to document" do + # User.should have_and_belong_to_many(:documents).with_options(:include => :attachments) + # end + # + # If you want a more detailed spec description text, feel free to write this out in the long + # form and use have_and_belong_to_many and its related matcher helpers. # # Note: The argument should be a symbol as in the model's association definition # and not the model's class name. - def it_should_have_and_belong_to_many(models) + def it_should_have_and_belong_to_many(models, options = {}) it "should have and belong to many #{models}" do - class_or_instance.should have_and_belong_to_many(models) + if options.empty? + class_or_instance.should have_and_belong_to_many(models) + else + class_or_instance.should have_and_belong_to_many(models).with_options(options) + end + end + end + + # Creates an expectation that new instances of the model being spec'd + # should initialise the specified attributes with a default value. + # + # it_should_default_attributes :status => 'new' + # + def it_should_default_attributes(hash_attribute_values) + hash_attribute_values.each_pair do |a,v| + it "should default #{a} attribute to #{v}" do + class_or_instance.new.send(a).should == v + end end end # Creates an expectation that the current model being spec'd validates_presence_of # the specified attribute. Takes an optional custom message to match the one in the model's # validation. - def it_should_validate_presence_of(attribute, message = I18n.translate('activerecord.errors.messages')[:blank]) + def it_should_validate_presence_of(attribute, message = default_error_message(:blank)) it "should not be valid if #{attribute} is blank" do instance.send "#{attribute}=", nil instance.errors_on(attribute).should include(message) end end + # Negative version of it_should_validate_presence_of. See that method for more + # details. You'd probably only be using this in a nested example block to compare that + # one scenario validates presence and another does not (because of conditions in + # :if/:unless). + def it_should_not_validate_presence_of(attribute, message = default_error_message(:blank)) + it "should be valid if #{attribute} is blank" do + instance.send "#{attribute}=", nil + instance.errors_on(attribute).should_not include(message) + end + end + + # Creates an expectation that the current model being spec'd validates_inclusion_of + # the specified attribute. Takes an optional custom message to match the one in the model's + # validation. + def it_should_validate_inclusion_of(attribute, options = {}, message = default_error_message(:inclusion)) + it "should validate #{attribute} is in #{options[:in].to_s}" do + # We specifically do not try to go below the range on String and character ranges because that problem set is unpredictable. + lower = options[:in].first.respond_to?(:-) ? options[:in].first - 0.0001 : nil + higher = options[:in].last.succ + + instance.send "#{attribute}=", lower + instance.errors_on(attribute).should include(message) + + instance.send "#{attribute}=", higher + instance.errors_on(attribute).should include(message) + + instance.send "#{attribute}=", (lower+higher)/2 + instance.errors_on(attribute).should_not include(message) + end + end + # Creates an expectation that the current model being spec'd validates_numericality_of # the specified attribute. Takes an optional custom message to match the one in the model's # validation. - def it_should_validate_numericality_of(attribute, message = I18n.translate('activerecord.errors.messages')[:not_a_number]) + def it_should_validate_numericality_of(attribute, message = default_error_message(:not_a_number)) it "should validate #{attribute} is a numeric" do instance.send "#{attribute}=", "NaN" instance.errors_on(attribute).should include(message) end end + # Negative version of it_should_validate_numericality_of. See that method for more + # details. You'd probably only be using this in a nested example block to compare that + # one scenario validates presence and another does not (because of conditions in + # :if/:unless). + def it_should_not_validate_numericality_of(attribute, message = default_error_message(:not_a_number)) + it "should not validate #{attribute} is a numeric" do + instance.send "#{attribute}=", "NaN" + instance.errors_on(attribute).should_not include(message) + end + end + # Creates an expectation that the current model being spec'd validates_confirmation_of # the specified attribute. Takes an optional custom message to match the one in the model's # validation. - def it_should_validate_confirmation_of(attribute, message = I18n.translate('activerecord.errors.messages')[:confirmation]) + def it_should_validate_confirmation_of(attribute, message = default_error_message(:confirmation)) it "should validate confirmation of #{attribute}" do dummy_value = dummy_value_for(instance, attribute) || "try a string" instance.send "#{attribute}=", dummy_value @@ -263,20 +411,33 @@ module LuckySneaks # # Note: This method will fail completely if valid_attributes # does not provide all the attributes needed to create a valid record. - def it_should_validate_uniqueness_of(attribute, message = I18n.translate('activerecord.errors.messages')[:taken]) - it "should validate #{attribute} confirmation" do - previous_instance = class_for(self.class.description_text).create!(valid_attributes) + def it_should_validate_uniqueness_of(attribute, message = default_error_message(:taken)) + it "should validate uniqueness of #{attribute}" do + previous_instance = instance.class.create!(valid_attributes) instance.attributes = valid_attributes instance.errors_on(attribute).should include(message) previous_instance.destroy end end + # Negative version of it_should_validate_uniqueness_of. See that method for more + # details. You'd probably only be using this in a nested example block to compare that + # one scenario validates presence and another does not (because of conditions in + # :if/:unless). + def it_should_not_validate_uniqueness_of(attribute, message = default_error_message(:taken)) + it "should not validate uniqueness of #{attribute}" do + previous_instance = instance.class.create!(valid_attributes) + instance.attributes = valid_attributes + instance.errors_on(attribute).should_not include(message) + previous_instance.destroy + end + end + # Creates an expectation that the current model being spec'd accepts the specified values as # valid for the specified attribute. This is most likely used with validates_format_of # but there's nothing saying it couldn't be another validation. def it_should_accept_as_valid(attribute, *values) - values.each do |value| + values.flatten.each do |value| value_inspect = case value when String : "'#{value}'" when NilClass : "nil" @@ -296,7 +457,7 @@ module LuckySneaks # spec'ing the actual error message. def it_should_not_accept_as_valid(attribute, *values) options = values.extract_options! - values.each do |value| + values.flatten.each do |value| value_inspect = case value when String : "'#{value}'" when NilClass : "nil" @@ -312,6 +473,7 @@ module LuckySneaks end end end + # Creates an expectation that the current model being spec'd doesn't allow mass-assignment # of the specified attribute. def it_should_not_mass_assign(attribute) @@ -321,6 +483,14 @@ module LuckySneaks }.should_not change(instance, attribute) end end + + def default_error_message(attribute) + if defined?(I18n) + I18n.translate attribute, :scope => "activerecord.errors.messages" + else + ActiveRecord::Errors.default_error_messages[attribute] + end + end end end -end \ No newline at end of file +end diff --git a/vendor/plugins/skinny_spec/lib/lucky_sneaks/view_spec_helpers.rb b/vendor/plugins/skinny_spec/lib/lucky_sneaks/view_spec_helpers.rb index eb532554..fb8775a1 100644 --- a/vendor/plugins/skinny_spec/lib/lucky_sneaks/view_spec_helpers.rb +++ b/vendor/plugins/skinny_spec/lib/lucky_sneaks/view_spec_helpers.rb @@ -6,9 +6,10 @@ module LuckySneaks # to make your view specs less brittle and more DRY. You might also be interested # in checking out the example block [read: "describe"] level versions in of these # methods which can DRY things up even more: - # LuckySneaks::ViewSpecHelpers::ExampleGroupLevelMethods + # LuckySneaks::ViewSpecHelpers::ExampleGroupLevelMethods. module ViewSpecHelpers include LuckySneaks::CommonSpecHelpers + include LuckySneaks::ViewStubHelpers include ActionController::PolymorphicRoutes def self.included(base) # :nodoc: @@ -21,7 +22,7 @@ module LuckySneaks have_tag("form[action=#{path}]") end - # Wraps a matcher that checks is the receiver contains any of several form elements + # Wraps a matcher that checks if the receiver contains any of several form elements # that would return sufficient named parameters to allow editing of the specified # attribute on the specified instance. Example: # @@ -36,7 +37,8 @@ module LuckySneaks # def allow_editing(instance, attribute) instance_name = instance.class.name.underscore.downcase - if instance.send(attribute).is_a?(Time) + column = instance.column_for_attribute(attribute) + if column && [Date, Time].include?(column.klass) have_tag( "input[name='#{instance_name}[#{attribute}]'], select[name=?]", /#{instance_name}\[#{attribute}\(.*\)\]/ @@ -48,10 +50,20 @@ module LuckySneaks select[name='#{instance_name}[#{attribute}]'], textarea[name='#{instance_name}[#{attribute}]'], input[type='checkbox'][name='#{instance_name}[#{attribute}]'], - input[type='checkbox'][name='#{instance_name}[#{attribute.to_s.tableize.singularize}_ids][]']" + input[type='checkbox'][name='#{instance_name}[#{attribute.to_s.tableize.singularize}_ids][]'], + input[type='radio'][name='#{instance_name}[#{attribute}]']" ) end end + + # Wraps a matcher that checks if the receiver contains a FORM element + # whose enctype attribute is set to "multipart/form-data" + # and contains an INPUT element whose name attribute correlates + # with the provided instance and attribute. + def allow_uploading(instance, attribute) + instance_name = instance.class.name.underscore.downcase + have_tag("form[enctype='multipart/form-data'] input[type='file'][name='#{instance_name}[#{attribute}]']") + end # Wraps a matcher that checks if the receiver contains an A element (link) # whose href attribute is set to the specified path or a FORM @@ -78,7 +90,14 @@ module LuckySneaks # Wraps have_link_or_button_to polymorphic_path(instance) which # corresponds with the show method of the controller. def have_link_or_button_to_show(instance) - have_link_or_button_to polymorphic_path(instance) + path = polymorphic_path(instance) + have_tag( + "a[href='#{path}'], + form[action='#{path}'][method='get'] input, + form[action='#{path}'][method='get'] button, + form[action='#{path}'] input[name='_method'][value='get'] + input, + form[action='#{path}'] input[name='_method'][value='get'] + button" + ) end alias have_link_to_show have_link_or_button_to_show alias have_button_to_show have_link_or_button_to_show @@ -99,7 +118,8 @@ module LuckySneaks path = polymorphic_path(instance) have_tag( "form[action='#{path}'] input[name='_method'][value='delete'] + input, - form[action='#{path}'] input[name='_method'][value='delete'] + button" + form[action='#{path}'] input[name='_method'][value='delete'] + button, + a[href=\"#{path}\"][onclick*=\"f.method = 'POST'\"][onclick*=\"m.setAttribute('name', '_method'); m.setAttribute('value', 'delete')\"]" ) end @@ -121,9 +141,9 @@ module LuckySneaks def mock_and_assign(klass, *args) options = args.extract_options! mocked = if options[:stub] - mock_model(klass, options[:stub]) + self.respond_to?(:stub_model) ? stub_model(klass, options[:stub]) : mock_model(klass, options[:stub]) else - mock_model(klass) + self.respond_to?(:stub_model) ? stub_model(klass) : mock_model(klass) end yield mocked if block_given? self.assigns[args.first || "#{klass}".underscore] = mocked @@ -133,12 +153,12 @@ module LuckySneaks # mock_and_assign. Accepts option[:size] which sets the size # of the array (default is 3). def mock_and_assign_collection(klass, *args) - options = args.dup.extract_options! + options = args.extract_options! return_me = Array.new(options[:size] || 3) do mocked = if options[:stub] - mock_model(klass, options[:stub]) + self.respond_to?(:stub_model) ? stub_model(klass, options[:stub]) : mock_model(klass, options[:stub]) else - mock_model(klass) + self.respond_to?(:stub_model) ? stub_model(klass) : mock_model(klass) end yield mocked if block_given? mocked @@ -150,8 +170,8 @@ module LuckySneaks def do_render if @the_template render @the_template - elsif File.exists?(File.join(RAILS_ROOT, "app/views", self.class.description_text)) - render self.class.description_text + elsif File.exists?(File.join(RAILS_ROOT, "app/views", class_description_text)) + render class_description_text else error_message = "Cannot determine template for render. " error_message << "Please define @the_template in the before block " @@ -182,6 +202,18 @@ module LuckySneaks end end + # Negative version of it_should_submit_to. See that method for more + # details. + def it_should_not_submit_to(hint = nil, &route) + if hint.nil? && route.respond_to?(:to_ruby) + hint = route.to_ruby.gsub(/(^proc \{)|(\}$)/, '').strip + end + it "should not submit to #{(hint || route)}" do + do_render + response.should_not submit_to(instance_eval(&route)) + end + end + # Creates an expectation that the template uses Rails' form_for to generate # the proper form action and method to create or update the specified object. # @@ -190,41 +222,85 @@ module LuckySneaks # not an instance variable, which would be nil in the scope of the example block. # If you use namespacing for your form_for, you'll have to manually write out # a similar spec. - def it_should_have_form_for(name) + def it_should_have_form_for(name, options = {}) it "should have a form_for(@#{name})" do - template.should_receive(:form_for).with(instance_for(name)) + if options.empty? + template.should_receive(:form_for).with(instance_for(name)) + else + template.should_receive(:form_for).with(instance_for(name), hash_including(options)) + end do_render end end # Negative version of it_should_have_form_for. See that method for more # details. - def it_should_not_have_form_for(name) + def it_should_not_have_form_for(name, options = {}) it "should not have a form_for(@#{name})" do - template.should_not_receive(:form_for).with(instance_for(name)) + if options.empty? + template.should_not_receive(:form_for).with(instance_for(name)) + else + template.should_not_receive(:form_for).with(instance_for(name), hash_including(options)) + end do_render end end - # Creates an expectation which calls allow_editing on the response - # from rendering the template. See that method for more details. + # Creates an expectation which calls allow_editing on the rendered + # template for each attribute specified. See the docs for allow_editing + # for more details. # # Note: This method takes a string or symbol representing the instance # variable's name to send to allow_editing # not an instance variable, which would be nil in the scope of the example block. - def it_should_allow_editing(name, method) - it "should allow editing of @#{name}##{method}" do - do_render - response.should allow_editing(instance_for(name), method) + def it_should_allow_editing(instance_name, *attributes) + attributes.flatten! + attributes.each do |attribute| + it "should allow editing of @#{instance_name}##{attribute}" do + do_render + response.should allow_editing(instance_for(instance_name), attribute) + end end end # Negative version of it_should_allow_editing. See that method for more # details. - def it_should_not_allow_editing(name, method) - it "should not allow editing of @#{name}##{method}" do - do_render - response.should_not allow_editing(instance_for(name), method) + def it_should_not_allow_editing(instance_name, *attributes) + attributes.flatten! + attributes.each do |attribute| + it "should not allow editing of @#{instance_name}##{attribute}" do + do_render + response.should_not allow_editing(instance_for(instance_name), attribute) + end + end + end + + # Creates an expectation which calls allow_uploading on the rendered + # template for each attribute specified. See the docs for allow_uploading + # for more details. + # + # Note: This method takes a string or symbol representing the instance + # variable's name to send to allow_uploading + # not an instance variable, which would be nil in the scope of the example block. + def it_should_allow_uploading(instance_name, *attributes) + attributes.flatten! + attributes.each do |attribute| + it "should allow editing of @#{instance_name}##{attribute}" do + do_render + response.should allow_uploading(instance_for(instance_name), attribute) + end + end + end + + # Negative version of it_should_allow_uploading. See that method for more + # details. + def it_should_not_allow_uploading(instance_name, *attributes) + attributes.flatten! + attributes.each do |attribute| + it "should not allow editing of @#{instance_name}##{attribute}" do + do_render + response.should_not allow_uploading(instance_for(instance_name), attribute) + end end end @@ -455,6 +531,47 @@ module LuckySneaks alias it_should_have_link_to_delete_each it_should_link_to_delete_each alias it_should_have_button_to_delete_each it_should_link_to_delete_each alias it_should_have_button_or_link_to_delete_each it_should_link_to_delete_each + + # Creates an expectation that the template should call render :partial + # with the specified template. + def it_should_render_partial(name) + it "should render :partial => '#{name}'" do + template.should_receive(:render).with(hash_including(:partial => name)) + do_render + end + end + + # Negative version of it_should_render_partial. See that method + # for more details. + def it_should_not_render_partial(name) + it "should not render :partial => '#{name}'" do + template.should_not_receive(:render).with(hash_including(:partial => name)) + do_render + end + end + + # Sets @the_template (for use in do_render) using the current + # example group description. Example: + # + # describe "users/index.haml.erb" do + # use_describe_for_template! + # # ... + # end + # + # This is equivalent to setting @the_template = "users/index.haml.erb" + # in a before block. + def use_describe_for_template! + template = self_description_text + if File.exists?(File.join(RAILS_ROOT, "app/views", template)) + before(:each) do + @the_template = template + end + else + error_message = "You called use_describe_for_template! " + error_message << "but 'app/views/#{template}' does not exist. " + raise NameError, error_message + end + end end end -end \ No newline at end of file +end diff --git a/vendor/plugins/skinny_spec/lib/lucky_sneaks/view_stub_helpers.rb b/vendor/plugins/skinny_spec/lib/lucky_sneaks/view_stub_helpers.rb new file mode 100644 index 00000000..2dde384e --- /dev/null +++ b/vendor/plugins/skinny_spec/lib/lucky_sneaks/view_stub_helpers.rb @@ -0,0 +1,15 @@ +$:.unshift File.join(File.dirname(__FILE__), "..") +require "skinny_spec" + +module LuckySneaks + # These methods are designed to be used in your example before blocks to accomplish + # a whole lot of functionality with just a tiny bit of effort. + module ViewStubHelpers + # Shorthand for the following stub: + # + # template.stub!(:render).with(hash_including(:partial => anything)) + def stub_partial_rendering! + template.stub!(:render).with(hash_including(:partial => anything)) + end + end +end \ No newline at end of file diff --git a/vendor/plugins/skinny_spec/lib/skinny_spec.rb b/vendor/plugins/skinny_spec/lib/skinny_spec.rb index c7883884..3c366ce1 100644 --- a/vendor/plugins/skinny_spec/lib/skinny_spec.rb +++ b/vendor/plugins/skinny_spec/lib/skinny_spec.rb @@ -1,10 +1,10 @@ # Let's make sure everyone else is loaded -require File.expand_path(File.dirname(__FILE__) + "/../../../../config/environment") +require File.expand_path(RAILS_ROOT + "/config/environment") require 'spec' require 'spec/rails' begin require 'ruby2ruby' -rescue +rescue LoadError puts "-----" puts "Attention: skinny_spec requires ruby2ruby for nicer route descriptions" puts "It is highly recommended that you install it: sudo gem install ruby2ruby"