require File.dirname(__FILE__) + '/../../spec_helper' require File.dirname(__FILE__) + "/shared_signature_examples" require 'ostruct' describe YARD::Templates::Helpers::HtmlHelper do include YARD::Templates::Helpers::BaseHelper include YARD::Templates::Helpers::HtmlHelper include YARD::Templates::Helpers::MethodHelper def options Templates::TemplateOptions.new.tap do |o| o.reset_defaults o.default_return = nil end end describe '#h' do it "should use #h to escape HTML" do h('Usage: foo "bar" ').should == "Usage: foo "bar" <baz>" end end describe '#charset' do it "should return foo if LANG=foo" do ENV.should_receive(:[]).with('LANG').and_return('shift_jis') if YARD.ruby18? Encoding.default_external.should_receive(:name).and_return('shift_jis') if defined?(Encoding) charset.should == 'shift_jis' end ['US-ASCII', 'ASCII-7BIT', 'ASCII-8BIT'].each do |type| it "should convert #{type} to iso-8859-1" do ENV.should_receive(:[]).with('LANG').and_return(type) if YARD.ruby18? Encoding.default_external.should_receive(:name).and_return(type) if defined?(Encoding) charset.should == 'iso-8859-1' end end it "should support utf8 as an encoding value for utf-8" do type = 'utf8' ENV.should_receive(:[]).with('LANG').and_return(type) if YARD.ruby18? Encoding.default_external.should_receive(:name).and_return(type) if defined?(Encoding) charset.should == 'utf-8' end it "should take file encoding if there is a file" do @file = OpenStruct.new(:contents => 'foo'.force_encoding('sjis')) # not the correct charset name, but good enough ['Shift_JIS', 'Windows-31J'].should include(charset) end if YARD.ruby19? it "should take file encoding if there is a file" do ENV.stub!(:[]).with('LANG').and_return('utf-8') if YARD.ruby18? @file = OpenStruct.new(:contents => 'foo') charset.should == 'utf-8' end if YARD.ruby18? if YARD.ruby18? it "should return utf-8 if no LANG env is set" do ENV.should_receive(:[]).with('LANG').and_return(nil) charset.should == 'utf-8' end it "should only return charset part of lang" do ENV.should_receive(:[]).with('LANG').and_return('en_US.UTF-8') charset.should == 'utf-8' end end end describe '#format_types' do it "should include brackets by default" do text = ["String"] should_receive(:linkify).at_least(1).times.with("String", "String").and_return("String") format_types(text).should == format_types(text, true) format_types(text).should == "(String)" end it "should avoid brackets if brackets=false" do should_receive(:linkify).with("String", "String").and_return("String") should_receive(:linkify).with("Symbol", "Symbol").and_return("Symbol") format_types(["String", "Symbol"], false).should == "String, Symbol" end { "String" => [["String"], "String"], "A::B::C" => [["A::B::C"], "A::B::C"], "Array" => [["Array", "String"], "Array<String>"], "Array" => [["Array", "String", "Symbol"], "Array<String, Symbol>"], "Array<{String => Array}>" => [["Array", "String", "Array", "Symbol"], "Array<{String => " + "Array<Symbol>}>"] }.each do |text, values| it "should link all classes in #{text}" do should_receive(:h).with('<').at_least(text.count('<')).times.and_return("<") should_receive(:h).with('>').at_least(text.count('>')).times.and_return(">") values[0].each {|v| should_receive(:linkify).with(v, v).and_return("#{v}") } format_types([text], false).should == values[1] end end end describe '#htmlify' do it "should not use hard breaks for textile markup (RedCloth specific)" do begin; require 'redcloth'; rescue LoadError; pending 'test requires redcloth gem' end htmlify("A\nB", :textile).should_not include("", :pre).should == "
fo\no\n\nbar<>
" end it "should return regular text with :text markup" do htmlify("fo\no\n\nbar<>", :text).should == "fo
o

