# encoding: UTF-8 require File.expand_path('../../test_helper', __FILE__) describe "Boxes" do before do @site = setup_site end after do teardown_site end describe "Box definitions" do before do class ::MyBoxClass < Box; end class ::MyContentClass < Piece; end class ::MyContentClass2 < MyContentClass; end MyContentClass.field :description end after do Object.send(:remove_const, :MyContentClass2) rescue nil Object.send(:remove_const, :MyContentClass) rescue nil Object.send(:remove_const, :MyBoxClass) rescue nil end it "start empty" do MyContentClass.boxes.length.must_equal 0 end it "have a flag showing there are no defined boxes" do refute MyContentClass.has_boxes? end it "be definable with a name" do MyContentClass.box :images0 MyContentClass.boxes.length.must_equal 1 MyContentClass.boxes.first.name.must_equal :images0 assert MyContentClass.has_boxes? end it "have a boolean test for emptiness" do MyContentClass.box :images0 instance = MyContentClass.new assert instance.images0.empty? instance.images0 << MyContentClass.new refute instance.images0.empty? end it "always return a symbol for the name" do MyContentClass.box 'images0' MyContentClass.boxes.first.name.must_equal :images0 end it "create a method of the same name" do MyContentClass.box :images1 MyContentClass.box :images2, :type => :MyBoxClass instance = MyContentClass.new instance.images1.class.superclass.must_equal Box instance.images2.class.superclass.must_equal MyBoxClass end it "be available by name" do MyContentClass.box :images1 MyContentClass.box :images2, :type => :MyBoxClass MyContentClass.boxes[:images1].must_equal MyContentClass.boxes.first MyContentClass.boxes[:images2].must_equal MyContentClass.boxes.last instance = MyContentClass.new instance.boxes[:images1].class.superclass.must_equal Box instance.boxes[:images2].class.superclass.must_equal MyBoxClass end it "accept a custom instance class" do MyContentClass.box :images1, :type => MyBoxClass MyContentClass.boxes.first.instance_class.superclass.must_equal MyBoxClass end it "accept a custom instance class as a string" do MyContentClass.box :images1, :type => 'MyBoxClass' MyContentClass.boxes.first.instance_class.superclass.must_equal MyBoxClass end it "accept a custom instance class as a symbol" do MyContentClass.box :images1, :type => :MyBoxClass MyContentClass.boxes.first.instance_class.superclass.must_equal MyBoxClass end it "Instantiate a box of the correct class" do MyContentClass.box :images1 MyContentClass.box :images2, :type => :MyBoxClass instance = MyContentClass.new instance.boxes.first.class.superclass.must_equal Box instance.boxes.last.class.superclass.must_equal MyBoxClass end it "give access to the prototype within the instance" do MyContentClass.box :images1 instance = MyContentClass.new instance.boxes[:images1]._prototype.must_equal MyContentClass.boxes[:images1] end it "Use the name as the title by default" do MyContentClass.box :band_and_band MyContentClass.box :related_items MyContentClass.boxes.first.title.must_equal "Band & Band" MyContentClass.boxes.last.title.must_equal "Related Items" end it "have 'title' option" do MyContentClass.box :images4, :title => "Custom Title" MyContentClass.boxes.first.title.must_equal "Custom Title" end it "inherit boxes from superclass" do MyContentClass.box :images1, :type => :MyBoxClass MyContentClass2.box :images2 MyContentClass2.boxes.length.must_equal 2 instance = MyContentClass2.new instance.images1.class.superclass.must_equal MyBoxClass instance.images2.class.superclass.must_equal Box instance.boxes.length.must_equal 2 end it "know their ordering in the container" do MyContentClass.box :box1 MyContentClass.box :box2 MyContentClass.box :box3 MyContentClass.box_order :box3, :box2, :box1 MyContentClass.boxes.box3.position.must_equal 0 MyContentClass.boxes.box2.position.must_equal 1 MyContentClass.boxes.box1.position.must_equal 2 instance = MyContentClass.new instance.box3.position.must_equal 0 instance.box2.position.must_equal 1 instance.box1.position.must_equal 2 end describe "instances" do it "have a connection to their owner" do MyContentClass.box :box1 instance = MyContentClass.new instance.box1.owner.must_equal instance instance.box1.parent.must_equal instance end it "have a link to their container (which is their owner)" do MyContentClass.box :box1 instance = MyContentClass.new instance.box1.container.must_equal instance instance.box1.container.must_equal instance end it "return their owner as content_instance" do MyContentClass.box :box1 instance = MyContentClass.new instance.box1.content_instance.must_equal instance end end describe "ranges" do before do MyContentClass.box :images1 MyContentClass.box :images2 MyContentClass.box :images3 MyContentClass.box :images4 MyContentClass.box :images5 MyContentClass2.box :images6 @instance = MyContentClass.new @instance2 = MyContentClass2.new end it "allow access to groups of boxes through ranges" do @instance.boxes[1..-2].map { |b| b.box_name }.must_equal [:images2, :images3, :images4] @instance2.boxes[1..-2].map { |b| b.box_name }.must_equal [:images2, :images3, :images4, :images5] end it "allow you to pass a list of names" do @instance.boxes[:images1, :images4].map { |b| b.box_name }.must_equal [:images1, :images4] @instance2.boxes[:images1, :images6].map { |b| b.box_name }.must_equal [:images1, :images6] end it "allow a mix of names and indexes" do @instance.boxes[0..2, :images5].map { |b| b.box_name }.must_equal [:images1, :images2, :images3, :images5] end end describe "with superclasses" do before do MyContentClass.box :images6, :tag => :main @subclass1 = Class.new(MyContentClass) do box :monkeys, :tag => :main box :apes end @subclass2 = Class.new(@subclass1) do box :peanuts end end it "inherit boxes from its superclass" do @subclass2.boxes.length.must_equal 4 @subclass2.boxes.map { |s| s.name }.must_equal [:images6, :monkeys, :apes, :peanuts] # @subclass2.boxes.tagged(:main).length.must_equal 2 instance = @subclass2.new instance.boxes.length.must_equal 4 end it "allow customisation of the box order" do new_order = [:peanuts, :apes, :images6, :monkeys] @subclass2.box_order *new_order @subclass2.boxes.map { |s| s.name }.must_equal new_order end it "take order of instance boxes from class defn" do new_order = [:peanuts, :apes, :images6, :monkeys] @subclass2.box_order *new_order instance = @subclass2.new instance.boxes.map { |e| e.box_name.to_sym }.must_equal new_order end end it "accept values for the box's fields" it "allow overwriting of class definitions using a block" end describe "Box classes" do before do @site.stubs(:template_root).returns(File.expand_path('../../fixtures/templates/boxes', __FILE__)) class ::MyContentClass < ::Piece; end class ::MyBoxClass < Box; end MyBoxClass.field :title, :string MyBoxClass.field :description, :string MyContentClass.box :images, :class => :MyBoxClass, :fields => { :title => "Default Title", :description => "Default Description" } @content = MyContentClass.new end after do Object.send(:remove_const, :MyContentClass) rescue nil Object.send(:remove_const, :MyBoxClass) rescue nil end it "have fields" do MyBoxClass.fields.length.must_equal 2 MyBoxClass.field :another, :string MyBoxClass.fields.length.must_equal 3 end describe "with fields" do it "save their field values" do @content.images.title = "something" @content.images.description = "description here" @content.save @content.reload @content.images.title.value.must_equal "something" @content.images.description.value.must_equal "description here" end it "take initial values from box definition" do @content.images.title.value.must_equal "Default Title" @content.images.description.value.must_equal "Default Description" end end it "allow inline definition of fields" do MyContentClass.box :partners do field :name, :string field :logo, :image field :description, :string end instance = MyContentClass.new assert instance.partners.name.class < Spontaneous::Field::String instance.partners.name = "Howard" instance.partners.description = "Here is Howard" instance.save instance = Content[instance.id] instance.partners.name.value.must_equal "Howard" instance.partners.description.value.must_equal "Here is Howard" end # true? it "default to template in root with the same name" end describe "Box content" do before do class ::BlankContent < ::Piece; end class ::StyledContent < ::Piece; end BlankContent.style :blank1 BlankContent.style :blank2 BlankContent.style :blank3 BlankContent.box :images BlankContent.box :words StyledContent.box :one do allow :BlankContent, :style => :blank2 end StyledContent.box :two do allow :BlankContent, :styles => [:blank3, :blank2] end @parent = BlankContent.new end after do Object.send(:remove_const, :BlankContent) rescue nil Object.send(:remove_const, :StyledContent) rescue nil end it "be addable" do child1 = BlankContent.new child2 = BlankContent.new child3 = BlankContent.new @parent.images << child1 @parent.words << child2 child1.box.schema_id.must_equal @parent.images.schema_id child2.box.schema_id.must_equal @parent.words.schema_id @parent.save child1.images << child3 child1.save @parent = Content[@parent.id] child1.reload; child2.reload; child3.reload @parent.images.contents.must_equal [child1] @parent.images.contents.must_equal [child1] @parent.words.contents.must_equal [child2] @parent.words.contents.must_equal [child2] @parent.contents.to_a.must_equal [child1, child2] child1.images.contents.must_equal [child3] child1.contents.to_a.must_equal [child3] @parent.images.contents.first.box.must_equal @parent.images @parent.words.contents.first.box.must_equal @parent.words @parent.contents.first.box.must_equal @parent.images end it "choose correct style" do styled = StyledContent.new child1 = BlankContent.new child2 = BlankContent.new child3 = BlankContent.new styled.one << child1 styled.two << child2 styled.save styled = Content.get styled.id styled.one.contents.first.style.name.must_equal :blank2 styled.two.contents.first.style.name.must_equal :blank3 end it "be insertable at any position" do BlankContent.box :box3 BlankContent.box :box4 instance = BlankContent.new count = 4 [:images, :words, :box3, :box4].map { |name| instance.boxes[name] }.each do |box| count.times { |n| box << StyledContent.new(:label => n)} end instance.box4.insert(1, StyledContent.new(:label => "a")) instance.box4.contents.map { |e| e.label }.must_equal ["0", "a", "1", "2", "3"] instance.box4.insert(5, StyledContent.new(:label => "b")) instance.box4.contents.map { |e| e.label }.must_equal ["0", "a", "1", "2", "3", "b"] instance.box3.insert(2, StyledContent.new(:label => "c")) instance.box3.contents.map { |e| e.label }.must_equal ["0", "1", "c", "2", "3"] end end describe "Allowed types" do before do class ::Allowed1 < Content style :frank style :freddy end class ::Allowed2 < Content style :john style :paul style :ringo style :george end class ::Allowed3 < Content style :arthur style :lancelot end class ::Allowed4 < Content; end class ::Allowed11 < ::Allowed1; end class ::Allowed111 < ::Allowed1; end class ::Parent < Box allow :Allowed1 allow Allowed2, :styles => [:ringo, :george] allow 'Allowed3' end class ::ChildClass < ::Parent end class ::Allowable < Content box :parents, :type => :Parent end class ::Mixed < Box allow_subclasses :Allowed1 end end after do [:Parent, :Allowed1, :Allowed11, :Allowed111, :Allowed2, :Allowed3, :Allowed4, :ChildClass, :Allowable, :Mixed].each { |k| Object.send(:remove_const, k) } rescue nil end it "have a list of allowed types" do Parent.allowed.length.must_equal 3 end it "have understood the type parameter" do Parent.allowed[0].instance_class.must_equal Allowed1 Parent.allowed[1].instance_class.must_equal Allowed2 Parent.allowed[2].instance_class.must_equal Allowed3 end # TODO: decide on whether testing class definitions is a good idea # it "raise an error when given an invalid type name" do # lambda { Parent.allow :WhatTheHellIsThis }.must_raise(NameError) # end it "allow all styles by default" do Parent.allowed[2].styles(nil).must_equal Allowed3.styles end it "have a list of allowable styles" do Parent.allowed[1].styles(nil).length.must_equal 2 Parent.allowed[1].styles(nil).map { |s| s.name }.must_equal [:ringo, :george] end # TODO: decide on whether verifying style names is a good idea # it "raise an error if we try to use an unknown style" do # lambda { Parent.allow :Allowed3, :styles => [:merlin, :arthur] }.must_raise(Spontaneous::UnknownStyleException) # end it "use a configured style when adding a defined allowed type" do a = Allowable.new b = Allowed2.new a.parents << b a.parents.contents.first.style.prototype.must_equal Allowed2.styles[:ringo] end it "know what the available styles are for an entry" do a = Allowable.new b = Allowed2.new c = Allowed3.new a.parents << b a.parents << c a.parents.available_styles(b).map { |s| s.name }.must_equal [:ringo, :george] a.parents.available_styles(c).map { |s| s.name }.must_equal [:arthur, :lancelot] end it "inherit allowed types from superclass" do ChildClass.allowed.must_equal Parent.allowed Allowable.boxes.parents.allowed_types(nil).must_equal [Allowed1, Allowed2, Allowed3] class ::AChild < Allowable box :parents do allow :Allowed11 end end class ::AChild2 < AChild box :parents, :title => "Things" do allow :Allowed111 end end box = AChild.boxes.parents box.allowed_types(nil).must_equal [Allowed1, Allowed2, Allowed3, Allowed11] box = AChild2.boxes.parents box.title.must_equal "Things" box.allowed_types(nil).must_equal [Allowed1, Allowed2, Allowed3, Allowed11, Allowed111] Object.send(:remove_const, :AChild) rescue nil Object.send(:remove_const, :AChild2) rescue nil end it "include a subtype's allowed list as well as the supertype's" do ChildClass.allow :Allowed4 ChildClass.allowed.map {|a| a.instance_class }.must_equal (Parent.allowed.map {|a| a.instance_class } + [Allowed4]) end it "propagate allowed types to slots" do instance = Allowable.new instance.parents.allowed_types.must_equal Parent.allowed_types end it "correctly allow addition of subclasses" do Mixed.allowed_types.must_equal [Allowed11, Allowed111] end it "create inline classes if passed a definition block" do allowed = ChildClass.allow :InlineType do field :title end inline_type = allowed.instance_class inline_type.fields.length.must_equal 1 inline_type.fields.first.name.must_equal :title inline_type.name.must_equal "ChildClass::InlineType" end it "use the given supertype for inline classes" do allowed = ChildClass.allow :InlineType, :supertype => :Allowed1 do field :title end inline_type = allowed.instance_class inline_type.ancestors[0..1].must_equal [ChildClass::InlineType, Allowed1] end it "add the created class to the schema immediately" do allowed = ChildClass.allow :InlineType, :supertype => :Allowed1 do field :title end assert @site.schema.classes.map(&:to_s).include?("ChildClass::InlineType"), "#{@site.schema.classes} does not include ChildClass::InlineType" end end describe "Box groups" do before do class ::A < ::Piece box_group :inner do box :a box :b end box_group :outer do box :c box :d end end class ::B < ::A box_group :outer do box :e end end class ::C < ::B box :f, :group => :inner end @a = ::A.new @b = ::B.new @c = ::C.new [@a, @b, @c].each do |instance| instance.boxes[:a].stubs(:render).with(anything).returns("[a]") instance.boxes[:b].stubs(:render).with(anything).returns("[b]") instance.boxes[:c].stubs(:render).with(anything).returns("[c]") instance.boxes[:d].stubs(:render).with(anything).returns("[d]") end @b.boxes[:e].stubs(:render).with(anything).returns("[e]") @c.boxes[:e].stubs(:render).with(anything).returns("[e]") @c.boxes[:f].stubs(:render).with(anything).returns("[f]") end after do Object.send(:remove_const, :A) rescue nil Object.send(:remove_const, :B) rescue nil Object.send(:remove_const, :C) rescue nil end it "successfully allocate boxes" do @a.boxes.inner.must_equal [@a.boxes[:a], @a.boxes[:b]] @a.boxes.outer.must_equal [@a.boxes[:c], @a.boxes[:d]] @b.boxes.inner.must_equal [@b.boxes[:a], @b.boxes[:b]] @b.boxes.outer.must_equal [@b.boxes[:c], @b.boxes[:d], @b.boxes[:e]] @c.boxes.inner.must_equal [@c.boxes[:a], @c.boxes[:b], @c.boxes[:f]] @c.boxes.outer.must_equal [@c.boxes[:c], @c.boxes[:d], @c.boxes[:e]] end it "successfully render groups" do @a.boxes.inner.render.must_equal "[a][b]" @a.boxes.outer.render.must_equal "[c][d]" @b.boxes.inner.render.must_equal "[a][b]" @b.boxes.outer.render.must_equal "[c][d][e]" @c.boxes.inner.render.must_equal "[a][b][f]" @c.boxes.outer.render.must_equal "[c][d][e]" end it "return an empty array when asking for an unknown box group" do @a.boxes.group(:nothing).must_equal [] end end end