# encoding: UTF-8 require 'spec_helper' module Alchemy describe Page do let(:rootpage) { Page.root } let(:language) { Language.get_default } let(:klingonian) { FactoryGirl.create(:klingonian) } let(:language_root) { FactoryGirl.create(:language_root_page) } let(:page) { mock(:page, :page_layout => 'foo') } let(:public_page) { FactoryGirl.create(:public_page) } let(:news_page) { FactoryGirl.create(:public_page, :page_layout => 'news', :do_not_autogenerate => false) } # Validations context 'validations' do context "Creating a normal content page" do let(:contentpage) { FactoryGirl.build(:page) } context "when its urlname exists as global page" do it "it should be possible to save." do contentpage.urlname = "existing_twice" global_with_same_urlname = FactoryGirl.create(:page, :urlname => "existing_twice", :layoutpage => true) contentpage.should be_valid end end it "should validate the page_layout" do contentpage.page_layout = nil contentpage.save contentpage.should have(1).error_on(:page_layout) end it "should validate the parent_id" do contentpage.parent_id = nil contentpage.save contentpage.should have(1).error_on(:parent_id) end it "should validate the uniqueness of urlname" do with_same_urlname = FactoryGirl.create(:page, :urlname => "existing_twice") contentpage.urlname = 'existing_twice' contentpage.should_not be_valid end context "with url_nesting set to true" do before { Config.stub!(:get).and_return(true) } it "should only validate urlname dependent of parent" do other_parent = FactoryGirl.create(:page, parent_id: Page.root.id) with_same_urlname = FactoryGirl.create(:page, :urlname => "existing_twice") contentpage.urlname = 'existing_twice' contentpage.parent_id = other_parent.id contentpage.should be_valid end it "should validate urlname dependent of parent" do with_same_urlname = FactoryGirl.create(:page, :urlname => "existing_twice") contentpage.urlname = 'existing_twice' contentpage.should_not be_valid end end end context "creating the rootpage without parent_id and page_layout" do before do Page.delete_all @rootpage = FactoryGirl.build(:page, :parent_id => nil, :page_layout => nil, :name => 'Rootpage') end it "should be valid" do @rootpage.save @rootpage.should be_valid end end context "saving a systempage" do before do @systempage = FactoryGirl.build(:systempage) end it "should not validate the page_layout" do @systempage.save @systempage.should be_valid end end end # Callbacks context 'callbacks' do let(:page) do FactoryGirl.create(:page, :name => 'My Testpage', :language => language, :parent_id => language_root.id) end context 'before_save' do it "should not set the title automatically if the name changed but title is not blank" do page.name = "My Renaming Test" page.save; page.reload page.title.should == "My Testpage" end it "should not automatically set the title if it changed its value" do page.title = "I like SEO" page.save; page.reload page.title.should == "I like SEO" end end context 'after_update' do context "urlname has changed" do it "should store legacy url if page is not redirect to external page" do page.urlname = 'new-urlname' page.save! page.legacy_urls.should_not be_empty page.legacy_urls.first.urlname.should == 'my-testpage' end it "should not store legacy url if page is redirect to external page" do page.urlname = 'new-urlname' page.page_layout = "external" page.save! page.legacy_urls.should be_empty end it "should not store legacy url twice for same urlname" do page.urlname = 'new-urlname' page.save! page.urlname = 'my-testpage' page.save! page.urlname = 'another-urlname' page.save! page.legacy_urls.select { |u| u.urlname == 'my-testpage' }.size.should == 1 end end context "urlname has not changed" do it "should not store a legacy url" do page.urlname = 'my-testpage' page.save! page.legacy_urls.should be_empty end end end context "a normal page" do before do @page = FactoryGirl.build(:page, :language_code => nil, :language => klingonian, :do_not_autogenerate => false) end it "should set the language code" do @page.save @page.language_code.should == "kl" end it "should autogenerate the elements" do @page.save @page.elements.should_not be_empty end it "should not autogenerate elements that are already on the page" do @page.elements << FactoryGirl.create(:element, :name => 'header') @page.save @page.elements.select { |e| e.name == 'header' }.length.should == 1 end context "with cells" do before do @page.stub!(:definition).and_return({'name' => 'with_cells', 'cells' => ['header', 'main']}) end it "should have the generated elements in their cells" do @page.stub!(:cell_definitions).and_return([{'name' => 'header', 'elements' => ['article']}]) @page.save @page.cells.where(:name => 'header').first.elements.should_not be_empty end context "and no elements in cell definitions" do it "should have the elements in the nil cell" do @page.stub!(:cell_definitions).and_return([{'name' => 'header', 'elements' => []}]) @page.save @page.cells.collect(&:elements).flatten.should be_empty end end end context "with children getting restricted set to true" do before do @page.save @child1 = FactoryGirl.create(:page, :name => 'Child 1', :parent_id => @page.id) @page.reload @page.restricted = true @page.save end it "should restrict all its children" do @child1.reload @child1.restricted?.should be_true end end context "with restricted parent gets created" do before do @page.save @page.parent.update_attributes(:restricted => true) @new_page = FactoryGirl.create(:page, :name => 'New Page', :parent_id => @page.id) end it "should also be restricted" do @new_page.restricted?.should be_true end end context "with do_not_autogenerate set to true" do before do @page.do_not_autogenerate = true end it "should not autogenerate the elements" do @page.save @page.elements.should be_empty end end end context "a systempage" do before do @page = FactoryGirl.create(:systempage) end it "should not get the language code for language" do @page.language_code.should be_nil end it "should not autogenerate the elements" do @page.elements.should be_empty end end context "after changing the page layout" do let(:news_element) { FactoryGirl.create(:element, :name => 'news') } it "all elements not allowed on this page should be trashed" do news_page.elements << news_element news_page.update_attributes :page_layout => 'standard' news_page.elements.trashed.should include(news_element) end it "should autogenerate elements" do news_page.update_attributes :page_layout => 'standard' news_page.elements.available.collect(&:name).should include('header') end end end # ClassMethods (a-z) describe '.all_from_clipboard_for_select' do context "with clipboard holding pages having non unique page layout" do it "should return the pages" do page_1 = FactoryGirl.create(:page, :language => language) page_2 = FactoryGirl.create(:page, :language => language, :name => 'Another page') clipboard = [ {:id => page_1.id, :action => "copy"}, {:id => page_2.id, :action => "copy"} ] Page.all_from_clipboard_for_select(clipboard, language.id).should include(page_1, page_2) end end context "with clipboard holding a page having unique page layout" do it "should not return any pages" do page_1 = FactoryGirl.create(:page, :language => language, :page_layout => 'contact') clipboard = [ {:id => page_1.id, :action => "copy"} ] Page.all_from_clipboard_for_select(clipboard, language.id).should == [] end end context "with clipboard holding two pages. One having a unique page layout." do it "should return one page" do page_1 = FactoryGirl.create(:page, :language => language, :page_layout => 'standard') page_2 = FactoryGirl.create(:page, :name => 'Another page', :language => language, :page_layout => 'contact') clipboard = [ {:id => page_1.id, :action => "copy"}, {:id => page_2.id, :action => "copy"} ] Page.all_from_clipboard_for_select(clipboard, language.id).should == [page_1] end end end describe '.all_locked' do it "should return 1 page that is blocked by a user at the moment" do FactoryGirl.create(:public_page, :locked => true, :name => 'First Public Child', :parent_id => language_root.id, :language => language) Page.all_locked.should have(1).pages end end describe '.contentpages' do before do layoutroot = Page.find_or_create_layout_root_for(klingonian.id) @layoutpage = FactoryGirl.create(:public_page, :name => 'layoutpage', :layoutpage => true, :parent_id => layoutroot.id, :language => klingonian) @klingonian_lang_root = FactoryGirl.create(:language_root_page, :name => 'klingonian_lang_root', :layoutpage => nil, :language => klingonian) @contentpage = FactoryGirl.create(:public_page, :name => 'contentpage', :parent_id => language_root.id, :language => language) end it "should return a collection of contentpages" do Page.contentpages.to_a.should include(language_root, @klingonian_lang_root, @contentpage) end it "should not contain pages with attribute :layoutpage set to true" do Page.contentpages.to_a.select { |p| p.layoutpage == true }.should be_empty end it "should contain pages with attribute :layoutpage set to nil" do Page.contentpages.to_a.select { |p| p.layoutpage == nil }.should include(@klingonian_lang_root) end end describe '.copy' do let(:page) { FactoryGirl.create(:page, :name => 'Source') } subject { Page.copy(page) } it "the copy should have added (copy) to name" do subject.name.should == "#{page.name} (Copy)" end context "page with tags" do before { page.tag_list = 'red, yellow'; page.save } it "the copy should have source tag_list" do # The order of tags varies between postgresql and sqlite/mysql # This is related to acts-as-taggable-on v.2.4.1 # To fix the spec we sort the tags until the issue is solved (https://github.com/mbleigh/acts-as-taggable-on/issues/363) subject.tag_list.should_not be_empty subject.tag_list.sort.should == page.tag_list.sort end end context "page with elements" do before { page.elements << FactoryGirl.create(:element) } it "the copy should have source elements" do subject.elements.should_not be_empty subject.elements.count.should == page.elements.count end end context "page with trashed elements" do before do page.elements << FactoryGirl.create(:element) page.elements.first.trash end it "the copy should not hold a copy of the trashed elements" do subject.elements.should be_empty end end context "page with cells" do before { page.cells << FactoryGirl.create(:cell) } it "the copy should have source cells" do subject.cells.should_not be_empty subject.cells.count.should == page.cells.length # It must be length, because! end end context "page with autogenerate elements" do before do page = FactoryGirl.create(:page) page.stub!(:definition).and_return({'name' => 'standard', 'elements' => ['headline'], 'autogenerate' => ['headline']}) end it "the copy should not autogenerate elements" do subject.elements.should be_empty end end context "with different page name given" do subject { Page.copy(page, {:name => 'Different name'}) } it "should take this name" do subject.name.should == 'Different name' end end end describe '.create' do context "before/after filter" do it "should automatically set the title from its name" do page = FactoryGirl.create(:page, :name => 'My Testpage', :language => language, :parent_id => language_root.id) page.title.should == 'My Testpage' end it "should get a webfriendly urlname" do page = FactoryGirl.create(:page, :name => 'klingon$&stößel ', :language => language, :parent_id => language_root.id) page.urlname.should == 'klingon-stoessel' end it "should generate a three letter urlname from two letter name" do page = FactoryGirl.create(:page, :name => 'Au', :language => language, :parent_id => language_root.id) page.urlname.should == '-au' end it "should generate a three letter urlname from two letter name with umlaut" do page = FactoryGirl.create(:page, :name => 'Aü', :language => language, :parent_id => language_root.id) page.urlname.should == 'aue' end it "should generate a three letter urlname from one letter name" do page = FactoryGirl.create(:page, :name => 'A', :language => language, :parent_id => language_root.id) page.urlname.should == '--a' end it "should add a user stamper" do page = FactoryGirl.create(:page, :name => 'A', :language => language, :parent_id => language_root.id) page.class.stamper_class.to_s.should == 'Alchemy::User' end end end describe '.language_roots' do it "should return 1 language_root" do FactoryGirl.create(:public_page, :name => 'First Public Child', :parent_id => language_root.id, :language => language) Page.language_roots.should have(1).pages end end describe '.layout_description' do it "should raise Exception if the page_layout could not be found in the definition file" do expect { page.layout_description }.to raise_error end context "for a language root page" do it "should return the page layout description as hash" do language_root.layout_description['name'].should == 'intro' end it "should return an empty hash for root page" do rootpage.layout_description.should == {} end end end describe '.layoutpages' do it "should return 1 layoutpage" do FactoryGirl.create(:public_page, :layoutpage => true, :name => 'Layoutpage', :parent_id => rootpage.id, :language => language) Page.layoutpages.should have(1).pages end end describe '.not_locked' do it "should return pages that are not blocked by a user at the moment" do FactoryGirl.create(:public_page, :locked => true, :name => 'First Public Child', :parent_id => language_root.id, :language => language) FactoryGirl.create(:public_page, :name => 'Second Public Child', :parent_id => language_root.id, :language => language) Page.not_locked.should have(3).pages end end describe '.not_restricted' do it "should return 2 accessible pages" do FactoryGirl.create(:public_page, :name => 'First Public Child', :restricted => true, :parent_id => language_root.id, :language => language) Page.not_restricted.should have(2).pages end end describe '.public' do it "should return pages that are public" do FactoryGirl.create(:public_page, :name => 'First Public Child', :parent_id => language_root.id, :language => language) FactoryGirl.create(:public_page, :name => 'Second Public Child', :parent_id => language_root.id, :language => language) Page.published.should have(3).pages end end describe '.restricted' do it "should return 1 restricted page" do FactoryGirl.create(:public_page, :name => 'First Public Child', :restricted => true, :parent_id => language_root.id, :language => language) Page.restricted.should have(1).pages end end describe '.rootpage' do it "should contain one rootpage" do Page.rootpage.should be_instance_of(Page) end end describe '.visible' do it "should return 1 visible page" do FactoryGirl.create(:public_page, :name => 'First Public Child', :visible => true, :parent_id => language_root.id, :language => language) Page.visible.should have(1).pages end end # InstanceMethods (a-z) describe '#available_element_definitions' do let(:page) { FactoryGirl.build_stubbed(:public_page) } it "returns all element definitions of available elements" do page.available_element_definitions.should be_an(Array) page.available_element_definitions.collect { |e| e['name'] }.should include('header') end context "with unique elements already on page" do let(:element) { FactoryGirl.build_stubbed(:unique_element) } before { page.stub_chain(:elements, :not_trashed, :pluck).and_return([element.name]) } it "does not return unique element definitions" do page.available_element_definitions.collect { |e| e['name'] }.should include('article') page.available_element_definitions.collect { |e| e['name'] }.should_not include('header') end end context "for page_layout not existing" do let(:page) { FactoryGirl.build_stubbed(:page, page_layout: 'not_existing_one') } it "should raise error" do expect { page.available_element_definitions }.to raise_error(Alchemy::PageLayoutDefinitionError) end end context 'limited amount' do let(:page) { FactoryGirl.build_stubbed(:page, page_layout: 'columns') } let(:unique_element) { FactoryGirl.build_stubbed(:unique_element, name: 'unique_headline') } let(:element_1) { FactoryGirl.build_stubbed(:element, name: 'column_headline') } let(:element_2) { FactoryGirl.build_stubbed(:element, name: 'column_headline') } let(:element_3) { FactoryGirl.build_stubbed(:element, name: 'column_headline') } before { Element.stub(:definitions).and_return([ { 'name' => 'column_headline', 'amount' => 3, 'contents' => [{'name' => 'headline', 'type' => 'EssenceText'}] }, { 'name' => 'unique_headline', 'unique' => true, 'amount' => 3, 'contents' => [{'name' => 'headline', 'type' => 'EssenceText'}] } ]) PageLayout.stub(:get).and_return({ 'name' => 'columns', 'elements' => ['column_headline', 'unique_headline'], 'autogenerate' => ['unique_headline', 'column_headline', 'column_headline', 'column_headline'] }) page.stub_chain(:elements, :not_trashed, :pluck).and_return([unique_element.name, element_1.name, element_2.name, element_3.name]) } it "should be readable" do element = page.element_definitions_by_name('column_headline').first element['amount'].should be 3 end it "should limit elements" do page.available_element_definitions.collect { |e| e['name'] }.should_not include('column_headline') end it "should be ignored if unique" do page.available_element_definitions.collect { |e| e['name'] }.should_not include('unique_headline') end end end describe '#available_element_names' do let(:page) { FactoryGirl.build_stubbed(:page) } it "returns all names of elements that could be placed on current page" do page.available_element_names == %w(header article) end end describe '#cache_key' do let(:page) { stub_model(Page) } subject { page } its(:cache_key) { should match(page.id.to_s) } end describe '#cell_definitions' do before do @page = FactoryGirl.build(:page, :page_layout => 'foo') @page.stub!(:layout_description).and_return({'name' => "foo", 'cells' => ["foo_cell"]}) @cell_descriptions = [{'name' => "foo_cell", 'elements' => ["1", "2"]}] Cell.stub!(:definitions).and_return(@cell_descriptions) end it "should return all cell definitions for its page_layout" do @page.cell_definitions.should == @cell_descriptions end it "should return empty array if no cells defined in page layout" do @page.stub!(:layout_description).and_return({'name' => "foo"}) @page.cell_definitions.should == [] end end describe '#destroy' do context "with trashed but still assigned elements" do before { news_page.elements.map(&:trash) } it "should not delete the trashed elements" do news_page.destroy Element.trashed.should_not be_empty end end end describe '#element_definitions' do let(:page) { FactoryGirl.build_stubbed(:page) } subject { page.element_definitions } before { Element.should_receive(:definitions).and_return([{'name' => 'article'}, {'name' => 'header'}]) } it "returns all element definitions that could be placed on current page" do should include({'name' => 'article'}) should include({'name' => 'header'}) end end describe '#element_definitions_by_name' do let(:page) { FactoryGirl.build_stubbed(:public_page) } context "with no name given" do it "returns empty array" do page.element_definitions_by_name(nil).should == [] end end context "with 'all' passed as name" do it "returns all element definitions" do Element.should_receive(:definitions) page.element_definitions_by_name('all') end end context "with :all passed as name" do it "returns all element definitions" do Element.should_receive(:definitions) page.element_definitions_by_name(:all) end end end describe '#element_definition_names' do let(:page) { FactoryGirl.build_stubbed(:public_page) } it "returns all element names defined in page layout" do page.element_definition_names.should == %w(article header) end it "returns always an array" do page.stub(:definition).and_return({}) page.element_definition_names.should be_an(Array) end end describe '#elements_grouped_by_cells' do let(:page) { FactoryGirl.create(:public_page, :do_not_autogenerate => false) } before do PageLayout.stub(:get).and_return({ 'name' => 'standard', 'cells' => ['header'], 'elements' => ['header', 'text'], 'autogenerate' => ['header', 'text'] }) Cell.stub!(:definitions).and_return([{ 'name' => "header", 'elements' => ["header"] }]) end it "should return elements grouped by cell" do elements = page.elements_grouped_by_cells elements.keys.first.should be_instance_of(Cell) elements.values.first.first.should be_instance_of(Element) end it "should only include elements beeing in a cell " do page.elements_grouped_by_cells.keys.should_not include(nil) end end describe '#feed_elements' do it "should return all rss feed elements" do news_page.feed_elements.should_not be_empty news_page.feed_elements.should == Element.find_all_by_name('news') end end describe '#find_elements' do before do FactoryGirl.create(:element, :public => false, :page => public_page) FactoryGirl.create(:element, :public => false, :page => public_page) end context "with show_non_public argument TRUE" do it "should return all elements from empty options" do public_page.find_elements({}, true).all.should == public_page.elements.all end it "should only return the elements passed as options[:only]" do public_page.find_elements({:only => ['article']}, true).all.should == public_page.elements.named('article').all end it "should not return the elements passed as options[:except]" do public_page.find_elements({:except => ['article']}, true).all.should == public_page.elements - public_page.elements.named('article').all end it "should return elements offsetted" do public_page.find_elements({:offset => 2}, true).all.should == public_page.elements.offset(2) end it "should return elements limitted in count" do public_page.find_elements({:count => 1}, true).all.should == public_page.elements.limit(1) end end context "with options[:from_cell]" do let(:element) { FactoryGirl.build_stubbed(:element) } context "given as String" do context '' do before { public_page.cells.stub_chain(:find_by_name, :elements, :offset, :limit, :published).and_return([element]) } it "returns only the elements from given cell" do public_page.find_elements(from_cell: 'A Cell').to_a.should == [element] end end context "that can not be found" do let(:elements) {[]} before { elements.stub_chain(:offset, :limit, :published).and_return([]) } it "returns empty set" do public_page.elements.should_receive(:where).with('1 = 0').and_return(elements) public_page.find_elements(from_cell: 'Lolo').to_a.should == [] end it "loggs a warning" do Rails.logger.should_receive(:debug) public_page.find_elements(from_cell: 'Lolo') end end end context "given as cell object" do let(:cell) { FactoryGirl.build_stubbed(:cell, page: public_page) } it "returns only the elements from given cell" do cell.stub_chain(:elements, :offset, :limit, :published).and_return([element]) public_page.find_elements(from_cell: cell).to_a.should == [element] end end end context "with show_non_public argument FALSE" do it "should return all elements from empty arguments" do public_page.find_elements().all.should == public_page.elements.published.all end it "should only return the public elements passed as options[:only]" do public_page.find_elements(:only => ['article']).all.should == public_page.elements.published.named('article').all end it "should return all public elements except the ones passed as options[:except]" do public_page.find_elements(:except => ['article']).all.should == public_page.elements.published.all - public_page.elements.published.named('article').all end it "should return elements offsetted" do public_page.find_elements({:offset => 2}).all.should == public_page.elements.published.offset(2) end it "should return elements limitted in count" do public_page.find_elements({:count => 1}).all.should == public_page.elements.published.limit(1) end end end describe '#first_public_child' do before do FactoryGirl.create(:page, :name => "First child", :language => language, :public => false, :parent_id => language_root.id) end it "should return first_public_child" do first_public_child = FactoryGirl.create(:public_page, :name => "First public child", :language => language, :parent_id => language_root.id) language_root.first_public_child.should == first_public_child end it "should return nil if no public child exists" do language_root.first_public_child.should == nil end end context 'folding' do let(:user) { mock_model('User') } describe '#fold!' do context "with folded status set to true" do it "should create a folded page for user" do public_page.fold!(user.id, true) expect(public_page.folded_pages.first.user_id).to eq(user.id) end end end describe '#folded?' do let(:page) { Page.new } context 'if page is folded' do before do page.stub_chain(:folded_pages, :find_by_user_id).and_return(mock_model('FoldedPage', folded: true)) end it "should return true" do expect(page.folded?(user.id)).to eq(true) end end context 'if page is not folded' do it "should return false" do expect(page.folded?(101093)).to eq(false) end end end end describe '#lock!' do let(:page) { FactoryGirl.create(:page) } let(:user) { mock_model('User') } it "should set locked to true" do page.lock!(user) page.reload page.locked.should == true end it "should not update the timestamps " do expect { page.lock!(user) }.to_not change(page, :updated_at) end it "should set locked_by to the users id" do page.lock!(user) page.reload page.locked_by.should == user.id end end describe '#paste_from_clipboard' do let(:source) { FactoryGirl.build_stubbed(:page) } let(:new_parent) { FactoryGirl.build_stubbed(:page) } let(:page_name) { "Pagename (pasted)" } let(:copied_page) { mock_model('Page') } subject { Page.paste_from_clipboard(source, new_parent, page_name) } it "should copy the source page with the given name to the new parent" do Page.should_receive(:copy).with(source, { parent_id: new_parent.id, language: new_parent.language, name: page_name, title: page_name }) subject end it "should return the copied page" do Page.stub!(:copy).and_return(copied_page) expect(subject).to be_a(copied_page.class) end context "if source page has children" do it "should also copy and paste the children" do Page.stub!(:copy).and_return(copied_page) source.stub!(:children).and_return([mock_model('Page')]) source.should_receive(:copy_children_to).with(copied_page) subject end end end context 'previous and next.' do let(:center_page) { FactoryGirl.create(:public_page, name: 'Center Page') } let(:next_page) { FactoryGirl.create(:public_page, name: 'Next Page') } let(:non_public_page) { FactoryGirl.create(:page, name: 'Not public Page') } let(:restricted_page) { FactoryGirl.create(:restricted_page, public: true) } before do public_page restricted_page non_public_page center_page next_page end describe '#previous' do it "should return the previous page on the same level" do center_page.previous.should == public_page end context "no previous page on same level present" do it "should return nil" do public_page.previous.should be_nil end end context "with options restricted" do context "set to true" do it "returns previous restricted page" do center_page.previous(restricted: true).should == restricted_page end end context "set to false" do it "skips restricted page" do center_page.previous(restricted: false).should == public_page end end end context "with options public" do context "set to true" do it "returns previous public page" do center_page.previous(public: true).should == public_page end end context "set to false" do it "skips public page" do center_page.previous(public: false).should == non_public_page end end end end describe '#next' do it "should return the next page on the same level" do center_page.next.should == next_page end context "no next page on same level present" do it "should return nil" do next_page.next.should be_nil end end end end describe '#publish!' do let(:page) { FactoryGirl.build_stubbed(:page, public: false) } before do page.stub!(:save).and_return(true) page.publish! end it "sets public attribute to true" do page.public.should == true end end describe '#set_language_from_parent_or_default_language' do let(:default_language) { mock_model('Language', code: 'es') } let(:page) { Page.new } before { page.stub!(:parent).and_return(parent) } subject { page } context "parent has a language" do let(:parent) { mock_model('Page', language: default_language, language_id: default_language.id, language_code: default_language.code) } before do page.set_language_from_parent_or_default_language end its(:language_id) { should eq(parent.language_id) } its(:language_code) { should eq(parent.language_code) } end context "parent has no language" do let(:parent) { mock_model('Page', language: nil, language_id: nil, language_code: nil) } before do Language.stub!(:get_default).and_return(default_language) page.set_language_from_parent_or_default_language end its(:language_id) { should eq(default_language.id) } its(:language_code) { should eq(default_language.code) } end end describe '#taggable?' do context "definition has 'taggable' key with true value" do it "should return true" do page = FactoryGirl.build(:page) page.stub(:definition).and_return({'name' => 'standard', 'taggable' => true}) page.taggable?.should be_true end end context "definition has 'taggable' key with foo value" do it "should return false" do page = FactoryGirl.build(:page) page.stub(:definition).and_return({'name' => 'standard', 'taggable' => 'foo'}) page.taggable?.should be_false end end context "definition has no 'taggable' key" do it "should return false" do page = FactoryGirl.build(:page) page.stub(:definition).and_return({'name' => 'standard'}) page.taggable?.should be_false end end end describe '#unlock!' do let(:page) { FactoryGirl.create(:page, locked: true, locked_by: 1) } its "should set the locked status to false" do page.unlock! page.reload page.locked.should == false end it "should not update the timestamps " do expect { page.unlock! }.to_not change(page, :updated_at) end it "should set locked_by to nil" do page.unlock! page.reload page.locked_by.should == nil end end context 'urlname updating' do let(:parentparent) { FactoryGirl.create(:page, name: 'parentparent', visible: true) } let(:parent) { FactoryGirl.create(:page, parent_id: parentparent.id, name: 'parent', visible: true) } let(:page) { FactoryGirl.create(:page, parent_id: parent.id, name: 'page', visible: true) } let(:invisible) { FactoryGirl.create(:page, parent_id: page.id, name: 'invisible', visible: false) } let(:contact) { FactoryGirl.create(:page, parent_id: invisible.id, name: 'contact', visible: true) } context "with activated url_nesting" do before { Config.stub!(:get).and_return(true) } it "should store all parents urlnames delimited by slash" do page.urlname.should == 'parentparent/parent/page' end it "should not include the root page" do page.urlname.should_not =~ /root/ end it "should not include the language root page" do page.urlname.should_not =~ /startseite/ end it "should not include invisible pages" do contact.urlname.should_not =~ /invisible/ end context "after changing my urlname" do it "should update urlnames of descendants" do page parentparent.urlname = 'new-urlname' parentparent.save! page.reload page.urlname.should == 'new-urlname/parent/page' end it "should create a legacy url" do page.stub!(:slug).and_return('foo') page.update_urlname! page.legacy_urls.should_not be_empty page.legacy_urls.collect(&:urlname).should include('parentparent/parent/page') end end context "after updating my visibility" do it "should update urlnames of descendants" do page parentparent.visible = false parentparent.save! page.reload page.urlname.should == 'parent/page' end end end context "with disabled url_nesting" do before { Config.stub!(:get).and_return(false) } it "should only store my urlname" do page.urlname.should == 'page' end end end describe '#slug' do context "with parents path saved in urlname" do let(:page) { FactoryGirl.build(:page, urlname: 'root/parent/my-name')} it "should return the last part of the urlname" do page.slug.should == 'my-name' end end context "with single urlname" do let(:page) { FactoryGirl.build(:page, urlname: 'my-name')} it "should return the last part of the urlname" do page.slug.should == 'my-name' end end context "with nil as urlname" do let(:page) { FactoryGirl.build(:page, urlname: nil)} it "should return nil" do page.slug.should be_nil end end end context 'page status methods' do let(:page) { FactoryGirl.build(:page, public: true, visible: true, restricted: false, locked: false)} describe '#status' do it "returns a combined status hash" do page.status.should == {public: true, visible: true, restricted: false, locked: false} end end describe '#status_title' do it "returns a translated status string for public status" do page.status_title(:public).should == 'Page is published.' end it "returns a translated status string for visible status" do page.status_title(:visible).should == 'Page is visible in navigation.' end it "returns a translated status string for locked status" do page.status_title(:locked).should == '' end it "returns a translated status string for restricted status" do page.status_title(:restricted).should == 'Page is not restricted.' end end end context 'indicate page editors' do let(:page) { Page.new } let(:user) { User.new(firstname: 'Paul', lastname: 'Page') } describe '#creator_name' do before { page.stub(:creator).and_return(user) } it "should return the name of the creator" do expect(page.creator_name).to eq('Paul Page') end end describe '#updater_name' do before { page.stub(:updater).and_return(user) } it "should return the name of the updater" do expect(page.updater_name).to eq('Paul Page') end end describe '#locker_name' do before { page.stub(:locker).and_return(user) } it "should return the name of the current page editor" do expect(page.locker_name).to eq('Paul Page') end end end describe '#controller_and_action' do let(:page) { Page.new } context 'if the page has a custom controller defined in its description' do before do page.stub!(:has_controller?).and_return(true) page.stub!(:layout_description).and_return({'controller' => 'comments', 'action' => 'index'}) end it "should return a Hash with controller and action key-value pairs" do expect(page.controller_and_action).to eq({controller: '/comments', action: 'index'}) end end end end end