require File.expand_path("#{File.dirname(__FILE__)}/../spec_helper") module WidgetSpec describe Erector::Widget do describe ".all_tags" do it "returns set of full and empty tags" do Erector::Widget.all_tags.class.should == Array Erector::Widget.all_tags.should == Erector::Widget.full_tags + Erector::Widget.empty_tags end end describe "#to_s" do class << self define_method("invokes #render and returns the string representation of the rendered widget") do it "invokes #render and returns the string representation of the rendered widget" do widget = Erector::Widget.new do div "Hello" end mock.proxy(widget).render widget.to_s.should == "
Hello
" end end end context "when passed no arguments" do send "invokes #render and returns the string representation of the rendered widget" end context "when passed an argument that is #render" do send "invokes #render and returns the string representation of the rendered widget" end context "when passed an argument that is not #render" do attr_reader :widget before do @widget = Erector::Widget.new def widget.alternate_render div "Hello from Alternate Render" end mock.proxy(widget).alternate_render end it "invokes the passed in method name and returns the string representation of the rendered widget" do widget.to_s(:alternate_render).should == "
Hello from Alternate Render
" end it "does not invoke #render" do dont_allow(widget).render widget.to_s(:alternate_render) end end end describe "#instruct" do it "when passed no arguments; returns an XML declaration with version 1 and utf-8" do html = Erector::Widget.new do instruct # version must precede encoding, per XML 1.0 4th edition (section 2.8) end.to_s.should == "" end end describe "#widget" do context "when nested" do it "renders the tag around the rest of the block" do parent_widget = Class.new(Erector::Widget) do def render div :id => "parent_widget" do super end end end child_widget = Class.new(Erector::Widget) do def render div :id => "child_widget" do super end end end widget = Class.new(Erector::Widget) do def render widget(parent_widget) do widget(child_widget) do super end end end end widget.new(nil, :parent_widget => parent_widget, :child_widget => child_widget) do div :id => "widget" end.to_s.should == '
' end end end describe "#element" do context "when receiving one argument" do it "returns an empty element" do Erector::Widget.new do element('div') end.to_s.should == "
" end end context "with a attribute hash" do it "returns an empty element with the attributes" do html = Erector::Widget.new do element( 'div', :class => "foo bar", :style => "display: none; color: white; float: left;", :nil_attribute => nil ) end.to_s doc = Hpricot(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::Widget.new do element('div', :class => [:foo, :bar]) end.to_s.should == "
"; end end context "with an array of CSS classes as strings" do it "returns a tag with the classes separated" do Erector::Widget.new do element('div', :class => ['foo', 'bar']) end.to_s.should == "
"; end end context "with a CSS class which is a string" do it "just use that as the attribute value" do Erector::Widget.new do element('div', :class => "foo bar") end.to_s.should == "
"; end end context "with many attributes" do it "alphabetize them" do Erector::Widget.new do empty_element('foo', :alpha => "", :betty => "5", :aardvark => "tough", :carol => "", :demon => "", :erector => "", :pi => "3.14", :omicron => "", :zebra => "", :brain => "") end.to_s.should == ""; end end context "with inner tags" do it "returns nested tags" do widget = Erector::Widget.new do element 'div' do element 'div' end end widget.to_s.should == '
' end end context "with text" do it "returns element with inner text" do Erector::Widget.new do element 'div', 'test text' end.to_s.should == "
test text
" end end context "with object other than hash" do it "returns element with inner text == object.to_s" do object = ['a', 'b'] Erector::Widget.new do element 'div', object end.to_s.should == "
#{object.to_s}
" end end context "with parameters and block" do it "returns element with inner html and attributes" do Erector::Widget.new do element 'div', 'class' => "foobar" do element 'span', 'style' => 'display: none;' end end.to_s.should == '
' end end context "with content and parameters" do it "returns element with content as inner html and attributes" do Erector::Widget.new do element 'div', 'test text', :style => "display: none;" end.to_s.should == '
test text
' end end context "with more than three arguments" do it "raises ArgumentError" do proc do Erector::Widget.new do element 'div', 'foobar', {}, 'fourth' end.to_s end.should raise_error(ArgumentError) end end it "renders the proper full tags" do Erector::Widget.full_tags.each do |tag_name| expected = "<#{tag_name}>" actual = Erector::Widget.new do send(tag_name) end.to_s 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::Widget.new do element 'div', 'test &<>text' end.to_s.should == "
test &<>text
" end end context "when outputting text via text" do it "quotes it" do Erector::Widget.new do element 'div' do text "test &<>text" end end.to_s.should == "
test &<>text
" end end context "when outputting attribute value" do it "quotes it" do Erector::Widget.new do element 'a', :href => "foo.cgi?a&b" end.to_s.should == "" end end context "with raw text" do it "does not quote it" do Erector::Widget.new do element 'div' do text raw("bold") end end.to_s.should == "
bold
" end end context "with raw text and no block" do it "does not quote it" do Erector::Widget.new do element 'div', raw("bold") end.to_s.should == "
bold
" end end context "with raw attribute" do it "does not quote it" do Erector::Widget.new do element 'a', :href => raw("foo?x= ") end.to_s.should == "" end end context "with quote in attribute" do it "quotes it" do Erector::Widget.new do element 'a', :onload => "alert(\"foo\")" end.to_s.should == "" end end end context "with a non-string, non-raw" do it "calls to_s and quotes" do Erector::Widget.new do element 'a' do text [7, "foo&bar"] end end.to_s.should == "7foo&bar" end end end describe "#empty_element" do context "when receiving attributes" do it "renders an empty element with the attributes" do Erector::Widget.new do empty_element 'input', :name => 'foo[bar]' end.to_s.should == '' end end context "when not receiving attributes" do it "renders an empty element without attributes" do Erector::Widget.new do empty_element 'br' end.to_s.should == '
' end end it "renders the proper empty-element tags" do ['area', 'base', 'br', 'hr', 'img', 'input', 'link', 'meta'].each do |tag_name| expected = "<#{tag_name} />" actual = Erector::Widget.new do send(tag_name) end.to_s 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 "#nbsp" do it "turns consecutive spaces into consecutive non-breaking spaces" do Erector::Widget.new do text nbsp("a b") end.to_s.should == "a  b" end it "works in text context" do Erector::Widget.new do element 'a' do text nbsp("&<> foo") end end.to_s.should == "&<> foo" end it "works in attribute value context" do Erector::Widget.new do element 'a', :href => nbsp("&<> foo") end.to_s.should == "" end it "defaults to a single non-breaking space if given no argument" do Erector::Widget.new do text nbsp end.to_s.should == " " end end describe "#character" do it "renders a character given the codepoint number" do Erector::Widget.new do text character(160) end.to_s.should == " " end it "renders a character given the unicode name" do Erector::Widget.new do text character(:right_arrow) end.to_s.should == "→" end it "renders a character above 0xffff" do Erector::Widget.new do text character(:old_persian_sign_ka) end.to_s.should == "𐎣" end it "throws an exception if a name is not recognized" do lambda { Erector::Widget.new do text character(:no_such_character_name) end.to_s }.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::Widget.new do text character([]) end.to_s }.should raise_error("Unrecognized argument to character: ") 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 "#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::Widget.new do javascript do rawtext 'if (x < y && x > z) alert("don\'t stop");' end end.to_s.should == expected end end it "renders the raw content inside script tags when given text" do expected = <<-EXPECTED EXPECTED expected.gsub!(/^ /, '') Erector::Widget.new do javascript('alert("&<>\'hello");') end.to_s.should == expected end context "when receiving a params hash" do it "renders a source file" do html = Erector::Widget.new do javascript(:src => "/my/js/file.js") end.to_s doc = Hpricot(html) doc.at('/')[: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::Widget.new do javascript('alert("&<>\'hello");', :src => "/my/js/file.js") end.to_s doc = Hpricot(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::Widget.new do javascript 'foobar', {}, 'fourth' end.to_s end.should raise_error(ArgumentError) end end end describe "#css" do it "makes a link when passed a string" do Erector::Widget.new do css "erector.css" end.to_s.should == "" end end describe "#url" do it "renders an anchor tag with the same href and text" do Erector::Widget.new do url "http://example.com" end.to_s.should == "http://example.com" end end describe '#capture' do it "should return content rather than write it to the buffer" do widget = Erector::Widget.new do captured = capture do p 'Captured Content' end div do text captured end end widget.to_s.should == '

Captured Content

' end it "works with nested captures" do widget = Erector::Widget.new do captured = capture do captured = capture do p 'Nested Capture' end p 'Captured Content' text captured end div do text captured end end widget.to_s.should == '

Captured Content

Nested Capture

' end end describe 'nested' do it "can insert another widget without raw" do inner = Erector::Widget.new do p "foo" end outer = Erector::Widget.new do div inner end.to_s.should == '

foo

' end end describe '#widget' do before do class Parent < Erector::Widget def render text 1 widget Child do text 2 third end end def third text 3 end end class Child < Erector::Widget def render super end end end it "renders nested widgets in the correct order" do Parent.new.to_s.should == '123' end end describe '#render_to' do class A < Erector::Widget def render p "A" end end it "renders to a doc" do class B < Erector::Widget def render text "B" A.new.render_to(@doc) text "B" end end b = B.new b.to_s.should == "B

A

B" b.doc.size.should == 10 # B,

, A,

, B end it "renders to a widget's doc" do class B < Erector::Widget def render text "B" A.new.render_to(self) text "B" end end b = B.new b.to_s.should == "B

A

B" b.doc.size.should == 10 # B,

, A,

, B end it "passing a widget to text method renders it" do Erector::Widget.new() do text "B" text A.new() text "B" end.to_s.should == "B

A

B" end end end end