test/model_test.rb in extjs-mvc-0.2.8 vs test/model_test.rb in extjs-mvc-0.3.0

- old
+ new

@@ -1,29 +1,83 @@ 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 - # common stuff for all tests. + 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") + 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 rec.kind_of?(Hash) && rec.keys.include?(Person.extjs_primary_key.to_s) + 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") + 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.to_s) + 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]) @@ -31,86 +85,348 @@ 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 + 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 - User.extjs_fields(:password, :person => [:first, {:last => {"sortDir" => "ASC"}}]) - @fields = User.extjs_record["fields"] + 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 @fields.find {|f| f[:name] === "password"} + 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] || assns["person"] - assert @fields.find {|f| f[:name] === assns[:person][:foreign_key] } + 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 @fields.find {|f| f[:name] === "person_first" and f["mapping"] === "person.first"} + 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 @fields.find {|f| f[:name] === "person_last" and f["mapping"] === "person.last"} + 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 @fields.find {|f| f[:name] === "person_last" and f["sortDir"] === 'ASC' } + 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 - @fields = DataType.extjs_record["fields"] + clean_all + @fields = DataType.extjs_record[:fields] end should "Understand 'string'" do - assert @fields.find {|f| f[:name] === 'string_column' && f["type"].to_s === 'string'} + 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 @fields.find {|f| f[:name] === 'integer_column' && f["type"].to_s === 'int'} + 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 @fields.find {|f| f[:name] === 'float_column' && f["type"].to_s === 'float'} + 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 @fields.find {|f| f[:name] === 'decimal_column' && f["type"].to_s === 'float'} + 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 @fields.find {|f| f[:name] === 'date_column' && f["type"].to_s === 'date'} + 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 @fields.find {|f| f[:name] === 'datetime_column' && f["type"].to_s === 'date'} + 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 @fields.find {|f| f[:name] === 'time_column' && f["type"].to_s === 'date'} + 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 @fields.find {|f| f[:name] === 'boolean_column' && f["type"].to_s === 'boolean'} + 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 @fields.find {|f| f[:name] === 'notnull_column' && f["allowBlank"] === false} + 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 # TODO implement this. - assert @fields.find {|f| f[:name] === 'default_column' && f["default"] === true} + 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