$:.unshift __dir__
require 'test_helper'
class EngineTest < Haml::TestCase
# A map of erroneous Haml documents to the error messages they should produce.
# The error messages may be arrays;
# if so, the second element should be the line number that should be reported for the error.
# If this isn't provided, the tests will assume the line number should be the last line of the document.
EXCEPTION_MAP = {
"!!!\n a" => error(:illegal_nesting_header),
"a\n b" => error(:illegal_nesting_plain),
"/ a\n b" => error(:illegal_nesting_content),
"% a" => error(:invalid_tag, '% a'),
"%p a\n b" => error(:illegal_nesting_line, 'p'),
"%p=" => error(:no_ruby_code, '='),
"%p~" => error(:no_ruby_code, '~'),
"~" => error(:no_ruby_code, '~'),
"=" => error(:no_ruby_code, '='),
"%p/\n a" => error(:illegal_nesting_self_closing),
#":a\n b" => [error(:filter_not_defined, 'a'), 1],
":a= b" => error(:invalid_filter_name, 'a= b'),
"." => error(:illegal_element),
".#" => error(:illegal_element),
".{} a" => error(:illegal_element),
".() a" => error(:illegal_element),
".= a" => error(:illegal_element),
"%p..a" => error(:illegal_element),
"%a/ b" => error(:self_closing_content),
" %p foo" => error(:indenting_at_start),
" %p foo" => error(:indenting_at_start),
"- end" => error(:no_end),
"%p{:a => 'b',\n:c => 'd'}/ e" => [error(:self_closing_content), 2],
"%p{:a => 'b',\n:c => 'd'}=" => [error(:no_ruby_code, '='), 2],
"%p.{:a => 'b',\n:c => 'd'} e" => [error(:illegal_element), 1],
"%p{:a => 'b',\n:c => 'd',\n:e => 'f'}\n%p/ a" => [error(:self_closing_content), 4],
"%p{:a => 'b',\n:c => 'd',\n:e => 'f'}\n- raise 'foo'" => ["foo", 4],
"%p{:a => 'b',\n:c => raise('foo'),\n:e => 'f'}" => ["foo", 2],
"%p{:a => 'b',\n:c => 'd',\n:e => raise('foo')}" => ["foo", 3],
" \n\t\n %p foo" => [error(:indenting_at_start), 3],
"\n\n %p foo" => [error(:indenting_at_start), 3],
"%p\n foo\n foo" => [error(:inconsistent_indentation, "1 space", "2 spaces"), 3],
"%p\n foo\n%p\n foo" => [error(:inconsistent_indentation, "1 space", "2 spaces"), 4],
"%p\n\t\tfoo\n\tfoo" => [error(:inconsistent_indentation, "1 tab", "2 tabs"), 3],
"%p\n foo\n foo" => [error(:inconsistent_indentation, "3 spaces", "2 spaces"), 3],
"%p\n foo\n %p\n bar" => [error(:inconsistent_indentation, "3 spaces", "2 spaces"), 4],
"%p\n :plain\n bar\n \t baz" => [error(:inconsistent_indentation, '" \t "', "2 spaces"), 4],
"%p\n foo\n%p\n bar" => [error(:deeper_indenting, 2), 4],
"%p\n foo\n %p\n bar" => [error(:deeper_indenting, 3), 4],
"%p\n \tfoo" => [error(:cant_use_tabs_and_spaces), 2],
"%p(" => error(:invalid_attribute_list, '"("'),
"%p(foo=)" => error(:invalid_attribute_list, '"(foo=)"'),
"%p(foo 'bar')" => error(:invalid_attribute_list, '"(foo \'bar\')"'),
"%p(foo=\nbar)" => [error(:invalid_attribute_list, '"(foo="'), 1],
"%p(foo 'bar'\nbaz='bang')" => [error(:invalid_attribute_list, '"(foo \'bar\'"'), 1],
"%p(foo='bar'\nbaz 'bang'\nbip='bop')" => [error(:invalid_attribute_list, '"(foo=\'bar\' baz \'bang\'"'), 2],
"%p{'foo' => 'bar' 'bar' => 'baz'}" => :compile,
"%p{:foo => }" => :compile,
"%p{=> 'bar'}" => :compile,
"%p{'foo => 'bar'}" => :compile,
"%p{:foo => 'bar}" => :compile,
"%p{:foo => 'bar\"}" => :compile,
# Regression tests
"foo\n\n\n bar" => [error(:illegal_nesting_plain), 4],
"%p/\n\n bar" => [error(:illegal_nesting_self_closing), 3],
"%p foo\n\n bar" => [error(:illegal_nesting_line, 'p'), 3],
"/ foo\n\n bar" => [error(:illegal_nesting_content), 3],
"!!!\n\n bar" => [error(:illegal_nesting_header), 3],
"- raise 'foo'\n\n\n\nbar" => ["foo", 1],
"= 'foo'\n-raise 'foo'" => ["foo", 2],
"\n\n\n- raise 'foo'" => ["foo", 4],
"%p foo |\n bar |\n baz |\nbop\n- raise 'foo'" => ["foo", 5],
#"foo\n:ruby\n 1\n 2\n 3\n- raise 'foo'" => ["foo", 6],
#"foo\n:erb\n 1\n 2\n 3\n- raise 'foo'" => ["foo", 6],
"foo\n:plain\n 1\n 2\n 3\n- raise 'foo'" => ["foo", 6],
"foo\n:plain\n 1\n 2\n 3\n4\n- raise 'foo'" => ["foo", 7],
"foo\n:plain\n 1\n 2\n 3\#{''}\n- raise 'foo'" => ["foo", 6],
"foo\n:plain\n 1\n 2\n 3\#{''}\n4\n- raise 'foo'" => ["foo", 7],
"foo\n:plain\n 1\n 2\n \#{raise 'foo'}" => ["foo", 5],
"= raise 'foo'\nfoo\nbar\nbaz\nbang" => ["foo", 1],
"- case 1\n\n- when 1\n - raise 'foo'" => ["foo", 4],
}
User = Struct.new('User', :id)
class CustomHamlClass < Struct.new(:id)
def haml_object_ref
"my_thing"
end
end
CpkRecord = Struct.new('CpkRecord', :id) do
def to_key
[*self.id] unless id.nil?
end
end
def use_test_tracing(options)
unless options[:filename]
# use caller method name as fake filename. useful for debugging
i = -1
caller[i+=1] =~ /`(.+?)'/ until $1 and $1.index('test_') == 0
options[:filename] = "(#{$1})"
end
options
end
def render(text, options = {}, &block)
options = use_test_tracing(options)
super
end
def engine(text, options = {})
options = use_test_tracing(options)
Hamlit::HamlEngine.new(text, options)
end
def setup
@old_default_internal = Encoding.default_internal
silence_warnings{Encoding.default_internal = nil}
end
def teardown
silence_warnings{Encoding.default_internal = @old_default_internal}
end
def test_empty_render
assert_equal "", render("")
end
def test_flexible_tabulation; skip # filter
assert_equal("
\n foo\n
\n\n bar\n \n baz\n \n
\n",
render("%p\n foo\n%q\n bar\n %a\n baz"))
assert_equal("\n foo\n
\n\n bar\n \n baz\n \n
\n",
render("%p\n\tfoo\n%q\n\tbar\n\t%a\n\t\tbaz"))
assert_equal("\n \t \t bar\n baz\n
\n",
render("%p\n :plain\n \t \t bar\n baz"))
end
def test_empty_render_should_remain_empty
assert_equal('', render(''))
end
def test_attributes_should_render_correctly
assert_equal("", render(".atlantis{:style => 'ugly'}").chomp)
end
def test_css_id_as_attribute_should_be_appended_with_underscore
assert_equal("", render("#my_id{:id => '1'}").chomp)
assert_equal("", render("#my_id{:id => 1}").chomp)
end
def test_ruby_code_should_work_inside_attributes
assert_equal("foo
", render("%p{:class => 1+2} foo").chomp)
end
def test_class_attr_with_array; skip # array attribute
assert_equal("foo
\n", render("%p{:class => %w[a b]} foo")) # basic
assert_equal("foo
\n", render("%p.css{:class => %w[a b]} foo")) # merge with css
assert_equal("foo
\n", render("%p.css{:class => %w[css b]} foo")) # merge uniquely
assert_equal("foo
\n", render("%p{:class => [%w[a b], %w[c d]]} foo")) # flatten
assert_equal("foo
\n", render("%p{:class => [:a, :b] } foo")) # stringify
assert_equal("foo
\n", render("%p{:class => [nil, false] } foo")) # strip falsey
assert_equal("foo
\n", render("%p{:class => :a} foo")) # single stringify
assert_equal("foo
\n", render("%p{:class => false} foo")) # single falsey
assert_equal("foo
\n", render("%p(class='html'){:class => %w[a b]} foo")) # html attrs
end
def test_id_attr_with_array; skip # array attribute
assert_equal("foo
\n", render("%p{:id => %w[a b]} foo")) # basic
assert_equal("foo
\n", render("%p#css{:id => %w[a b]} foo")) # merge with css
assert_equal("foo
\n", render("%p{:id => [%w[a b], %w[c d]]} foo")) # flatten
assert_equal("foo
\n", render("%p{:id => [:a, :b] } foo")) # stringify
assert_equal("foo
\n", render("%p{:id => [nil, false] } foo")) # strip falsey
assert_equal("foo
\n", render("%p{:id => :a} foo")) # single stringify
assert_equal("foo
\n", render("%p{:id => false} foo")) # single falsey
assert_equal("foo
\n", render("%p(id='html'){:id => %w[a b]} foo")) # html attrs
end
def test_colon_in_class_attr
assert_equal("\n", render("%p.foo:bar/"))
end
def test_colon_in_id_attr
assert_equal("
\n", render("%p#foo:bar/"))
end
def test_dynamic_attributes_with_no_content
assert_equal(<
HTML
%p
%a{:href => "http://" + "haml.info"}
HAML
end
def test_attributes_with_to_s
assert_equal(<
HTML
%p#foo{:id => 1+1}
%p.foo{:class => 1+1}
%p{:blaz => 1+1}
%p{(1+1) => 1+1}
HAML
end
def test_nil_should_render_empty_tag
assert_equal("",
render(".no_attributes{:nil => nil}").chomp)
end
def test_strings_should_get_stripped_inside_tags
assert_equal("This should have no spaces in front of it
",
render(".stripped This should have no spaces in front of it").chomp)
end
def test_one_liner_should_be_one_line
assert_equal("Hello
", render('%p Hello').chomp)
end
def test_one_liner_with_newline_shouldnt_be_one_line; skip # dynamic indentation # script bug
assert_equal("\n foo\n bar\n
", render('%p= "foo\nbar"').chomp)
end
def test_multi_render
engine = engine("%strong Hi there!")
assert_equal("Hi there!\n", engine.to_html)
assert_equal("Hi there!\n", engine.to_html)
assert_equal("Hi there!\n", engine.to_html)
end
def test_interpolation
assert_equal("Hello World
\n", render('%p Hello #{who}', locals: {who: 'World'}, escape_html: false))
assert_equal("\n Hello World\n
\n", render("%p\n Hello \#{who}", locals: {who: 'World'}, escape_html: false))
assert_equal("Hello World
\n", render('%p Hello #{who}', locals: {who: 'World'}, escape_html: true))
assert_equal("\n Hello World\n
\n", render("%p\n Hello \#{who}", locals: {who: 'World'}, escape_html: true))
end
def test_interpolation_with_instance_var; skip # special interpolation
scope = Object.new
scope.instance_variable_set(:@who, 'World')
assert_equal("Hello World
\n", render('%p Hello #@who', scope: scope, escape_html: false))
assert_equal("\n Hello World\n
\n", render("%p\n Hello \#@who", scope: scope, escape_html: false))
assert_equal("Hello World
\n", render('%p Hello #@who', scope: scope, escape_html: true))
assert_equal("\n Hello World\n
\n", render("%p\n Hello \#@who", scope: scope, escape_html: true))
end
def test_interpolation_with_global; skip # special interpolation
$global_var_for_testing = 'World'
assert_equal("Hello World
\n", render('%p Hello #$global_var_for_testing', escape_html: false))
assert_equal("\n Hello World\n
\n", render("%p\n Hello \#$global_var_for_testing", escape_html: false))
assert_equal("Hello World
\n", render('%p Hello #$global_var_for_testing', escape_html: true))
assert_equal("\n Hello World\n
\n", render("%p\n Hello \#$global_var_for_testing", escape_html: true))
ensure
$global_var_for_testing = nil
end
def test_interpolation_in_the_middle_of_a_string
assert_equal("\"title 'Title'. \"\n",
render("\"title '\#{\"Title\"}'. \""))
end
def test_interpolation_with_instance_var_in_the_middle_of_a_string; skip # special interpolation
scope = Object.new
scope.instance_variable_set(:@title, 'Title')
assert_equal("\"title 'Title'. \"\n",
render("\"title '\#@title'. \"", :scope => scope))
end
def test_interpolation_with_global_in_the_middle_of_a_string; skip # special interpolation
$global_var_for_testing = 'Title'
assert_equal("\"title 'Title'. \"\n",
render("\"title '\#$global_var_for_testing'. \""))
ensure
$global_var_for_testing = nil
end
def test_interpolation_at_the_beginning_of_a_line
assert_equal("2
\n", render('%p #{1 + 1}'))
assert_equal("\n 2\n
\n", render("%p\n \#{1 + 1}"))
end
def test_interpolation_with_instance_var_at_the_beginning_of_a_line; skip # special interpolation
scope = Object.new
scope.instance_variable_set(:@foo, 2)
assert_equal("2
\n", render('%p #@foo', :scope => scope))
assert_equal("\n 2\n
\n", render("%p\n \#@foo", :scope => scope))
end
def test_interpolation_with_global_at_the_beginning_of_a_line; skip # special interpolation
$global_var_for_testing = 2
assert_equal("2
\n", render('%p #$global_var_for_testing'))
assert_equal("\n 2\n
\n", render("%p\n \#$global_var_for_testing"))
ensure
$global_var_for_testing = nil
end
def test_escaped_interpolation
assert_equal("Foo & Bar & Baz
\n", render('%p& Foo #{"&"} Bar & Baz'))
end
def test_nil_tag_value_should_render_as_empty
assert_equal("\n", render("%p= nil"))
end
def test_tag_with_failed_if_should_render_as_empty
assert_equal("\n", render("%p= 'Hello' if false"))
end
def test_static_attributes_with_empty_attr
assert_equal("\n", render("%img{:src => '/foo.png', :alt => ''}"))
end
def test_dynamic_attributes_with_empty_attr
assert_equal("\n", render("%img{:width => nil, :src => '/foo.png', :alt => String.new}"))
end
def test_attribute_hash_with_newlines
assert_equal("foop
\n", render("%p{:a => 'b',\n :c => 'd'} foop"))
assert_equal("\n foop\n
\n", render("%p{:a => 'b',\n :c => 'd'}\n foop"))
assert_equal("\n", render("%p{:a => 'b',\n :c => 'd'}/"))
assert_equal("
\n", render("%p{:a => 'b',\n :c => 'd',\n :e => 'f'}"))
end
def test_attr_hashes_not_modified
hash = {:color => 'red'}
assert_equal(< {:hash => hash}))
HTML
%div{hash}
.special{hash}
%div{hash}
HAML
assert_equal(hash, {:color => 'red'})
end
def test_ugly_semi_prerendered_tags
assert_equal(< true))
foo
foo
foo
bar
foo
bar
foo
HTML
%p{:a => 1 + 1}
%p{:a => 1 + 1} foo
%p{:a => 1 + 1}/
%p{:a => 1 + 1}= "foo"
%p{:a => 1 + 1}= "foo\\nbar"
%p{:a => 1 + 1}~ "foo\\nbar"
%p{:a => 1 + 1}
foo
HAML
end
def test_end_of_file_multiline
assert_equal("0
\n1
\n2
\n", render("- for i in (0...3)\n %p= |\n i |"))
end
def test_cr_newline
assert_equal("foo
\nbar
\nbaz
\nboom
\n", render("%p foo\r%p bar\r\n%p baz\n\r%p boom"))
end
def test_textareas; skip # script bug
assert_equal("\n",
render('%textarea= "Foo\n bar\n baz"'))
assert_equal("Foo
bar
baz
\n",
render('%pre= "Foo\n bar\n baz"'))
assert_equal("\n",
render("%textarea #{'a' * 100}"))
assert_equal("\n \n
\n", render(<