require 'spec_helper' class User extend ActiveModel::Naming attr_accessor :name, :widgets_attributes def to_key [1] end end class Widget extend ActiveModel::Naming attr_accessor :name def persisted? false end end class ControllerRenderer < AbstractController::Base include AbstractController::Rendering self.view_paths = [ActionView::FileSystemResolver.new(File.join(File.dirname(__FILE__), 'fixtures', 'views'))] view_context_class.class_eval do def url_for(*args) '/users' end def protect_against_forgery? false end end end class MockBuilder < ActionView::Helpers::FormBuilder; end describe SignedForm::FormBuilder do include SignedFormViewHelper before { SignedForm.secret_key = "abc123" } before { SignedForm.options[:digest] = false } let(:user) { User.new } let(:widget) { Widget.new } describe "form_for tag" do it "should build a form with signature" do content = form_for(User.new, signed: true) do |f| f.text_field :name end regex = '.*.*' \ '.*' \ '' content.should =~ Regexp.new(regex, Regexp::MULTILINE) end it "should not sign if no option is present" do content = form_for(User.new) do |f| f.text_field :name end content.should_not =~ /form_signature/ end context "signed default" do before { SignedForm.options[:signed] = true } it "should sign a form when the default option is set" do content = form_for(User.new) do |f| f.text_field :name end content.should =~ /form_signature/ end it "should allow the form to be overriden" do content = form_for(User.new, signed: false) do |f| f.text_field :name end content.should_not =~ /form_signature/ end end end describe "third party builders" do it "should build a signed form" do content = form_for(User.new, signed: true, builder: MockBuilder) do |f| f.text_field :name end data = get_data_from_form(content) data['user'].should include(:name) end it "should raise if a builder isn't supported" do expect { form_for(User.new, signed: true, builder: Class.new) {} }.to raise_error end end describe "sign_destination" do after do @data.should include(:_options_) @data[:_options_].should include(:method, :url) @data[:_options_][:method].should == :post @data[:_options_][:url].should == '/users' end it "should set a target" do content = form_for(User.new, signed: true, sign_destination: true) do |f| f.text_field :name end @data = get_data_from_form(content) end it "should set a target when the default options are enabled" do SignedForm.options[:sign_destination] = true content = form_for(User.new, signed: true) do |f| f.text_field :name end @data = get_data_from_form(content) end end describe "form inputs" do fields = ActionView::Helpers::FormBuilder.instance_methods - Object.instance_methods fields -= [:button, :multipart=, :submit, :field_helpers, :label, :multipart, :emitted_hidden_id?, :to_model, :field_helpers?, :field_helpers=, :fields_for, :object_name=, :object=, :object_name, :model_name_from_record_or_class, :multipart?, :options, :options=, :convert_to_model, :to_partial_path, :index, :object, :radio_button, :parent_builder, :collection_check_boxes, :grouped_collection_select, :select, :collection_select, :collection_radio_buttons, :time_select, :datetime_select, :time_zone_select, :date_select, :search_field] after do @data['user'].size.should == 1 @data['user'].should include(:name) end fields.each do |field| it "should add to the allowed attributes when #{field} is used" do content = form_for(User.new, signed: true) do |f| f.send field, :name end @data = get_data_from_form(content) end end it "should add to the allowed attributes when collection_check_boxes is used", action_pack: /4\.\d+/ do content = form_for(User.new, signed: true) do |f| f.collection_check_boxes :name, ['a', 'b'], :to_s, :to_s end @data = get_data_from_form(content) end it "should add to the allowed attributes when grouped_collection_select is used" do continent = Struct.new('Continent', :continent_name, :countries) country = Struct.new('Country', :country_id, :country_name) content = form_for(User.new, signed: true) do |f| f.grouped_collection_select(:name, [continent.new("", [country.new("", "")])], :countries, :continent_name, :country_id, :country_name) end @data = get_data_from_form(content) end it "should add to the allowed attributes when select is used" do content = form_for(User.new, signed: true) do |f| f.select :name, %w(a b) end @data = get_data_from_form(content) end it "should add to the allowed attributes when collection_select is used" do content = form_for(User.new, signed: true) do |f| f.collection_select :name, %w(a b), :to_s, :to_s end @data = get_data_from_form(content) end it "should add to the allowed attributes when collection_radio_buttons is used", action_pack: /4\.\d+/ do content = form_for(User.new, signed: true) do |f| f.collection_radio_buttons :name, %w(a b), :to_s, :to_s end @data = get_data_from_form(content) end it "should add to the allowed attributes when date_select is used" do content = form_for(User.new, signed: true) do |f| f.date_select :name end @data = get_data_from_form(content) end it "should add to the allowed attributes when time_select is used" do content = form_for(User.new, signed: true) do |f| f.time_select :name end @data = get_data_from_form(content) end it "should add to the allowed attributes when datetime_select is used" do content = form_for(User.new, signed: true) do |f| f.datetime_select :name end @data = get_data_from_form(content) end it "should add to the allowed attributes when time_zone_select is used" do content = form_for(User.new, signed: true) do |f| f.time_zone_select :name end @data = get_data_from_form(content) end it "should add to the allowed attributes when radio_button is used" do content = form_for(User.new, signed: true) do |f| f.radio_button :name, ['bar'] end @data = get_data_from_form(content) end end describe "add_signed_fields" do it "should add fields to the marshaled data" do content = form_for(User.new, signed: true) do |f| f.add_signed_fields :name, :address end data = get_data_from_form(content) data['user'].should include(:name, :address) data['user'].size.should == 2 end end describe "fields_for" do it "should nest attributes" do user.stub(widgets: [widget]) content = form_for(user, signed: true) do |f| f.fields_for :widgets do |ff| ff.text_field :name end end data = get_data_from_form(content) data['user'].should include("widgets_attributes" => [:name]) end it "should deeply nest attributes" do content = form_for(:author, url: '/', signed: true) do |f| f.fields_for :books do |ff| ff.text_field :name ff.check_box :hardcover ff.fields_for :pages do |fff| fff.text_field :number end end end data = get_data_from_form(content) data.should include(:author) data[:author].first.should include(:books) data[:author].first[:books].should include(:name, :hardcover, { pages: [:number] }) end specify "nested arrays should not have duplicates" do content = form_for(:author, url: '/', signed: true) do |f| f.fields_for :books do |ff| ff.text_field :name ff.text_field :name end end data = get_data_from_form(content) data[:author].first[:books].size.should == 1 end specify "attribute arrays should not have duplicates" do content = form_for(:author, url: '/', signed: true) do |f| f.text_field :name f.text_field :name end data = get_data_from_form(content) data[:author].size.should == 1 end end describe "form digests" do before { SignedForm.options[:digest] = true } let (:controller) { ControllerRenderer.new } it "should append a digest to the marshaled data" do controller.render template: 'form' data = get_data_from_form(controller.response_body) data[:_options_].should include(:digest) end it "should not digest if the option is disabled" do SignedForm.options[:digest] = false controller.render template: 'form' data = get_data_from_form(controller.response_body) data[:_options_].should_not include(:digest) end it "should get the digest from the view paths" do controller.render template: 'form' data = get_data_from_form(controller.response_body) digestor = data[:_options_][:digest] digestor.view_paths = controller.view_paths digestor.to_s.should == "6a161ab9978322e8251d809b3558ab1a" end it "should set a grace period" do controller.render template: 'form' data = get_data_from_form(controller.response_body) data[:_options_].should include(:digest_expiration) (Time.now..(Time.now + SignedForm.options[:digest_grace_period])).should cover(data[:_options_][:digest_expiration]) end end end