# coding: utf-8 require 'test_helper' require 'tilt' require 'tilt/template' require 'tempfile' class TiltTemplateTest < Minitest::Test class MockTemplate < Tilt::Template def prepare end end test "needs a file or block" do assert_raises(ArgumentError) { Tilt::Template.new } end test "initializing with a file" do inst = MockTemplate.new('foo.erb') {} assert_equal 'foo.erb', inst.file end test "initializing with a file and line" do inst = MockTemplate.new('foo.erb', 55) {} assert_equal 'foo.erb', inst.file assert_equal 55, inst.line end test "initializing with a tempfile" do tempfile = Tempfile.new('tilt_template_test') inst = MockTemplate.new(tempfile) assert_equal File.basename(tempfile.path), inst.basename end test "initializing with a pathname" do tempfile = Tempfile.new('tilt_template_test') pathname = Pathname.new(tempfile.path) inst = MockTemplate.new(pathname) assert_equal File.basename(tempfile.path), inst.basename end class SillyHash < Hash def path(arg) end end test "initialize with hash that implements #path" do options = SillyHash[:key => :value] inst = MockTemplate.new(options) {} assert_equal :value, inst.options[:key] end test "uses correct eval_file" do inst = MockTemplate.new('foo.erb', 55) {} assert_equal 'foo.erb', inst.eval_file end test "uses a default filename for #eval_file when no file provided" do inst = MockTemplate.new { 'Hi' } refute_nil inst.eval_file assert !inst.eval_file.include?("\n") end test "calculating template's #basename" do inst = MockTemplate.new('/tmp/templates/foo.html.erb') {} assert_equal 'foo.html.erb', inst.basename end test "calculating the template's #name" do inst = MockTemplate.new('/tmp/templates/foo.html.erb') {} assert_equal 'foo', inst.name end test "initializing with a data loading block" do MockTemplate.new { |template| "Hello World!" } end class PreparingMockTemplate < Tilt::Template def prepare raise "data must be set" if data.nil? @prepared = true end def prepared? ; @prepared ; end end test "raises NotImplementedError when #prepare not defined" do assert_raises(NotImplementedError) { Tilt::Template.new { |template| "Hello World!" } } end test "raises NotImplementedError when #evaluate or #template_source not defined" do inst = PreparingMockTemplate.new { |t| "Hello World!" } assert_raises(NotImplementedError) { inst.render } assert inst.prepared? end class SimpleMockTemplate < PreparingMockTemplate def evaluate(scope, locals, &block) raise "should be prepared" unless prepared? raise "scope should be present" if scope.nil? raise "locals should be present" if locals.nil? "#{@data}" end end test "prepares and evaluates the template on #render" do inst = SimpleMockTemplate.new { |t| "Hello World!" } assert_equal "Hello World!", inst.render assert inst.prepared? end test 'prepares and evaluates the template on #render with nil arg' do inst = SimpleMockTemplate.new { |t| "Hello World!" } assert_equal 'Hello World!', inst.render(nil) assert inst.prepared? end class SourceGeneratingMockTemplate < PreparingMockTemplate def precompiled_template(locals) "foo = [] ; foo << %Q{#{data}} ; foo.join" end end test "template_source with locals" do inst = SourceGeneratingMockTemplate.new { |t| 'Hey #{name}!' } assert_equal "Hey Joe!", inst.render(Object.new, :name => 'Joe') assert inst.prepared? end test "template_source with locals of strings" do inst = SourceGeneratingMockTemplate.new { |t| 'Hey #{name}!' } assert_equal "Hey Joe!", inst.render(Object.new, 'name' => 'Joe') assert inst.prepared? end test "template_source with locals of strings" do inst = SourceGeneratingMockTemplate.new { |t| 'Hey #{name}!' } assert_equal "Hey Joe!", inst.render(Object.new, 'name' => 'Joe', :name=>'Joe') assert inst.prepared? end test "template_source with locals having non-variable keys raises error" do inst = SourceGeneratingMockTemplate.new { |t| '1 + 2 = #{_answer}' } err = assert_raises(RuntimeError) { inst.render(Object.new, 'ANSWER' => 3) } assert_equal "invalid locals key: \"ANSWER\" (keys must be variable names)", err.message assert_equal "1 + 2 = 3", inst.render(Object.new, '_answer' => 3) end test "template_source with nil locals" do inst = SourceGeneratingMockTemplate.new { |t| 'Hey' } assert_equal 'Hey', inst.render(Object.new, nil) assert inst.prepared? end class CustomGeneratingMockTemplate < PreparingMockTemplate def precompiled_template(locals) data end def precompiled_preamble(locals) options.fetch(:preamble) end def precompiled_postamble(locals) options.fetch(:postamble) end end test "supports pre/postamble" do inst = CustomGeneratingMockTemplate.new( :preamble => 'buf = []', :postamble => 'buf.join' ) { 'buf << 1' } assert_equal "1", inst.render end class Person CONSTANT = "Bob" attr_accessor :name def initialize(name) @name = name end end test "template_source with an object scope" do inst = SourceGeneratingMockTemplate.new { |t| 'Hey #{@name}!' } scope = Person.new('Joe') assert_equal "Hey Joe!", inst.render(scope) end test "template_source with a block for yield" do inst = SourceGeneratingMockTemplate.new { |t| 'Hey #{yield}!' } assert_equal "Hey Joe!", inst.render(Object.new){ 'Joe' } end test "template which accesses a constant" do inst = SourceGeneratingMockTemplate.new { |t| 'Hey #{CONSTANT}!' } assert_equal "Hey Bob!", inst.render(Person.new("Joe")) end test "populates Tilt.current_template during rendering" do inst = SourceGeneratingMockTemplate.new { '#{$inst = Tilt.current_template}' } inst.render assert_equal inst, $inst assert_nil Tilt.current_template end test "populates Tilt.current_template in nested rendering" do inst1 = SourceGeneratingMockTemplate.new { '#{$inst.render; $inst1 = Tilt.current_template}' } inst2 = SourceGeneratingMockTemplate.new { '#{$inst2 = Tilt.current_template}' } $inst = inst2 inst1.render assert_equal inst1, $inst1 assert_equal inst2, $inst2 assert_nil Tilt.current_template end ## # Encodings class DynamicMockTemplate < MockTemplate def precompiled_template(locals) options[:code] end end class UTF8Template < MockTemplate def default_encoding Encoding::UTF_8 end end if ''.respond_to?(:encoding) original_encoding = Encoding.default_external setup do @file = Tempfile.open('template') @file.puts "stuff" @file.close @template = @file.path end teardown do Encoding.default_external = original_encoding Encoding.default_internal = nil @file.delete end test "reading from file assumes default external encoding" do Encoding.default_external = 'Big5' inst = MockTemplate.new(@template) assert_equal 'Big5', inst.data.encoding.to_s end test "reading from file with a :default_encoding overrides default external" do Encoding.default_external = 'Big5' inst = MockTemplate.new(@template, :default_encoding => 'GBK') assert_equal 'GBK', inst.data.encoding.to_s end test "reading from file with default_internal set does no transcoding" do Encoding.default_internal = 'utf-8' Encoding.default_external = 'Big5' inst = MockTemplate.new(@template) assert_equal 'Big5', inst.data.encoding.to_s end test "using provided template data verbatim when given as string" do Encoding.default_internal = 'Big5' inst = MockTemplate.new(@template) { "blah".force_encoding('GBK') } assert_equal 'GBK', inst.data.encoding.to_s end test "uses the template from the generated source code" do tmpl = "ふが" code = tmpl.inspect.encode('Shift_JIS') inst = DynamicMockTemplate.new(:code => code) { '' } res = inst.render assert_equal 'Shift_JIS', res.encoding.to_s assert_equal tmpl, res.encode(tmpl.encoding) end test "uses the magic comment from the generated source code" do tmpl = "ふが" code = ("# coding: Shift_JIS\n" + tmpl.inspect).encode('Shift_JIS') # Set it to an incorrect encoding code.force_encoding('UTF-8') inst = DynamicMockTemplate.new(:code => code) { '' } res = inst.render assert_equal 'Shift_JIS', res.encoding.to_s assert_equal tmpl, res.encode(tmpl.encoding) end test "uses #default_encoding instead of default_external" do Encoding.default_external = 'Big5' inst = UTF8Template.new(@template) assert_equal 'UTF-8', inst.data.encoding.to_s end test "uses #default_encoding instead of current encoding" do tmpl = "".force_encoding('Big5') inst = UTF8Template.new(@template) { tmpl } assert_equal 'UTF-8', inst.data.encoding.to_s end test "raises error if the encoding is not valid" do assert_raises(Encoding::InvalidByteSequenceError) do UTF8Template.new(@template) { "\xe4" } end end end end