spec/class_action/action_spec.rb in class-action-0.0.1 vs spec/class_action/action_spec.rb in class-action-1.1.0

- old
+ new

@@ -3,28 +3,31 @@ describe ClassAction::Action do let(:controller) { double(:controller, :view_assigns => {}, :response_body => nil) } let(:action_class) { Class.new(ClassAction::Action) } let(:action) { action_class.new(controller) } + before { allow(controller).to receive(:class_action).and_return(action) } it "should by default be available" do expect(action).to be_available end describe '.helpers && .helper_method' do it "should create an empty module upon inheritance" do expect(action_class.helpers).to be_a(Module) end - it "should define the helper method in the action's helpers module, which should call the method on the controller action" do + before do action_class.class_eval do def helper1 'HELPER RESULT' end helper_method :helper1 end + end + it "should define the helper method in the action's helpers module, which should call the method on the controller action" do helpers = action_class.helpers klass = Class.new do include helpers attr_reader :controller def initialize(controller) @@ -36,26 +39,51 @@ expect(obj).to respond_to(:helper1) expect(controller).to receive(:class_action).and_return(action) expect(obj.helper1).to eql('HELPER RESULT') end + + it "should also expose helper methods on the controller" do + action # Load the action up to include the helpers module. + expect(controller).to respond_to(:helper1) + expect(controller.helper1).to eql('HELPER RESULT') + end + + it "should also expose helper methods defined in a superclass" do + action_subclass = Class.new(action_class) do + def helper2 + 'HELPER2' + end + helper_method :helper2 + end + + obj= Class.new{ include action_subclass.helpers }.new + expect(obj).to respond_to(:helper1) + expect(obj).to respond_to(:helper2) + end + end - describe '.controller_method' do - before { allow(controller).to receive(:load_post) } - before { action_class.class_eval { controller_method :load_post } } + describe 'controller methods' do + let(:result) { double(:result) } + before { allow(controller).to receive(:load_post).and_return(result) } - it "should create a protected method :load_post" do - expect(action.protected_methods).to include(:load_post) + it "should make the action respond to :load_post, but protectedly" do + expect(action).not_to respond_to(:load_post) + expect(action.respond_to?(:load_post, true)).to be_true # matcher doesn't work with second argument end - it "should create a proxy to the controller" do - result = double(:result) - expect(controller).to receive(:load_post).and_return(result) - expect(action.send(:load_post)).to be(result) + it "should pass the method :load_post on to the controller" do + expect(action.load_post).to be(result) end + it "should create a protected method :load_post the first time it is called" do + expect(action.protected_methods).not_to include(:load_post) + action.load_post + expect(action.protected_methods).to include(:load_post) + end + it "should copy assigns to the controller before executing the controller method, and copy them back afterwards" do # Simulate an instance variable. var = 1 allow(controller).to receive(:view_assigns) do {'var' => var} @@ -66,11 +94,10 @@ expect(controller).to receive(:increase_var) do var += 1 end action_class.class_eval do - controller_method :increase_var def execute @var = 2 increase_var end end @@ -96,11 +123,11 @@ private def method4; end end - expect(action_class.action_methods).to eql([ :method1, :method2 ]) + expect(action_class._action_methods).to eql([ :method1, :method2 ]) end end describe '#_execute' do it "should raise an exception if the action is not available" do @@ -109,38 +136,90 @@ end it "should execute all action methods in the action, and call #copy_assigns_to_controller finally" do called = [] - expect(action_class).to receive(:action_methods).and_return([:method1, :method2]) - expect(action).to receive(:method1) { called << :method1 } - expect(action).to receive(:method2) { called << :method2 } + expect(action_class).to receive(:_action_methods).and_return([:method1, :method2]) + action_class.class_eval do + def method1 + @called << :method1 + end + def method2 + @called << :method2 + end + end + + action.instance_variable_set '@called', called action._execute expect(called).to eql([:method1, :method2]) end + it "should skip methods that take arguments" do + action_class.class_eval do + def one + @called = [] + @called << :one + end + def two(*args) + @called << :two + end + def three(arg) + @called << :three + end + def three(arg = nil) + @called << :four + end + end + + action._execute + called = action.instance_variable_get('@called') + expect(called).to eql([:one]) + end + it "should stop executing when a response body is set" do called = []; response_body = nil allow(controller).to receive(:response_body) { response_body } - expect(action_class).to receive(:action_methods).and_return([:method1, :method2]) - expect(action).to receive(:method1) { called << :method1; response_body = '<html></html>' } - expect(action).not_to receive(:method2) + allow(controller).to receive(:response).and_return(double()) + allow(controller.response).to receive(:body) { response_body } + allow(controller.response).to receive(:body=) { |val| response_body = val } + expect(action_class).to receive(:_action_methods).and_return([:method1, :method2]) + + action_class.class_eval do + def method1 + @called << :method1 + response.body = '<html></html>' + end + def method2 + @called << :method2 + end + end + + action.instance_variable_set '@called', called action._execute expect(called).to eql([:method1]) end it "should call _respond at the end" do called = [] - expect(action_class).to receive(:action_methods).and_return([:method1, :method2]) - expect(action).to receive(:method1) { called << :method1 } - expect(action).to receive(:method2) { called << :method2 } + expect(action_class).to receive(:_action_methods).and_return([:method1, :method2]) + + action_class.class_eval do + def method1 + @called << :method1 + end + def method2 + @called << :method2 + end + end + expect(action).to receive(:_respond) { called << :_respond } + action.instance_variable_set '@called', called action._execute expect(called).to eql([:method1, :method2, :_respond]) end it "should not call _respond if a response body is set" do @@ -166,39 +245,61 @@ expect(controller).to receive(:instance_variable_set).with(:@my_var, :test) action._execute end - context "with no respond with or responders" do + context "with no response method or responders" do it "should not call a respond method, but copy all instance variables into the controller at the end" do expect(controller).not_to receive(:respond_with) expect(controller).not_to receive(:respond_to) action._execute end end - context "having set a respond_with" do + context "having set a response method" do let(:response) { double(:response) } before do action_class.class_eval do respond_with :response + respond_with :invalid_response, on: :invalid + + protected + + def invalid?() false end end - expect(action).to receive(:response).and_return(response) end - it "should call the respond_with method and use it in the response" do + it "should use the value of #response" do + expect(action).to receive(:response).and_return(response) expect(controller).to receive(:respond_with).with(response) do |&blk| expect(blk).to be_nil end action._execute end + it "should use the value of #invalid_response if invalid? returns true" do + allow(action).to receive(:invalid?).and_return(true) + expect(action).to receive(:invalid_response).and_return(response) + expect(controller).to receive(:respond_with).with(response) + action._execute + end + + it "should read an instance variable if this is set" do + action_class.class_eval do + respond_with :@response + end + action.instance_variable_set '@response', response + expect(controller).to receive(:respond_with).with(response) + action._execute + end + it "should use the _respond_block if it is set" do block = proc{} allow(action).to receive(:_respond_block).and_return(block) + expect(action).to receive(:response).and_return(response) expect(controller).to receive(:respond_with).with(response) do |&blk| expect(blk).to be(block) end action._execute @@ -225,39 +326,111 @@ describe 'responders & _respond_block' do # Private method, but specced individually to make spec terser. - let(:respond_block) { action.send(:_respond_block) } + def respond_block + action.send(:_respond_block) + end it "should create a block using the given responders, which is executed on the action" do called = nil; receiver = nil json_block = proc { receiver = self; called = :json } html_block = proc { receiver = self; called = :html } - any_block = proc { receiver = self; called = :any } + html_invalid_block = proc { receiver = self; called = :html_invalid } + any_ok_block = proc { receiver = self; called = :any_ok } action_class.class_eval do respond_to :json, &json_block respond_to :html, &html_block - respond_to_any &any_block + respond_to :html, on: :invalid, &html_invalid_block + respond_to_any on: :ok, &any_ok_block + + attr_accessor :status + + protected + + def ok?() status == :ok end + def invalid?() status == :invalid end end # Simulate ActionController's format collector. collector = Class.new{ attr_reader :json_block, :html_block, :any_block }.new def collector.json(&block) @json_block = block end def collector.html(&block) @html_block = block end def collector.any(&block) @any_block = block end + action.status = :ok respond_block.call collector + collector.json_block.call + expect(receiver).to be(action); expect(called).to be(:json) + action.status = :invalid + respond_block.call collector collector.json_block.call expect(receiver).to be(action); expect(called).to be(:json) + action.status = :ok + respond_block.call collector collector.html_block.call expect(receiver).to be(action); expect(called).to be(:html) + action.status = :invalid + respond_block.call collector + collector.html_block.call + expect(receiver).to be(action); expect(called).to be(:html_invalid) + + action.status = :ok + respond_block.call collector collector.any_block.call - expect(receiver).to be(action); expect(called).to be(:any) + expect(receiver).to be(action); expect(called).to be(:any_ok) + + receiver = nil + called = nil + + action.status = :invalid + expect(collector).not_to receive(:any) + respond_block.call collector + end + + it "should copy assigns back to the controller after responding" do + action_class.class_eval do + respond_to :html do + @my_var = :value + end + end + + collector = Class.new{ attr_reader :html_block }.new + def collector.html(&block) @html_block = block end + respond_block.call collector + + collector.html_block.call + expect(controller.instance_variable_get('@my_var')).to be(:value) + end + + it "should take responders to a subclass" do + action_class.class_eval do + respond_to :html + end + action_subclass = Class.new(action_class) do + respond_to :json + end + + expect(action_subclass._responders).to eql( + [ :html, nil ] => nil, + [ :json, nil ] => nil + ) + end + + it "should copy the respond_with_method to a subclass" do + action_class.class_eval do + respond_with :post + end + + action_subclass = Class.new(action_class) + expect(action_subclass).to have(1)._response + expect(action_subclass._responses.keys[0]).to be_nil + expect(action_subclass._responses.values[0]).to be(:post) end end end \ No newline at end of file