require 'test_helper' class BogusModel include ExtJS::Model class << self def extjs_allow_blank(col) true end def extjs_default(col) nil end def extjs_type(col) nil end def extjs_column_names [:one, :two, :three_id] end def extjs_columns_hash { :one => {}, :two => {}, :three_id => {} } end def extjs_polymorphic_type(id_column_name) id_column_name.to_s.gsub(/_id\Z/, '_type').to_sym end def extjs_primary_key :id end def extjs_associations { :three => { :name => :tree, :type => :belongs_to, :class => nil, :foreign_key => :three_id, :is_polymorphic => false } } end end end class BogusModelChild < BogusModel end class ModelTest < Test::Unit::TestCase context "Rendering DataReader configuration for Person and User" do setup do clean_all end should "Person and User should render a valid Reader config" do reader = Person.extjs_record assert reader.kind_of?(Hash) && reader.has_key?(:fields) && reader.has_key?(:idProperty) end should "Person instance should render with to_record, a Hash containing at least a primary_key" do rec = Person.first.to_record assert_kind_of(Hash, rec) assert_array_has_item(rec.keys, 'has primary key') { |i| i.to_s == Person.extjs_primary_key.to_s } end should "User should render a Reader config" do reader = User.extjs_record assert reader.kind_of?(Hash) && reader.has_key?(:fields) && reader.has_key?(:idProperty) end should "User instance should render with to_record, a Hash containing at least a primary_key" do rec = User.first.to_record assert rec.kind_of?(Hash) && rec.keys.include?(User.extjs_primary_key) end should "User instance should render to_record containing foreign_key of Person" do rec = User.first.to_record assn = User.extjs_associations[:person] assert rec.keys.include?(assn[:foreign_key]) end end context "A User with HABTM relationship with Group" do setup do clean_all UserGroup.destroy_all @user = User.first UserGroup.create(:user => @user, :group => Group.create(:title => "Merb")) UserGroup.create(:user => @user, :group => Group.create(:title => "Rails")) end should "Render to_record should return 2 groups" do User.extjs_fields(:groups) assert @user.to_record[:groups].length == 2 end end context "A User with Person relationship: User.extjs_fields(:password, :person => [:first, {:last => {'sortDir' => 'ASC'}}])" do setup do clean_all User.extjs_fields(:password, {:person => [:first, {:last => {:sortDir => "ASC"}}]}) @fields = User.extjs_record[:fields] end should "User should render a Reader with 4 total fields" do assert @fields.count === 4 end should "Reader fields should contain 'password' field" do assert_array_has_item(@fields, 'has password field') {|f| f[:name] === "password"} end should "Reader fields should contain person_id" do assns = User.extjs_associations assn = assns[:person] assert_array_has_item(@fields, 'has foreign key person_id') {|f| f[:name] === assns[:person][:foreign_key].to_s } end should "Reader fields should contain mapped field 'person.first'" do assert_array_has_item(@fields, 'has person_first') {|f| f[:name] === "person_first" and f[:mapping] === "person.first"} end should "Reader fields should contain mapped field 'person.last'" do assert_array_has_item(@fields, 'has person_last') {|f| f[:name] === "person_last" and f[:mapping] === "person.last"} end should "person.last should have additional configuration 'sortDir' => 'ASC'" do assert_array_has_item(@fields, 'has person.last with sortDir') {|f| f[:name] === "person_last" and f[:sortDir] === 'ASC' } end should "produce a valid to_record record" do person = Person.create!(:first => 'first', :last => 'last', :email => 'email') user = User.create!(:person_id => person.id, :password => 'password') record = user.to_record assert_equal(user.id, record[:id]) assert_equal(person.id, record[:person_id]) assert_equal('password', record[:password]) assert_equal('last', record[:person][:last]) assert_equal('first', record[:person][:first]) end end context "User with standard Person association" do setup do clean_all User.extjs_fields(:id, :password, :person) end should "produce a valid store config" do fields = User.extjs_record[:fields] assert_array_has_item(fields, 'has id') {|f| f[:name] === "id" } assert_array_has_item(fields, 'has person_id') {|f| f[:name] === "person_id" } assert_array_has_item(fields, 'has password') {|f| f[:name] === "password" } assert_array_has_item(fields, 'has person_last') {|f| f[:name] === "person_last" and f[:mapping] == "person.last" } assert_array_has_item(fields, 'has person_first') {|f| f[:name] === "person_first" and f[:mapping] == "person.first" } end should "produce a valid to_record record" do person = Person.create!(:first => 'first', :last => 'last', :email => 'email') user = User.create!(:person_id => person.id, :password => 'password') record = user.to_record assert_equal(user.id, record[:id]) assert_equal(person.id, record[:person_id]) assert_equal('password', record[:password]) assert_equal('last', record[:person][:last]) assert_equal('first', record[:person][:first]) end end context "Fields should render with correct, ExtJS-compatible data-types" do setup do clean_all @fields = DataType.extjs_record[:fields] end should "Understand 'string'" do assert_array_has_item(@fields, 'has string_column with string') {|f| f[:name] == 'string_column' and f[:type] == 'string'} end should "Understand 'integer' as 'int'" do assert_array_has_item(@fields, 'has integer_column with int') {|f| f[:name] == 'integer_column' and f[:type] == 'int'} end should "Understand 'float'" do assert_array_has_item(@fields, 'has float_column with float') {|f| f[:name] == 'float_column' and f[:type] == 'float'} end should "Understand 'decimal' as 'float'" do # Is this correct?? assert_array_has_item(@fields, 'has decimal_column with float') {|f| f[:name] == 'decimal_column' and f[:type] == 'float'} end should "Understand 'date'" do assert_array_has_item(@fields, 'has date_column with date') {|f| f[:name] == 'date_column' and f[:type] == 'date'} end should "Understand 'datetime' as 'date'" do assert_array_has_item(@fields, 'has datetime_column with date') {|f| f[:name] == 'datetime_column' and f[:type] == 'date'} end should "Understand 'time' as 'date'" do assert_array_has_item(@fields, 'has time_column with date') {|f| f[:name] == 'time_column' and f[:type] == 'date'} end should "Understand 'boolean'" do assert_array_has_item(@fields, 'has boolean_column with boolean') {|f| f[:name] == 'boolean_column' and f[:type] == 'boolean'} end should "Understand NOT NULL" do assert_array_has_item(@fields, 'has notnull_column with allowBlank == false') {|f| f[:name] == 'notnull_column' and f[:allowBlank] === false} end should "Understand DEFAULT" do assert_array_has_item(@fields, 'has default_column with defaultValue == true') {|f| f[:name] == 'default_column' and f[:defaultValue] === true} end end context "polymorphic assosiations" do setup do clean_all end should "return nil as class for a polymorphic relation" do assert_equal(nil, Address.extjs_associations[:addressable][:class]) end should "create a proper default store config" do Address.extjs_fields fields = Address.extjs_record[:fields] assert_array_has_item(fields, 'has addressable_id') {|f| f[:name] === 'addressable_id' && !f[:mapping] } assert_array_has_item(fields, 'addressable_type') {|f| f[:name] === 'addressable_type' && !f[:mapping] } end should "create the right store config when including members of the polymorpic association" do Address.extjs_fields :street, :addressable => [:name] fields = Address.extjs_record[:fields] assert_array_has_item(fields, "has addressable_name") {|f| f[:name] === 'addressable_name' && f[:mapping] === 'addressable.name'} assert_array_has_item(fields, "has addressable_id") {|f| f[:name] === 'addressable_id' && !f[:mapping] } assert_array_has_item(fields, "has addressable_type") {|f| f[:name] === 'addressable_type' && !f[:mapping] } end should "fill in the right values for to_record" do Address.extjs_fields :street, :addressable => [:name] location = Location.create!(:name => 'Home') address = location.create_address(:street => 'Main Street 1') record = address.to_record assert_equal({:name => "Home", :id => location.id}, record[:addressable]) assert_equal("Location", record[:addressable_type]) assert_equal(location.id, record[:addressable_id]) assert_equal(address.id, record[:id]) assert_equal("Main Street 1", record[:street]) end end context "single table inheritance" do setup do clean_all end should "fieldsets should be accessible from decendants" do Location.extjs_fieldset :on_location, [:street] fields = House.extjs_record(:on_location)[:fields] assert_array_has_item(fields, 'has street') {|f| f[:name] === 'street' } assert_array_has_not_item(fields, 'has name') {|f| f[:name] === 'name' } end should "fieldsets should be overrideable from decendants" do Location.extjs_fieldset :override, [:street] House.extjs_fieldset :override, [:name] fields = House.extjs_record(:override)[:fields] assert_array_has_not_item(fields, 'has street') {|f| f[:name] === 'street' } assert_array_has_item(fields, 'has name') {|f| f[:name] === 'name' } end end context "ExtJS::Model::ClassMethods" do context "#extjs_extract_fieldset! default" do setup do @fieldset, @fields = BogusModel.extjs_extract_fieldset! [:one, :two, :three] end should "return :default when no fieldset provided" do assert_equal(:'default', @fieldset) end should "not alter the fields array" do assert_equal([:one, :two, :three], @fields) end end context "#extjs_extract_fieldset! with explicit fieldset definition" do setup do @fieldset, @fields = BogusModel.extjs_extract_fieldset! [:explicit, [:one, :two, :three]] end should "return :default when no fieldset provided" do assert_equal(:'explicit', @fieldset) end should "not alter the fields array" do assert_equal([:one, :two, :three], @fields) end end context "#extjs_extract_fieldset! edge cases" do should "called without arguments" do @fieldset, @fields = BogusModel.extjs_extract_fieldset! [] assert_equal(:'default', @fieldset) assert_equal([], @fields) end should "called with only the fieldset and no field arguments" do @fieldset, @fields = BogusModel.extjs_extract_fieldset! [:explicit] assert_equal(:'explicit', @fieldset) assert_equal([], @fields) end end context "#process_fields" do should "handle a simple Array of Symbols" do @fields = BogusModel.process_fields :one, :two, :three assert_equal([{:name => :one}, {:name => :two}, {:name => :three}], @fields) end should "handle a mixed Array where the last item is a Hash" do @fields = BogusModel.process_fields :one, :two, :three => [:three_one, :three_two] assert_equal([{:name => :one}, {:name => :two}, {:name => :three, :fields => [{:name => :three_one}, {:name => :three_two}]}], @fields) end should "handle a mixed Array where a middle item is a Hash" do @fields = BogusModel.process_fields :one, {:two => [:two_one, :two_two]}, :three assert_equal([ {:name => :one}, {:name => :two, :fields => [{:name => :two_one}, {:name => :two_two}]}, {:name => :three}], @fields) end should "handle option :only" do @fields = BogusModel.process_fields :only => [:one, :two, :three] assert_equal([{:name => :one}, {:name => :two}, {:name => :three}], @fields) end should "handle option :exclude" do @fields = BogusModel.process_fields :exclude => [:two] assert_equal([{:name => :one}, {:name => :three_id}], @fields) end should "handle {:field => {:sortDir => 'ASC'}}" do @fields = BogusModel.process_fields({:field => {:sortDir => 'ASC'}}) assert_equal([{:name => :field, :sortDir => 'ASC'}], @fields) end should "handle recursive definition" do @fields = BogusModel.process_fields(:one, {:three => [{:one => [:one, :two]}, {:two => {:sortDir => "ASC"}}]}) assert_equal([{:name => :one}, {:name => :three, :fields => [{:name => :one, :fields => [{:name => :one}, {:name => :two}]}, {:name => :two, :sortDir => 'ASC'}]}], @fields) end should "not touch already correct fields" do @fields = BogusModel.process_fields(:one, {:name => :field,:sortDir => 'ASC'}) assert_equal([{:name => :one},{:name => :field, :sortDir => 'ASC'}], @fields) end should "raise ArgumentError when pass in bogus hash" do assert_raise(ArgumentError) { @fields = BogusModel.process_fields(:one, {:nme => :field,:sortDir => 'ASC'}) } end end context "#extjs_field" do should "type gets set to 'auto' when not present" do @field = BogusModel.extjs_field({:name => :test}) assert_equal('auto', @field[:type]) end should "not touch type when alredy present" do @field = BogusModel.extjs_field({:name => :test, :type => 'untouched'}) assert_equal('untouched', @field[:type]) end should "raise exception when bogus field config passed" do assert_raise(ArgumentError) { BogusModel.extjs_field({:name => :test, "type" => 'untouched'}) } end end context "#extjs_field with ORM config" do should "set allowBlank" do BogusModel.expects(:extjs_allow_blank).returns(false) @field = BogusModel.extjs_field({:name => :test}, stub()) assert_equal(false, @field[:allowBlank]) end should "set type" do BogusModel.expects(:extjs_type).returns('int') @field = BogusModel.extjs_field({:name => :test}, stub()) assert_equal('int', @field[:type]) end should "set defaultValue" do BogusModel.expects(:extjs_default).returns(true) @field = BogusModel.extjs_field({:name => :test}, stub()) assert_equal(true, @field[:defaultValue]) end should "set dateFormat to c it's a date" do BogusModel.expects(:extjs_type).returns('date') @field = BogusModel.extjs_field({:name => :test}, stub()) assert_equal('c', @field[:dateFormat]) end should "not touch dateFormat if it's already set" do BogusModel.expects(:extjs_type).returns('date') @field = BogusModel.extjs_field({:name => :test, :dateFormat => 'not-c'}, stub()) assert_equal('not-c', @field[:dateFormat]) end end context "#extjs_field with Hash config" do should "set correct name and mapping" do @field = BogusModel.extjs_field({:name => :son}, {:mapping => 'grandfather.father', :parent_trail => 'grandfather_father'}) assert_equal('grandfather_father_son', @field[:name]) assert_equal('grandfather.father.son', @field[:mapping]) end should "apply config to field" do @field = BogusModel.extjs_field({:name => :son}, {:sortDir => 'ASC'}) assert_equal('ASC', @field[:sortDir]) end end context "#extjs_get_fields_for_fieldset" do should "return full list of columns for fieldset that was not defined, yet" do @fields = BogusModel.extjs_get_fields_for_fieldset :not_there assert_equal(BogusModel.process_fields(*BogusModel.extjs_column_names), @fields) end should "return the right fields for a fieldset that was defined before in the same class" do BogusModel.extjs_fieldset :fieldset_was_defined, [:one] @fields = BogusModel.extjs_get_fields_for_fieldset :fieldset_was_defined assert_equal(BogusModel.process_fields(:one), @fields) end should "return the fieldset of the ancestor when it was only defined in the ancestor" do BogusModel.extjs_fieldset :fieldset_was_defined_in_ancestor, [:one] @fields = BogusModelChild.extjs_get_fields_for_fieldset :fieldset_was_defined_in_ancestor assert_equal(BogusModel.process_fields(:one), @fields) end should "return the fieldset of the child when it was defined in the child and the ancestor" do BogusModel.extjs_fieldset :fieldset_was_defined_in_both, [:one] BogusModelChild.extjs_fieldset :fieldset_was_defined_in_both, [:two] @fields = BogusModelChild.extjs_get_fields_for_fieldset :fieldset_was_defined_in_both assert_equal(BogusModel.process_fields(:two), @fields) end end end protected def assert_array_has_item array, item_description, &blk assert array.find {|i| blk.call(i) }, "The array #{array.inspect} should #{item_description} but it does not" end def assert_array_has_not_item array, item_description, &blk assert !array.find {|i| blk.call(i) }, "The array #{array.inspect} should not #{item_description} but it does" end end