# encoding: UTF-8
require File.expand_path('../../test_helper', __FILE__)
require 'sinatra/base'
describe "Render" do
before do
@site = setup_site
Content.delete
end
after do
teardown_site
Spontaneous::Output.cache_templates = false
end
def template_root
@template_root ||= File.expand_path(File.join(File.dirname(__FILE__), "../fixtures/templates"))
end
describe "Publish rendering step" do
before do
@site.paths.add(:templates, template_root)
Page.field :title
Page.box :sections1
Page.box :sections2
class ::TemplateClass < ::Piece
field :title do
def to_epub
to_html
end
end
field :description do
def render(format = :html, locals = {}, parent_context = nil)
case format
when :pdf
to_pdf
else
super
end
end
def to_pdf
"{#{value}}"
end
def to_epub
to_html
end
end
style :this_template
style :another_template
end
@root = ::Page.create(:title => "Home")
@page = ::Page.create(:title => "Page Title")
@content = TemplateClass.new
@content.style.must_equal TemplateClass.default_style
@content.title = "The Title"
@content.description = "The Description"
@page.sections1 << @content
@section1 = ::Page.new(:title => "Section 1", :uid => "section1")
@section2 = ::Page.new(:title => "Section 2")
@section3 = ::Page.new(:title => "Section 3")
@section4 = ::Page.new(:title => "Section 4")
@root.sections1 << @section1
@root.sections1 << @section2
@root.sections2 << @section3
@root.sections2 << @section4
@root.sections2.entries.last.set_position(0)
@root.save.reload
@renderer = Spontaneous::Output::Template::PublishRenderer.new(@site)
end
after do
Object.send(:remove_const, :TemplateClass) rescue nil
Object.send(:remove_const, :Page) rescue nil
end
it "render strings correctly" do
@renderer.render_string('${title} {{ Time.now }}', @page.output(:html), {}).must_equal "Page Title {{ Time.now }}"
end
it "use a cache for the site root" do
a = @renderer.render_string('#{root.object_id} #{root.object_id}', @page.output(:html), {})
a.wont_equal "#{nil.object_id} #{nil.object_id}"
a.split.uniq.length.must_equal 1
end
it "uses a cache for site pages" do
a = @renderer.render_string("${site_page('$section1').object_id}", @page.output(:html), {})
a.wont_equal "#{nil.object_id} #{nil.object_id}"
b = @renderer.render_string("${site_page('$section1').object_id}", @page.output(:html), {})
a.must_equal b
end
it "iterate through the sections" do
template = '%%{ navigation(%s) do |section, active| }${section.title}/${active} %%{ end }'
a = @renderer.render_string(template % "", @section1.output(:html), {})
a.must_equal "Section 1/true Section 2/false Section 4/false Section 3/false "
a = @renderer.render_string(template % "depth: 1", @section2.output(:html), {})
a.must_equal "Section 1/false Section 2/true Section 4/false Section 3/false "
a = @renderer.render_string(template % "depth: :section", @section1.output(:html), {})
a.must_equal "Section 1/true Section 2/false Section 4/false Section 3/false "
end
it "use a cache for navigation pages" do
a = b = c = nil
template = '%{ navigation do |section, active| }${section.object_id} %{ end }'
renderer = Spontaneous::Output::Template::PreviewRenderer.new(@site)
a = renderer.render_string(template, ::Content[@section1.id].output(:html), {}).strip
b = renderer.render_string(template, ::Content[@section1.id].output(:html), {}).strip
a.wont_equal b
renderer = Spontaneous::Output::Template::PublishRenderer.new(@site)
template = '%{ navigation do |section, active| }${section.object_id} %{ end }'
a = renderer.render_string(template, ::Content[@section1.id].output(:html), {}).strip
b = renderer.render_string(template, ::Content[@section1.id].output(:html), {}).strip
a.must_equal b
renderer = Spontaneous::Output::Template::PublishRenderer.new(@site)
template = '%{ navigation do |section, active| }${section.object_id} %{ end }'
c = renderer.render_string(template, ::Content[@section1.id].output(:html), {}).strip
a.wont_equal c
end
it "be able to render themselves to HTML" do
@content.render.must_equal "
The TitleThe Description\n"
end
it "be able to render themselves to PDF" do
Page.add_output :pdf
@content.render(:pdf).must_equal "The Title{The Description}\n"
end
it "be able to render themselves to EPUB" do
Page.add_output :epub
@content.render(:epub).must_equal "The TitleThe Description\n"
end
it "can specify an alternate object as the content source" do
class Page
layout(:html) { "=${title}"}
end
class DivertedPage < Page
layout(:html) { "!${title}"}
renders { sections1.first }
end
parent = DivertedPage.create(title: "parent")
child = Page.create(title: "child")
@root.sections1 << parent
@root.save
parent.sections1 << child
parent.save
child.save
expected = child.render(:html)
parent.render(:html).must_equal expected
end
describe "piece trees" do
before do
@page = ::Page.create
TemplateClass.style :complex_template, :default => true
TemplateClass.box :bits
@content = TemplateClass.new
@page.sections1 << @content
@content.title = "The Title"
@content.description = "The Description"
@child = TemplateClass.new
@child.title = "Child Title"
@child.description = "Child Description"
@content.bits << @child
@content.contents.first.style = TemplateClass.get_style(:this_template)
end
after do
Content.delete
end
it "be accessible through #content method" do
expected = "\nThe Title\nChild TitleChild Description\n\n\n"
@content.render.must_equal expected
end
it "cascade the chosen format to all subsequent #render calls" do
::Page.add_output :pdf
@content.render(:pdf).must_equal "\nThe Title\nChild Title{Child Description}\n\n\n"
end
it "only show visible pieces" do
child = TemplateClass.new
child.title = "Child2 Title"
child.description = "Child2 Description"
@content.bits << child
@content.bits.last.style = TemplateClass.get_style(:this_template)
@content.bits.last.hide!
expected = "\nThe Title\nChild TitleChild Description\n\n\n"
@content.render.must_equal expected
end
end
describe "fields" do
it "render a joined list of field values" do
Page.field :description, :markdown
Page.field :image
::Page.layout do
%(${ fields })
end
@page.title = "Title & Things"
@page.image = "/photo.jpg"
@page.description = "Description & Stuff"
lines = @page.render.split(/\n(?=#{field.render(:html)}<\/div>/
end
end
it "passes arguments onto the render" do
Page.field :image do
size :large do; end
size :small do; end
end
Page.layout do
%{${ image(width: 10, height: 50, alt: "Fish")}}
end
@page.image = "/photo.jpg"
output = @page.render
output.must_match /width=['"]10['"]/
output.must_match /height=['"]50['"]/
output.must_match /alt=['"]Fish['"]/
end
it "passes arguments onto the render for image sizes" do
Page.field :image do
size :large do; end
size :small do; end
end
Page.layout do
%{${ image.large(width: 10, height: 50)}}
end
@page.image = "/photo.jpg"
output = @page.render
output.must_match /width=['"]10["']/
output.must_match /height=["']50['"]/
end
end
describe "boxes" do
before do
TemplateClass.style :slots_template, :default => true
TemplateClass.box :images
@page = ::Page.new
@content = TemplateClass.new
@content.title = "The Title"
@content.description = "The Description"
@page.sections1 << @content
@child = TemplateClass.new
@child.title = "Child Title"
@child.description = "Child Description"
@content.images << @child
@content.images.first.style = TemplateClass.get_style(:this_template)
end
it "render box sets as a joined list of each box's output" do
::Page.layout do
%(${ content })
end
@page.render.must_equal @page.boxes.map(&:render).join("\n")
end
it "render 'boxes' as a joined list of each box's output" do
::Page.layout do
%(${ boxes })
end
@page.render.must_equal @page.boxes.map(&:render).join("\n")
end
it "render boxes" do
@content.render.must_equal "
\n Child TitleChild Description\n\n\n"
end
it "render boxes to alternate formats" do
::Page.add_output :pdf
@content.render(:pdf).must_equal "
\n Child Title{Child Description}\n\n\n"
end
end
describe "anonymous boxes" do
before do
TemplateClass.style :anonymous_style, :default => true
TemplateClass.box :images do
field :introduction
end
class ::AnImage < Content; end
AnImage.field :title
AnImage.template '
#{title}'
@page = ::Page.new
@root = TemplateClass.new
@page.sections1 << @root
@root.images.introduction = "Images below:"
@image1 = AnImage.new
@image1.title = "Image 1"
@image2 = AnImage.new
@image2.title = "Image 2"
@root.images << @image1
@root.images << @image2
end
after do
Object.send(:remove_const, :AnImage) rescue nil
end
it "render using anonymous style" do
@root.render.must_equal "
\nImages below:\nImage 1\nImage 2\n\n"
end
end
describe "default templates" do
before do
TemplateClass.style :default_template_style, :default => true
TemplateClass.box :images_with_template do
field :introduction
end
class ::AnImage < Content; end
AnImage.field :title
AnImage.template '
#{title}'
@page = ::Page.create
@root = TemplateClass.new
@page.sections1 << @root
@root.images_with_template.introduction = "Images below:"
@image1 = AnImage.new
@image1.title = "Image 1"
@image2 = AnImage.new
@image2.title = "Image 2"
@root.images_with_template << @image1
@root.images_with_template << @image2
end
after do
Object.send(:remove_const, :AnImage) rescue nil
end
it "render using default style if present" do
@root.render.must_equal "
\nImages below:\n\n Image 1\n Image 2\n\n\n"
end
end
describe "page styles" do
before do
class ::PageClass < Page
field :title, :string
end
PageClass.layout :subdir_style
PageClass.layout :standard_page
@parent = PageClass.new
@parent.title = "Parent"
end
after do
Object.send(:remove_const, :PageClass) rescue nil
end
it "find page styles at root of templates dir" do
@parent.layout = :standard_page
@parent.render.must_equal "/Parent/\n"
end
it "find page styles in class sub dir" do
@parent.layout = :subdir_style
@parent.render.must_equal "
\n"
end
end
describe "pages as inline content" do
before do
class ::PageClass < Page
field :title, :string
end
PageClass.box :things
PageClass.layout :page_style
PageClass.style :inline_style
@parent = PageClass.new
@parent.title = "Parent"
@page = PageClass.new
@page.title = "Child"
@parent.things << @page
@parent.save
@page.save
end
after do
Object.send(:remove_const, :PageClass) rescue nil
end
it "use style assigned by entry" do
@parent.contents.first.style.must_equal PageClass.default_style
@parent.things.first.style.must_equal PageClass.default_style
end
it "use their default page style when accessed directly" do
@page = PageClass[@page.id]
@page.layout.must_equal PageClass.default_layout
assert_correct_template(@parent, template_root / 'layouts/page_style', @renderer)
@page.render.must_equal "\n"
end
it "persist sub-page style settings" do
@parent = Content[@parent.id]
@parent.contents.first.style.must_equal PageClass.default_style
end
it "render using the inline style" do
assert_correct_template(@parent.contents.first, template_root / 'page_class/inline_style', @renderer)
@parent.contents.first.render.must_equal "Child\n"
@parent.things.render.must_equal "Child\n"
@parent.render.must_equal "Child\n\n"
end
it "renders using the inline style when loaded directly" do
id = @page.id
PageClass.layout(:html) { "=${::PageClass.get(#{id})}" }
@parent.render.must_equal "=Child\n"
end
end
describe "params in templates" do
before do
class ::TemplateParams < Page; end
TemplateParams.field :image, :default => "/images/fromage.jpg"
TemplateParams.layout :template_params
@page = TemplateParams.new
end
after do
Object.send(:remove_const, :TemplateParams) rescue nil
end
it "be passed to the render call" do
@page.image.value.must_equal "/images/fromage.jpg"
@page.image.src.must_equal "/images/fromage.jpg"
@page.render.must_match /alt="Smelly"/
end
end
end
describe "Request rendering" do
before do
@site.paths.add(:templates, template_root)
class ::PreviewRender < Page
field :title, :string
end
class ::Image < Piece
field :src
template :html, "${ src }/ q={{ query }}"
end
PreviewRender.style :inline
PreviewRender.box(:images) { allow :Image }
PreviewRender.field :description, :markdown
@page = PreviewRender.new(:title => "PAGE", :description => "DESCRIPTION")
@page.save
@session = ::Rack::MockSession.new(::Sinatra::Application)
end
after do
Object.send(:remove_const, :PreviewRender)
Object.send(:remove_const, :Image)
end
describe "Preview render" do
before do
@renderer = Spontaneous::Output::Template::PreviewRenderer.new(@site)
PreviewRender.layout :preview_render
end
it "output both publish & request tags" do
@now = Time.now
::Time.stubs(:now).returns(@now)
@renderer.render_string('${title} {{ Time.now }}', @page.output(:html), {}).must_equal "PAGE #{@now.to_s}"
end
it "renders all includes before calling the request render stage" do
PreviewRender.layout do
"q={{ query }} <${ images }>"
end
@page.images << Image.new(src: 'fish.jpg')
result = @page.render_using(@renderer, :html, { query: 'frog'})
result.must_equal "q=frog "
end
# it "render all tags & include preview edit markers" do
# @page.render.must_equal <<-HTML
# PAGE DESCRIPTION
#
#
#
#
# HTML
# end
end
describe "Request rendering" do
before do
@renderer = Spontaneous::Output::Template::PreviewRenderer.new(@site)
PreviewRender.layout :params
end
it "pass on passed params" do
result = @page.render_using(@renderer, :html, {
:welcome => "hello"
})
result.must_equal "PAGE hello\n"
end
end
describe "entry parameters" do
before do
@renderer = Spontaneous::Output::Template::PreviewRenderer.new(@site)
PreviewRender.layout :entries
@first = PreviewRender.new(:title => "first")
@second = PreviewRender.new(:title => "second")
@third = PreviewRender.new(:title => "third")
@page.images << @first
@page.images << @second
@page.images << @third
@page.save
end
it "be available to templates" do
@page.render.must_equal "0>first\n1second\n2 template_mtime, "Compiled file should register as newer"
@first = PreviewRender.new(:title => "first")
@first.save
end
# Disabled pending decision about the best way to optimize templates
# in the case of this example, where we are optimizing the first render
# of a site template (not a rendered page) I'm not sure that it's worth it
# at all...
it "ignore compiled template file if it is older than the template"
# @first.render_using(@renderer).must_equal "compiled"
# File.open(@temp_template_root / "layouts/standard.html.cut", "w") do |t|
# t.write("updated template")
# end
# later = Time.now + 1000
# File.utime(later, later, @template_path)
# template_mtime = File.mtime(@template_path)
# compiled_mtime = File.mtime(@compiled_path)
# assert template_mtime > compiled_mtime, "Template file should register as newer"
# # Need to use a new renderer because the existing one will have cached the compiled template
# @renderer = Spontaneous::Output::Template::PublishRenderer.new(@site)
# @first.render.must_equal "updated template"
# end
end
describe "PublishedRenderer" do
before do
@site.background_mode = :immediate
@site.output_store :Memory
::Spontaneous::State.delete
::Content.delete
::Content.delete_revision(1) rescue nil
@renderer = Spontaneous::Output::Template::PublishedRenderer.new(@site, 1)
Page.box :other
class ::DynamicPage < Page
layout(:html) { "${path}.${ __format }:{{ something }}"}
end
class ::StaticPage < Page
layout(:html) { "${ path }.${ __format }"}
end
@root = Page.create
assert @root.is_root?
@dynamic = DynamicPage.new(slug: "dynamic", uid: "dynamic")
@static = StaticPage.new(slug: "static", uid: "static")
@root.other << @dynamic
@root.other << @static
[@root, @dynamic, @static].each(&:save)
@site.publish do
run :render_revision
run :activate_revision
end
@site.publish_all
end
after do
Object.send :remove_const, :StaticPage
Object.send :remove_const, :DynamicPage
::Content.delete
end
it "should render dynamic pages from the template store xxx" do
result = @renderer.render!(@dynamic.output(:html), { something: "something here" }, nil)
result.must_equal "/dynamic.html:something here"
end
it "should render static pages from the template store xxx" do
result = @renderer.render!(@static.output(:html), { something: "something here" }, nil)
result.read.must_equal "/static.html"
end
end
describe "variables in render command" do
before do
@renderer = Spontaneous::Output::Template::PublishRenderer.new(@site)
PreviewRender.layout :variables
PreviewRender.style :variables
@page.layout = :variables
@first = PreviewRender.new(:title => "first")
@page.images << @first
@page.images.first.style = :variables
end
it "be passed to page content" do
@page.render(:html, :param => "param").must_equal "param\n\n\nlocal\n"
end
end
end
end