require File.expand_path('spec_helper', File.dirname(__FILE__)) require File.expand_path('model_factory', File.dirname(__FILE__)) module ModelMatchers class HavePage def initialize(path) @category = path end def matches?(article) @article = article article.categories.map { |c| c.path }.include?(@category) end def failure_message "expected '#{@article.path}' to be assigned to #{@category}" end def negative_failure_message "'#{@article.path}' should not be assigned to #{@category}" end end def be_in_category(path) HavePage.new(path) end end describe "Page", :shared => true do include ModelFactory include ModelMatchers def create_page(options) super(options.merge(:ext => @extension)) end before(:each) do stub_configuration end after(:each) do remove_temp_directory Nesta::FileModel.purge_cache end it "should be findable" do create_page(:heading => 'Apple', :path => 'the-apple') Nesta::Page.find_all.should have(1).item end it "should find by path" do create_page(:heading => 'Banana', :path => 'banana') Nesta::Page.find_by_path('banana').heading.should == 'Banana' end it "should find index page by path" do create_page(:heading => 'Banana', :path => 'banana/index') Nesta::Page.find_by_path('banana').heading.should == 'Banana' end describe "for home page" do it "should set title to heading and site title" do create_page(:heading => 'Home', :path => 'index') Nesta::Page.find_by_path('/').title.should == 'Home - My blog' end it "should respect title metadata" do create_page(:path => 'index', :metadata => { 'title' => 'Specific title' }) Nesta::Page.find_by_path('/').title.should == 'Specific title' end it "should set title to site title by default" do create_page(:path => 'index') Nesta::Page.find_by_path('/').title.should == 'My blog' end it "should set permalink to empty string" do create_page(:path => 'index') Nesta::Page.find_by_path('/').permalink.should == '' end it "should set abspath to /" do create_page(:path => 'index') Nesta::Page.find_by_path('/').abspath.should == '/' end end it "should not find nonexistent page" do Nesta::Page.find_by_path("no-such-page").should be_nil end it "should ensure file exists on instantiation" do lambda { Nesta::Page.new("no-such-file") }.should raise_error(Sinatra::NotFound) end it "should reload cached files when modified" do create_page(:path => "a-page", :heading => "Version 1") File.stub!(:mtime).and_return(Time.new - 1) Nesta::Page.find_by_path("a-page") create_page(:path => "a-page", :heading => "Version 2") File.stub!(:mtime).and_return(Time.new) Nesta::Page.find_by_path("a-page").heading.should == "Version 2" end it "should have default priority of 0 in category" do page = create_page(:metadata => { 'categories' => 'some-page' }) page.priority('some-page').should == 0 page.priority('another-page').should be_nil end it "should read priority from category metadata" do page = create_page(:metadata => { 'categories' => ' some-page:1, another-page , and-another :-1 ' }) page.priority('some-page').should == 1 page.priority('another-page').should == 0 page.priority('and-another').should == -1 end describe "with assigned pages" do before(:each) do @category = create_category create_article(:heading => 'Article 1', :path => 'article-1') create_article( :heading => 'Article 2', :path => 'article-2', :metadata => { 'date' => '30 December 2008', 'categories' => @category.path } ) @article = create_article( :heading => 'Article 3', :path => 'article-3', :metadata => { 'date' => '31 December 2008', 'categories' => @category.path } ) @category1 = create_category( :path => 'category-1', :heading => 'Category 1', :metadata => { 'categories' => @category.path } ) @category2 = create_category( :path => 'category-2', :heading => 'Category 2', :metadata => { 'categories' => @category.path } ) @category3 = create_category( :path => 'category-3', :heading => 'Category 3', :metadata => { 'categories' => "#{@category.path}:1" } ) end it "should find articles" do @category.articles.should have(2).items end it "should order articles by reverse chronological order" do @category.articles.first.path.should == @article.path end it "should find pages" do @category.pages.should have(3).items end it "should sort pages by priority" do @category.pages.index(@category3).should == 0 end it "should order pages by heading if priority not set" do pages = @category.pages pages.index(@category1).should < pages.index(@category2) end it "should not find pages scheduled in the future" do future_date = (Time.now + 86400).strftime("%d %B %Y") article = create_article(:heading => "Article 4", :path => "foo/article-4", :metadata => { "date" => future_date }) Nesta::Page.find_articles.detect{|a| a == article}.should be_nil end end describe "with pages in draft" do before(:each) do @category = create_category @draft = create_page(:heading => 'Forthcoming content', :path => 'foo/in-draft', :metadata => { 'categories' => @category.path, 'flags' => 'draft' }) Nesta::App.stub!(:production?).and_return(true) end it "should not find assigned drafts" do @category.pages.should_not include(@draft) end it "should not find drafts by path" do Nesta::Page.find_by_path('foo/in-draft').should be_nil end end describe "when finding articles" do before(:each) do create_article(:heading => "Article 1", :path => "article-1") create_article(:heading => "Article 2", :path => "article-2", :metadata => { "date" => "31 December 2008" }) create_article(:heading => "Article 3", :path => "foo/article-3", :metadata => { "date" => "30 December 2008" }) end it "should only find pages with dates" do articles = Nesta::Page.find_articles articles.size.should > 0 Nesta::Page.find_articles.each { |page| page.date.should_not be_nil } end it "should return articles in reverse chronological order" do article1, article2 = Nesta::Page.find_articles[0..1] article1.date.should > article2.date end end it "should be able to find parent page" do category = create_category(:path => 'parent') article = create_article(:path => 'parent/child') article.parent.should == category end describe "(with deep index page)" do it "should be able to find index parent" do home = create_category(:path => 'index', :heading => 'Home') category = create_category(:path => 'parent') category.parent.should == home home.parent.should be_nil end it "should be able to find parent of index" do category = create_category(:path => "parent") index = create_category(:path => "parent/child/index") index.parent.should == category end it "should be able to find permalink of index" do index = create_category(:path => "parent/child/index") index.permalink.should == 'child' end end describe "(with missing nested page)" do it "should consider grandparent to be parent" do grandparent = create_category(:path => 'grandparent') child = create_category(:path => 'grandparent/parent/child') child.parent.should == grandparent end it "should consider grandparent home page to be parent" do home = create_category(:path => 'index') child = create_category(:path => 'parent/child') child.parent.should == home end end describe "when assigned to categories" do before(:each) do create_category(:heading => "Apple", :path => "the-apple") create_category(:heading => "Banana", :path => "banana") @article = create_article( :metadata => { "categories" => "banana, the-apple" }) end it "should be possible to list the categories" do @article.categories.should have(2).items @article.should be_in_category("the-apple") @article.should be_in_category("banana") end it "should sort categories by heading" do @article.categories.first.heading.should == "Apple" end it "should not be assigned to non-existant category" do delete_page(:category, "banana", @extension) @article.should_not be_in_category("banana") end end it "should set parent to nil when at root" do create_category(:path => "top-level").parent.should be_nil end describe "when not assigned to category" do it "should have empty category list" do article = create_article Nesta::Page.find_by_path(article.path).categories.should be_empty end end describe "with no content" do it "should produce no HTML output" do create_article do |path| file = File.open(path, 'w') file.close end Nesta::Page.find_all.first.to_html.should match(/^\s*$/) end end describe "without metadata" do before(:each) do create_article @article = Nesta::Page.find_all.first end it "should use default layout" do @article.layout.should == :layout end it "should use default template" do @article.template.should == :page end it "should parse heading correctly" do @article.to_html.should have_tag("h1", "My article") end it "should have default read more link text" do @article.read_more.should == "Continue reading" end it "should not have description" do @article.description.should be_nil end it "should not have keywords" do @article.keywords.should be_nil end end describe "with metadata" do before(:each) do @layout = 'my_layout' @template = 'my_template' @date = '07 September 2009' @keywords = 'things, stuff' @description = 'Page about stuff' @summary = 'Multiline\n\nsummary' @read_more = 'Continue at your leisure' @skillz = 'ruby, guitar, bowstaff' @article = create_article(:metadata => { 'date' => @date.gsub('September', 'Sep'), 'description' => @description, 'flags' => 'draft, orange', 'keywords' => @keywords, 'layout' => @layout, 'read more' => @read_more, 'skillz' => @skillz, 'summary' => @summary, 'template' => @template }) end it "should override default layout" do @article.layout.should == @layout.to_sym end it "should override default template" do @article.template.should == @template.to_sym end it "should set permalink to basename of filename" do @article.permalink.should == 'my-article' end it "should set path from filename" do @article.path.should == 'article-prefix/my-article' end it "should retrieve heading" do @article.heading.should == 'My article' end it "should be possible to convert an article to HTML" do @article.to_html.should have_tag("h1", "My article") end it "should not include metadata in the HTML" do @article.to_html.should_not have_tag("p", /^Date/) end it "should not include heading in body" do @article.body.should_not have_tag("h1", "My article") end it "should retrieve description from metadata" do @article.description.should == @description end it "should retrieve keywords from metadata" do @article.keywords.should == @keywords end it "should retrieve date published from metadata" do @article.date.strftime("%d %B %Y").should == @date end it "should retrieve read more link from metadata" do @article.read_more.should == @read_more end it "should retrieve summary text from metadata" do @article.summary.should match(/#{@summary.split('\n\n').first}/) end it "should treat double newline chars as paragraph break in summary" do @article.summary.should match(/#{@summary.split('\n\n').last}/) end it "should allow access to metadata" do @article.metadata('skillz').should == @skillz end it "should allow access to flags" do @article.should be_flagged_as('draft') @article.should be_flagged_as('orange') end it "should know whether or not it's a draft" do @article.should be_draft end end describe "when checking last modification time" do before(:each) do create_article @article = Nesta::Page.find_all.first end it "should check filesystem" do mock_file_stat(:should_receive, @article.filename, "3 January 2009") @article.last_modified.should == Time.parse("3 January 2009") end end end describe "All types of page" do include ModelFactory before(:each) do stub_configuration end after(:each) do remove_temp_directory Nesta::FileModel.purge_cache end it "should still return top level menu items" do # Page.menu_items is deprecated; we're keeping it for the moment so # that we don't break themes or code in a local app.rb (just yet). page1 = create_category(:path => "page-1") page2 = create_category(:path => "page-2") create_menu([page1.path, page2.path].join("\n")) Nesta::Page.menu_items.should == [page1, page2] end end describe "Markdown page" do before(:each) do @extension = :mdown end it_should_behave_like "Page" it "should set heading from first h1 tag" do page = create_page( :heading => "First heading", :content => "# Second heading") page.heading.should == "First heading" end it "should ignore trailing # characters in headings" do article = create_article(:heading => 'With trailing #') article.heading.should == 'With trailing' end end describe "Haml page" do before(:each) do @extension = :haml end it_should_behave_like "Page" it "should set heading from first h1 tag" do create_page( :path => "a-page", :heading => "First heading", :content => "%h1 Second heading" ) Nesta::Page.find_by_path("a-page").heading.should == "First heading" end end describe "Textile page" do before(:each) do @extension = :textile end it_should_behave_like "Page" it "should set heading from first h1 tag" do create_page( :path => "a-page", :heading => "First heading", :content => "h1. Second heading" ) Nesta::Page.find_by_path("a-page").heading.should == "First heading" end end describe "Menu" do include ModelFactory before(:each) do stub_configuration @page = create_page(:path => "page-1") end after(:each) do remove_temp_directory Nesta::FileModel.purge_cache end it "should find top level menu items" do text = [@page.path, "no-such-page"].join("\n") create_menu(text) Nesta::Menu.top_level.should == [@page] end it "should find all items in the menu" do create_menu(@page.path) Nesta::Menu.full_menu.should == [@page] Nesta::Menu.for_path('/').should == [@page] end describe "with nested sub menus" do before(:each) do (2..6).each do |i| instance_variable_set("@page#{i}", create_page(:path => "page-#{i}")) end text = <<-EOF #{@page.path} #{@page2.path} #{@page3.path} #{@page4.path} #{@page5.path} #{@page6.path} EOF create_menu(text) end it "should return top level menu items" do Nesta::Menu.top_level.should == [@page, @page5] end it "should return full tree of menu items" do Nesta::Menu.full_menu.should == [@page, [@page2, [@page3, @page4]], @page5, [@page6]] end it "should return part of the tree of menu items" do Nesta::Menu.for_path(@page2.path).should == [@page2, [@page3, @page4]] end it "should deem menu for path that isn't in menu to be nil" do Nesta::Menu.for_path('wibble').should be_nil end end end