require File.expand_path("#{File.dirname(__FILE__)}/../spec_helper")
require 'nokogiri'
describe Erector::HTML do
include Erector::Mixin
describe "#instruct" do
it "when passed no arguments; returns an XML declaration with version 1 and utf-8" do
# version must precede encoding, per XML 1.0 4th edition (section 2.8)
erector { instruct }.should == ""
end
end
describe "#element" do
context "when receiving one argument" do
it "returns an empty element" do
erector { element('div') }.should == "
"
end
end
context "with a attribute hash" do
it "returns an empty element with the attributes" do
html = erector do
element(
'div',
:class => "foo bar",
:style => "display: none; color: white; float: left;",
:nil_attribute => nil
)
end
doc = Nokogiri::HTML(html)
div = doc.at('div')
div[:class].should == "foo bar"
div[:style].should == "display: none; color: white; float: left;"
div[:nil_attribute].should be_nil
end
end
context "with an array of CSS classes" do
it "returns a tag with the classes separated" do
erector do
element('div', :class => [:foo, :bar])
end.should == "";
end
end
context "with an array of CSS classes as strings" do
it "returns a tag with the classes separated" do
erector do
element('div', :class => ['foo', 'bar'])
end.should == "";
end
end
context "with a CSS class which is a string" do
it "just use that as the attribute value" do
erector do
element('div', :class => "foo bar")
end.should == "";
end
end
context "with an empty array of CSS classes" do
it "does not emit a class attribute" do
erector do
element('div', :class => [])
end.should == ""
end
end
context "with many attributes" do
it "should emit them in the order they are given" do
erector do
empty_element('foo', :alpha => "", :betty => "5", :aardvark => "tough")
end.should == ""
end
end
context "with boolean attributes" do
it "should emit HTML5-style true attributes" do
erector do
empty_element('foo', :alpha => true)
end.should == ""
end
it "should emit nothing for false or nil attributes" do
erector do
empty_element('foo', :alpha => false)
end.should == ""
erector do
empty_element('foo', :alpha => nil)
end.should == ""
end
end
context "with underscored attributes" do
context "without hyphenize_underscores" do
it "keeps class names as underscored" do
erector do
empty_element('foo').max_thingie
end.should == ''
end
it "also keeps classes specified as attributes as underscored" do
erector do
empty_element('foo', :class => "max_thingie")
end.should == ''
end
end
context "with hyphenize_underscores" do
before do
Erector::Widget.hyphenize_underscores = true
end
it "turns underscores in class names into hyphens" do
erector do
empty_element('foo').max_thingie
end.should == ''
end
it "lets you pick underscores or hyphens when specifying a class as an attribute" do
erector do
empty_element('foo', :class => "max_thingie")
end.should == ''
end
end
end
context "with inner tags" do
it "returns nested tags" do
erector do
element 'div' do
element 'div'
end
end.should == '
'
end
end
context "with text" do
it "returns element with inner text" do
erector do
element 'div', 'test text'
end.should == "
test text
"
end
end
context "with a widget" do
it "renders the widget inside the element" do
erector do
element 'div', Erector.inline { p "foo" }
end.should == '
foo
'
end
end
context "with object other than hash" do
it "returns element with inner text == object.to_s" do
object = ['a', 'b']
erector do
element 'div', object
end.should == "
#{CGI.escapeHTML object.to_s}
"
end
end
context "with parameters and block" do
it "returns element with inner html and attributes" do
erector do
element 'div', 'class' => "foobar" do
element 'span', 'style' => 'display: none;'
end
end.should == '
'
end
end
context "with content and parameters" do
it "returns element with content as inner html and attributes" do
erector do
element 'div', 'test text', :style => "display: none;"
end.should == '
test text
'
end
end
context "with more than three arguments" do
it "raises ArgumentError" do
proc do
erector do
element 'div', 'foobar', {}, 'fourth'
end
end.should raise_error(ArgumentError)
end
end
it "renders the proper full tags" do
Erector::HTMLWidget.full_tags.each do |tag_name|
expected = "<#{tag_name}>#{tag_name}>"
actual = erector { send(tag_name) }
begin
actual.should == expected
rescue Spec::Expectations::ExpectationNotMetError => e
puts "Expected #{tag_name} to be a full element. Expected #{expected}, got #{actual}"
raise e
end
end
end
describe "quoting" do
context "when outputting text" do
it "quotes it" do
erector do
element 'div', 'test &<>text'
end.should == "
test &<>text
"
end
end
context "when outputting text via text" do
it "quotes it" do
erector do
element 'div' do
text "test &<>text"
end
end.should == "
test &<>text
"
end
end
context "when outputting attribute value" do
it "quotes it" do
erector do
element 'a', :href => "foo.cgi?a&b"
end.should == ""
end
end
context "with raw text" do
it "does not quote it" do
erector do
element 'div' do
text raw("bold")
end
end.should == "
bold
"
end
end
context "with raw text and no block" do
it "does not quote it" do
erector do
element 'div', raw("bold")
end.should == "
bold
"
end
end
context "with raw attribute" do
it "does not quote it" do
erector do
element 'a', :href => raw("foo?x= ")
end.should == ""
end
end
context "with quote in attribute" do
it "quotes it" do
erector do
element 'a', :onload => "alert(\"foo\")"
end.should == ""
end
end
end
context "with a non-string, non-raw" do
it "calls to_s and quotes" do
array = [7, "foo&bar"]
erector do
element 'a' do
text array
end
end.should == "#{CGI.escapeHTML array.to_s}"
end
end
end
describe "#empty_element" do
context "when receiving attributes" do
it "renders an empty element with the attributes" do
erector do
empty_element 'input', :name => 'foo[bar]'
end.should == ''
end
end
context "when not receiving attributes" do
it "renders an empty element without attributes" do
erector do
empty_element 'br'
end.should == ' '
end
end
it "renders the proper empty-element tags" do
Erector::HTMLWidget.self_closing_tags.each do |tag_name|
expected = "<#{tag_name} />"
actual = erector { send(tag_name) }
begin
actual.should == expected
rescue Spec::Expectations::ExpectationNotMetError => e
puts "Expected #{tag_name} to be an empty-element tag. Expected #{expected}, got #{actual}"
raise e
end
end
end
end
describe "#comment" do
it "emits a single line comment when receiving a string" do
erector do
comment "foo"
end.should == "\n"
end
it "emits a multiline comment when receiving a block" do
erector do
comment do
text "Hello"
text " world!"
end
end.should == "\n"
end
it "emits a multiline comment when receiving a string and a block" do
erector do
comment "Hello" do
text " world!"
end
end.should == "\n"
end
# see http://www.w3.org/TR/html4/intro/sgmltut.html#h-3.2.4
it "does not HTML-escape character references" do
erector do
comment " "
end.should == "\n"
end
# see http://www.w3.org/TR/html4/intro/sgmltut.html#h-3.2.4
# "Authors should avoid putting two or more adjacent hyphens inside comments."
it "warns if there's two hyphens in a row" do
capturing_output do
erector do
comment "he was -- awesome!"
end.should == "\n"
end.should == "Warning: Authors should avoid putting two or more adjacent hyphens inside comments.\n"
end
it "renders an IE conditional comment with endif when receiving an if IE" do
erector do
comment "[if IE]" do
text "Hello IE!"
end
end.should == "\n"
end
it "doesn't render an IE conditional comment if there's just some text in brackets" do
erector do
comment "[puppies are cute]"
end.should == "\n"
end
end
describe "#nbsp" do
it "turns consecutive spaces into consecutive non-breaking spaces" do
erector do
text nbsp("a b")
end.should == "a b"
end
it "works in text context" do
erector do
element 'a' do
text nbsp("&<> foo")
end
end.should == "&<> foo"
end
it "works in attribute value context" do
erector do
element 'a', :href => nbsp("&<> foo")
end.should == ""
end
it "defaults to a single non-breaking space if given no argument" do
erector do
text nbsp
end.should == " "
end
end
describe "#character" do
it "renders a character given the codepoint number" do
erector do
text character(160)
end.should == " "
end
it "renders a character given the unicode name" do
erector do
text character(:right_arrow)
end.should == "→"
end
it "renders a character above 0xffff" do
erector do
text character(:old_persian_sign_ka)
end.should == "𐎣"
end
it "throws an exception if a name is not recognized" do
lambda {
erector { text character(:no_such_character_name) }
}.should raise_error("Unrecognized character no_such_character_name")
end
it "throws an exception if passed something besides a symbol or integer" do
# Perhaps calling to_s would be more ruby-esque, but that seems like it might
# be pretty confusing when this method can already take either a name or number
lambda {
erector { text character([]) }
}.should raise_error("Unrecognized argument to character: #{[].to_s}")
end
end
describe '#h' do
before do
@widget = Erector::Widget.new
end
it "escapes regular strings" do
@widget.h("&").should == "&"
end
it "does not escape raw strings" do
@widget.h(@widget.raw("&")).should == "&"
end
end
describe 'escaping' do
plain = 'if (x < y && x > z) alert("don\'t stop");'
escaped = "if (x < y && x > z) alert("don't stop");"
describe "#text" do
it "does HTML escape its param" do
erector { text plain }.should == escaped
end
it "doesn't escape pre-escaped strings" do
erector { text h(plain) }.should == escaped
end
end
describe "#rawtext" do
it "doesn't HTML escape its param" do
erector { rawtext plain }.should == plain
end
end
describe "#text!" do
it "doesn't HTML escape its param" do
erector { text! plain }.should == plain
end
end
describe "#element" do
it "does HTML escape its param" do
erector { element "foo", plain }.should == "#{escaped}"
end
end
end
describe "#javascript" do
context "when receiving a block" do
it "renders the content inside of script text/javascript tags" do
expected = <<-EXPECTED
EXPECTED
expected.gsub!(/^ /, '')
erector do
javascript do
rawtext 'if (x < y && x > z) alert("don\'t stop");'
end
end.should == expected
end
end
it "renders the raw content inside script tags when given text" do
expected = <<-EXPECTED
EXPECTED
expected.gsub!(/^ /, '')
erector do
javascript('alert("&<>\'hello");')
end.should == expected
end
context "when receiving a params hash" do
it "renders a source file" do
html = erector do
javascript(:src => "/my/js/file.js")
end
doc = Nokogiri::HTML(html)
doc.at("script")[:src].should == "/my/js/file.js"
end
end
context "when receiving text and a params hash" do
it "renders a source file" do
html = erector do
javascript('alert("&<>\'hello");', :src => "/my/js/file.js")
end
doc = Nokogiri::HTML(html)
script_tag = doc.at('script')
script_tag[:src].should == "/my/js/file.js"
script_tag.inner_html.should include('alert("&<>\'hello");')
end
end
context "with too many arguments" do
it "raises ArgumentError" do
proc do
erector do
javascript 'foobar', {}, 'fourth'
end
end.should raise_error(ArgumentError)
end
end
end
describe "exception handling" do
class RenderWithReturn < Erector::Widget
def content
h2 do
return "returned_value"
text "don't get here"
end
end
end
it "closes tags when a block returns" do
RenderWithReturn.new.to_html.should == ""
end
it "closes tags when a block throws and the exception is caught" do
erector do
begin
div do
raise "no way"
text "not reached"
end
rescue
end
end.should == ""
end
it "closes tags when throwing block versus text exception" do
erector do
begin
div do
span "a value" do
text "a block"
end
end
rescue ArgumentError => e
e.to_s.should include(
"You can't pass both a block and a value to span")
end
end.should == ""
end
end
end