bar<>" end it "should return unmodified text with :none markup" do htmlify("fo\no\n\nbar<>", :none).should == "fo\no\n\nbar<>" end it "should highlight ruby if markup is :ruby" do htmlify("class Foo; end", :ruby).should =~ /\A

HI

" end it "should autolink URLs (markdown specific)" do log.enter_level(Logger::FATAL) do unless markup_class(:markdown).to_s == "RedcarpetCompat" pending 'This test depends on a markdown engine that supports autolinking' end end htmlify('http://example.com', :markdown).chomp.gsub('/', '/').should == '

http://example.com

' end it "should not autolink URLs inside of {} (markdown specific)" do log.enter_level(Logger::FATAL) do pending 'This test depends on markdown' unless markup_class(:markdown) end htmlify('{http://example.com Title}', :markdown).chomp.should =~ %r{

Title

} htmlify('{http://example.com}', :markdown).chomp.should =~ %r{

http://example.com

} end end describe "#link_object" do before do stub!(:object).and_return(CodeObjects::NamespaceObject.new(nil, :YARD)) end it "should return the object path if there's no serializer and no title" do stub!(:serializer).and_return nil link_object(CodeObjects::NamespaceObject.new(nil, :YARD)).should == "YARD" end it "should return the title if there's a title but no serializer" do stub!(:serializer).and_return nil link_object(CodeObjects::NamespaceObject.new(nil, :YARD), 'title').should == "title" end it "should link objects from overload tag" do YARD.parse_string <<-'eof' module Foo class Bar; def a; end end class Baz # @overload a def a; end end end eof obj = Registry.at('Foo::Baz#a').tag(:overload) foobar = Registry.at('Foo::Bar') foobaz = Registry.at('Foo::Baz') serializer = Serializers::FileSystemSerializer.new stub!(:serializer).and_return(serializer) stub!(:object).and_return(obj) link_object("Bar#a").should =~ %r{href="Bar.html#a-instance_method"} end it "should use relative path in title" do CodeObjects::ModuleObject.new(:root, :YARD) CodeObjects::ClassObject.new(P('YARD'), :Bar) stub!(:object).and_return(CodeObjects::ModuleObject.new(P('YARD'), :Foo)) serializer = Serializers::FileSystemSerializer.new stub!(:serializer).and_return(serializer) link_object("Bar").should =~ %r{>Bar} end it "should use #title if overridden" do CodeObjects::ModuleObject.new(:root, :YARD) CodeObjects::ClassObject.new(P('YARD'), :Bar) Registry.at('YARD::Bar').stub(:title).and_return('TITLE!') stub!(:object).and_return(Registry.at('YARD::Bar')) serializer = Serializers::FileSystemSerializer.new stub!(:serializer).and_return(serializer) link_object("Bar").should =~ %r{>TITLE!} end it "should use relative path to parent class in title" do root = CodeObjects::ModuleObject.new(:root, :YARD) obj = CodeObjects::ModuleObject.new(root, :SubModule) stub!(:object).and_return(obj) serializer = Serializers::FileSystemSerializer.new stub!(:serializer).and_return(serializer) link_object("YARD").should =~ %r{>YARD} end it "should use Klass.foo when linking to class method in current namespace" do root = CodeObjects::ModuleObject.new(:root, :Klass) obj = CodeObjects::MethodObject.new(root, :foo, :class) stub!(:object).and_return(root) serializer = Serializers::FileSystemSerializer.new stub!(:serializer).and_return(serializer) link_object("foo").should =~ %r{>Klass.foo} end it "should escape method name in title" do YARD.parse_string <<-'eof' class Array def &(other) end end eof obj = Registry.at('Array#&') serializer = Serializers::FileSystemSerializer.new stub!(:serializer).and_return(serializer) stub!(:object).and_return(obj) link_object("Array#&").should =~ %r{title="Array#& \(method\)"} end end describe '#url_for' do before { Registry.clear } it "should return nil if serializer is nil" do stub!(:serializer).and_return nil stub!(:object).and_return Registry.root url_for(P("Mod::Class#meth")).should be_nil end it "should return nil if object is hidden" do yard = CodeObjects::ModuleObject.new(:root, :YARD) stub!(:serializer).and_return Serializers::FileSystemSerializer.new stub!(:object).and_return Registry.root stub!(:options).and_return OpenStruct.new(:verifier => Verifier.new('false')) url_for(yard).should be_nil end it "should return nil if serializer does not implement #serialized_path" do stub!(:serializer).and_return Serializers::Base.new stub!(:object).and_return Registry.root url_for(P("Mod::Class#meth")).should be_nil end it "should link to a path/file for a namespace object" do stub!(:serializer).and_return Serializers::FileSystemSerializer.new stub!(:object).and_return Registry.root yard = CodeObjects::ModuleObject.new(:root, :YARD) url_for(yard).should == 'YARD.html' end it "should link to the object's namespace path/file and use the object as the anchor" do stub!(:serializer).and_return Serializers::FileSystemSerializer.new stub!(:object).and_return Registry.root yard = CodeObjects::ModuleObject.new(:root, :YARD) meth = CodeObjects::MethodObject.new(yard, :meth) url_for(meth).should == 'YARD.html#meth-instance_method' end it "should properly urlencode methods with punctuation in links" do obj = CodeObjects::MethodObject.new(nil, :/) serializer = mock(:serializer) serializer.stub!(:serialized_path).and_return("file.html") stub!(:serializer).and_return(serializer) stub!(:object).and_return(obj) url_for(obj).should == "#%2F-instance_method" end end describe '#anchor_for' do it "should not urlencode data when called directly" do obj = CodeObjects::MethodObject.new(nil, :/) anchor_for(obj).should == "/-instance_method" end end describe '#resolve_links' do def parse_link(link) results = {} link =~ /(.+?)<\/a>/m params, results[:inner_text] = $1, $2 params.scan(/\s*(\S+?)=['"](.+?)['"]\s*/).each do |key, value| results[key.to_sym] = value.gsub(/^["'](.+)["']$/, '\1') end results end it "should escape {} syntax with backslash (\\{foo bar})" do input = '\{foo bar} \{XYZ} \{file:FOO} $\{N-M}' output = '{foo bar} {XYZ} {file:FOO} ${N-M}' resolve_links(input).should == output end it "should escape {} syntax with ! (!{foo bar})" do input = '!{foo bar} !{XYZ} !{file:FOO} $!{N-M}' output = '{foo bar} {XYZ} {file:FOO} ${N-M}' resolve_links(input).should == output end it "should link static files with file: prefix" do stub!(:serializer).and_return Serializers::FileSystemSerializer.new stub!(:object).and_return Registry.root parse_link(resolve_links("{file:TEST.txt#abc}")).should == { :inner_text => "TEST", :title => "TEST", :href => "file.TEST.html#abc" } parse_link(resolve_links("{file:TEST.txt title}")).should == { :inner_text => "title", :title => "title", :href => "file.TEST.html" } end it "should create regular links with http:// or https:// prefixes" do parse_link(resolve_links("{http://example.com}")).should == { :inner_text => "http://example.com", :target => "_parent", :href => "http://example.com", :title => "http://example.com" } parse_link(resolve_links("{http://example.com title}")).should == { :inner_text => "title", :target => "_parent", :href => "http://example.com", :title => "title" } end it "should create mailto links with mailto: prefixes" do parse_link(resolve_links('{mailto:joanna@example.com}')).should == { :inner_text => 'mailto:joanna@example.com', :target => '_parent', :href => 'mailto:joanna@example.com', :title => 'mailto:joanna@example.com' } parse_link(resolve_links('{mailto:steve@example.com Steve}')).should == { :inner_text => 'Steve', :target => '_parent', :href => 'mailto:steve@example.com', :title => 'Steve' } end it "should ignore {links} that begin with |...|" do resolve_links("{|x|x == 1}").should == "{|x|x == 1}" end it "should gracefully ignore {} in links" do should_receive(:linkify).with('Foo', 'Foo').and_return('FOO') resolve_links("{} {} {Foo Foo}").should == '{} {} FOO' end %w(tt code pre).each do |tag| it "should ignore links in <#{tag}>" do text = "<#{tag}>{Foo}" resolve_links(text).should == text end end it "should resolve {Name}" do should_receive(:link_file).with('TEST', nil, nil).and_return('') resolve_links("{file:TEST}") end it "should resolve ({Name})" do should_receive(:link_file).with('TEST', nil, nil).and_return('') resolve_links("({file:TEST})") end it "should resolve link with newline in title-part" do parse_link(resolve_links("{http://example.com foo\nbar}")).should == { :inner_text => "foo bar", :target => "_parent", :href => "http://example.com", :title => "foo bar" } end it "should resolve links to methods whose names have been escaped" do should_receive(:linkify).with('Object#<<', nil).and_return('') resolve_links("{Object#<<}") end it "should warn about missing reference at right file location for object" do YARD.parse_string <<-eof # Comments here # And a reference to {InvalidObject} class MyObject; end eof logger = mock(:log) logger.should_receive(:warn).ordered.with("In file `(stdin)':2: Cannot resolve link to InvalidObject from text:") logger.should_receive(:warn).ordered.with("...{InvalidObject}") stub!(:log).and_return(logger) stub!(:object).and_return(Registry.at('MyObject')) resolve_links(object.docstring) end it "should show ellipsis on either side if there is more on the line in a reference warning" do YARD.parse_string <<-eof # {InvalidObject1} beginning of line # end of line {InvalidObject2} # Middle of {InvalidObject3} line # {InvalidObject4} class MyObject; end eof logger = mock(:log) logger.should_receive(:warn).ordered.with("In file `(stdin)':1: Cannot resolve link to InvalidObject1 from text:") logger.should_receive(:warn).ordered.with("{InvalidObject1}...") logger.should_receive(:warn).ordered.with("In file `(stdin)':2: Cannot resolve link to InvalidObject2 from text:") logger.should_receive(:warn).ordered.with("...{InvalidObject2}") logger.should_receive(:warn).ordered.with("In file `(stdin)':3: Cannot resolve link to InvalidObject3 from text:") logger.should_receive(:warn).ordered.with("...{InvalidObject3}...") logger.should_receive(:warn).ordered.with("In file `(stdin)':4: Cannot resolve link to InvalidObject4 from text:") logger.should_receive(:warn).ordered.with("{InvalidObject4}") stub!(:log).and_return(logger) stub!(:object).and_return(Registry.at('MyObject')) resolve_links(object.docstring) end it "should warn about missing reference for file template (no object)" do @file = CodeObjects::ExtraFileObject.new('myfile.txt', '') logger = mock(:log) logger.should_receive(:warn).ordered.with("In file `myfile.txt':3: Cannot resolve link to InvalidObject from text:") logger.should_receive(:warn).ordered.with("...{InvalidObject Some Title}") stub!(:log).and_return(logger) stub!(:object).and_return(Registry.root) resolve_links(<<-eof) Hello world This is a line And {InvalidObject Some Title} And more. eof end end describe '#signature' do before do @results = { :regular => "- (Object) foo", :default_return => "- (Hello) foo", :no_default_return => "- foo", :private_class => "+ (Object) foo (private)", :single => "- (String) foo", :two_types => "- (String, Symbol) foo", :two_types_multitag => "- (String, Symbol) foo", :type_nil => "- (Type?) foo", :type_array => "- (Type+) foo", :multitype => "- (Type, ...) foo", :void => "- (void) foo", :hide_void => "- foo", :block => "- (Object) foo {|a, b, c| ... }", :empty_overload => '- (String) foobar' } end def format_types(types, brackets = false) types.join(", ") end def signature(obj, link = false) super(obj, link).strip end it_should_behave_like "signature" it "should link to regular method if overload name does not have the same method name" do YARD.parse_string <<-eof class Foo # @overload bar(a, b, c) def foo; end end eof serializer = mock(:serializer) serializer.stub!(:serialized_path).with(Registry.at('Foo')).and_return('') stub!(:serializer).and_return(serializer) stub!(:object).and_return(Registry.at('Foo')) signature(Registry.at('Foo#foo').tag(:overload), true).should == "- bar(a, b, c) " end end describe '#html_syntax_highlight' do subject do obj = OpenStruct.new obj.options = options obj.object = Registry.root obj.extend(Templates::Helpers::HtmlHelper) obj end it "should return empty string on nil input" do subject.html_syntax_highlight(nil).should == '' end it "should call #html_syntax_highlight_ruby by default" do Registry.root.source_type = nil subject.should_receive(:html_syntax_highlight_ruby).with('def x; end') subject.html_syntax_highlight('def x; end') end it "should call #html_syntax_highlight_NAME if there's an object with a #source_type" do subject.object = OpenStruct.new(:source_type => :NAME) subject.should_receive(:respond_to?).with('html_markup_html').and_return(true) subject.should_receive(:respond_to?).with('html_syntax_highlight_NAME').and_return(true) subject.should_receive(:html_syntax_highlight_NAME).and_return("foobar") subject.htmlify('
def x; end
', :html).should == '
foobar
' end it "should add !!!LANG to className in outputted pre tag" do subject.object = OpenStruct.new(:source_type => :LANG) subject.should_receive(:respond_to?).with('html_markup_html').and_return(true) subject.should_receive(:respond_to?).with('html_syntax_highlight_LANG').and_return(true) subject.should_receive(:html_syntax_highlight_LANG).and_return("foobar") subject.htmlify("
!!!LANG\ndef x; end
", :html).should == '
foobar
' end it "should call html_syntax_highlight_NAME if source starts with !!!NAME" do subject.should_receive(:respond_to?).with('html_syntax_highlight_NAME').and_return(true) subject.should_receive(:html_syntax_highlight_NAME).and_return("foobar") subject.html_syntax_highlight(<<-eof !!!NAME def x; end eof ).should == "foobar" end it "should not highlight if highlight option is false" do subject.options.highlight = false subject.should_not_receive(:html_syntax_highlight_ruby) subject.html_syntax_highlight('def x; end').should == 'def x; end' end it "should not highlight if there is no highlight method specified by !!!NAME" do subject.should_receive(:respond_to?).with('html_syntax_highlight_NAME').and_return(false) subject.should_not_receive(:html_syntax_highlight_NAME) subject.html_syntax_highlight("!!!NAME\ndef x; end").should == "def x; end" end it "should highlight as ruby if htmlify(text, :ruby) is called" do subject.should_receive(:html_syntax_highlight_ruby).with('def x; end').and_return('x') subject.htmlify('def x; end', :ruby).should == '
x
' end it "should not prioritize object source type when called directly" do subject.should_receive(:html_syntax_highlight_ruby).with('def x; end').and_return('x') subject.object = OpenStruct.new(:source_type => :c) subject.html_syntax_highlight("def x; end").should == "x" end it "shouldn't escape code snippets twice" do subject.htmlify('
{"foo" => 1}
', :html).should == '
{"foo" => 1}
' end it "should highlight source when matching a pre lang= tag" do subject.htmlify('
x = 1
', :html).should == '
x = 1
' end it "should highlight source when matching a code class= tag" do subject.htmlify('
x = 1
', :html).should == '
x = 1
' end end describe '#link_url' do it "should add target if scheme is provided" do link_url("http://url.com").should include(" target=\"_parent\"") link_url("https://url.com").should include(" target=\"_parent\"") link_url("irc://url.com").should include(" target=\"_parent\"") link_url("../not/scheme").should_not include("target") end end end