aboutsummaryrefslogblamecommitdiffstats
path: root/actionview/test/template/digestor_test.rb
blob: dde757b5a20efe23eaae019ad53b40c518538164 (plain) (tree)
1
2
3
4
5
6
7
8
9

                       
                                        

                     
                               
 

                                      
                                                               






                                                                 
 
                                     
                        
                         

                
                  
                                                       
                  
                  





                 


                                                                         
                                                     

                                                                 
     



                           



                                                 




                                                        
     
 
              

                           














                                                





                                                   



























                                                                                   
















                                                        
 




                                                    

                                      
                                                                                       



                             
                                                         
                                                                                         



                             











                                                                                          




                                                
 




                                                                      
                                                 








                                                                      
                                                     








                                                                      












                                                       
 





                                                    













                                                               






















                                                      
 





                                                
                   
                                                                    




                                                  













                                                                                        






                                                                                     
                                            




                                                                       
 





                                                                                     

                                            


                                                  
 

                              
                                          
                        
                                               
 







                                            

       
                                                             
                                                      
                                      


           
                                                                                      

                                      
 

                                           
 



                                                                                         

       







                                                                                           

                                   
       
 



                                                                           


                                     




                                                       
   
require 'abstract_unit'
require 'fileutils'
require 'action_view/dependency_tracker'

class FixtureTemplate
  attr_reader :source, :handler

  def initialize(template_path)
    @source = File.read(template_path)
    @handler = ActionView::Template.handler_for_extension(:erb)
  rescue Errno::ENOENT
    raise ActionView::MissingTemplate.new([], "", [], true, [])
  end
end

class FixtureFinder
  FIXTURES_DIR = "#{File.dirname(__FILE__)}/../fixtures/digestor"

  attr_reader   :details, :view_paths
  attr_accessor :formats
  attr_accessor :variants

  def initialize
    @details  = {}
    @view_paths = ActionView::PathSet.new(['digestor'])
    @formats  = []
    @variants = []
  end

  def details_key
    details.hash
  end

  def find(name, prefixes = [], partial = false, keys = [], options = {})
    partial_name = partial ? name.gsub(%r|/([^/]+)$|, '/_\1') : name
    format = @formats.first.to_s
    format += "+#{@variants.first}" if @variants.any?

    FixtureTemplate.new("digestor/#{partial_name}.#{format}.erb")
  end

  def disable_cache(&block)
    yield
  end
end

class TemplateDigestorTest < ActionView::TestCase
  def setup
    @cwd     = Dir.pwd
    @tmp_dir = Dir.mktmpdir

    FileUtils.cp_r FixtureFinder::FIXTURES_DIR, @tmp_dir
    Dir.chdir @tmp_dir
  end

  def teardown
    Dir.chdir @cwd
    FileUtils.rm_r @tmp_dir
    ActionView::Digestor.cache.clear
  end

  def test_top_level_change_reflected
    assert_digest_difference("messages/show") do
      change_template("messages/show")
    end
  end

  def test_explicit_dependency
    assert_digest_difference("messages/show") do
      change_template("messages/_message")
    end
  end

  def test_explicit_dependency_in_multiline_erb_tag
    assert_digest_difference("messages/show") do
      change_template("messages/_form")
    end
  end

  def test_explicit_dependency_wildcard
    assert_digest_difference("events/index") do
      change_template("events/_completed")
    end
  end

  def test_explicit_dependency_wildcard_picks_up_added_file
    old_caching, ActionView::Resolver.caching = ActionView::Resolver.caching, false

    assert_digest_difference("events/index") do
      add_template("events/_uncompleted")
    end
  ensure
    remove_template("events/_uncompleted")
    ActionView::Resolver.caching = old_caching
  end

  def test_explicit_dependency_wildcard_picks_up_removed_file
    old_caching, ActionView::Resolver.caching = ActionView::Resolver.caching, false
    add_template("events/_subscribers_changed")

    assert_digest_difference("events/index") do
      remove_template("events/_subscribers_changed")
    end
  ensure
    ActionView::Resolver.caching = old_caching
  end

  def test_second_level_dependency
    assert_digest_difference("messages/show") do
      change_template("comments/_comments")
    end
  end

  def test_second_level_dependency_within_same_directory
    assert_digest_difference("messages/show") do
      change_template("messages/_header")
    end
  end

  def test_third_level_dependency
    assert_digest_difference("messages/show") do
      change_template("comments/_comment")
    end
  end

  def test_directory_depth_dependency
    assert_digest_difference("level/below/index") do
      change_template("level/below/_header")
    end
  end

  def test_logging_of_missing_template
    assert_logged "Couldn't find template for digesting: messages/something_missing" do
      digest("messages/show")
    end
  end

  def test_logging_of_missing_template_ending_with_number
    assert_logged "Couldn't find template for digesting: messages/something_missing_1" do
      digest("messages/show")
    end
  end

  def test_logging_of_missing_template_for_dependencies
    assert_logged "'messages/something_missing' file doesn't exist, so no dependencies" do
      dependencies("messages/something_missing")
    end
  end

  def test_logging_of_missing_template_for_nested_dependencies
    assert_logged "'messages/something_missing' file doesn't exist, so no dependencies" do
      nested_dependencies("messages/something_missing")
    end
  end

  def test_nested_template_directory
    assert_digest_difference("messages/show") do
      change_template("messages/actions/_move")
    end
  end

  def test_recursion_in_renders
    assert digest("level/recursion") # assert recursion is possible
    assert_not_nil digest("level/recursion") # assert digest is stored
  end

  def test_chaining_the_top_template_on_recursion
    assert digest("level/recursion") # assert recursion is possible

    assert_digest_difference("level/recursion") do
      change_template("level/recursion")
    end

    assert_not_nil digest("level/recursion") # assert digest is stored
  end

  def test_chaining_the_partial_template_on_recursion
    assert digest("level/recursion") # assert recursion is possible

    assert_digest_difference("level/recursion") do
      change_template("level/_recursion")
    end

    assert_not_nil digest("level/recursion") # assert digest is stored
  end

  def test_dont_generate_a_digest_for_missing_templates
    assert_equal '', digest("nothing/there")
  end

  def test_collection_dependency
    assert_digest_difference("messages/index") do
      change_template("messages/_message")
    end

    assert_digest_difference("messages/index") do
      change_template("events/_event")
    end
  end

  def test_collection_derived_from_record_dependency
    assert_digest_difference("messages/show") do
      change_template("events/_event")
    end
  end

  def test_details_are_included_in_cache_key
    # Cache the template digest.
    old_digest = digest("events/_event")

    # Change the template; the cached digest remains unchanged.
    change_template("events/_event")

    # The details are changed, so a new cache key is generated.
    finder.details[:foo] = "bar"

    # The cache is busted.
    assert_not_equal old_digest, digest("events/_event")
  end

  def test_extra_whitespace_in_render_partial
    assert_digest_difference("messages/edit") do
      change_template("messages/_form")
    end
  end

  def test_extra_whitespace_in_render_named_partial
    assert_digest_difference("messages/edit") do
      change_template("messages/_header")
    end
  end

  def test_extra_whitespace_in_render_record
    assert_digest_difference("messages/edit") do
      change_template("messages/_message")
    end
  end

  def test_extra_whitespace_in_render_with_parenthesis
    assert_digest_difference("messages/edit") do
      change_template("events/_event")
    end
  end

  def test_old_style_hash_in_render_invocation
    assert_digest_difference("messages/edit") do
      change_template("comments/_comment")
    end
  end

  def test_variants
    assert_digest_difference("messages/new", variants: [:iphone]) do
      change_template("messages/new",     :iphone)
      change_template("messages/_header", :iphone)
    end
  end

  def test_dependencies_via_options_results_in_different_digest
    digest_plain        = digest("comments/_comment")
    digest_fridge       = digest("comments/_comment", dependencies: ["fridge"])
    digest_phone        = digest("comments/_comment", dependencies: ["phone"])
    digest_fridge_phone = digest("comments/_comment", dependencies: ["fridge", "phone"])

    assert_not_equal digest_plain, digest_fridge
    assert_not_equal digest_plain, digest_phone
    assert_not_equal digest_plain, digest_fridge_phone
    assert_not_equal digest_fridge, digest_phone
    assert_not_equal digest_fridge, digest_fridge_phone
    assert_not_equal digest_phone, digest_fridge_phone
  end

  def test_digest_cache_cleanup_with_recursion
    first_digest = digest("level/_recursion")
    second_digest = digest("level/_recursion")

    assert first_digest

    # If the cache is cleaned up correctly, subsequent digests should return the same
    assert_equal first_digest, second_digest
  end

  def test_digest_cache_cleanup_with_recursion_and_template_caching_off
    resolver_before = ActionView::Resolver.caching
    ActionView::Resolver.caching = false

    first_digest = digest("level/_recursion")
    second_digest = digest("level/_recursion")

    assert first_digest

    # If the cache is cleaned up correctly, subsequent digests should return the same
    assert_equal first_digest, second_digest
  ensure
    ActionView::Resolver.caching = resolver_before
  end


  private
    def assert_logged(message)
      old_logger = ActionView::Base.logger
      log = StringIO.new
      ActionView::Base.logger = Logger.new(log)

      begin
        yield

        log.rewind
        assert_match message, log.read
      ensure
        ActionView::Base.logger = old_logger
      end
    end

    def assert_digest_difference(template_name, options = {})
      previous_digest = digest(template_name, options)
      ActionView::Digestor.cache.clear

      yield

      assert previous_digest != digest(template_name, options), "digest didn't change"
      ActionView::Digestor.cache.clear
    end

    def digest(template_name, options = {})
      options = options.dup

      finder.formats  = [:html]
      finder.variants = options.delete(:variants) || []

      ActionView::Digestor.digest({ name: template_name, finder: finder }.merge(options))
    end

    def dependencies(template_name)
      ActionView::Digestor.new({ name: template_name, finder: finder }).dependencies
    end

    def nested_dependencies(template_name)
      ActionView::Digestor.new({ name: template_name, finder: finder }).nested_dependencies
    end

    def finder
      @finder ||= FixtureFinder.new
    end

    def change_template(template_name, variant = nil)
      variant = "+#{variant}" if variant.present?

      File.open("digestor/#{template_name}.html#{variant}.erb", "w") do |f|
        f.write "\nTHIS WAS CHANGED!"
      end
    end
    alias_method :add_template, :change_template

    def remove_template(template_name)
      File.delete("digestor/#{template_name}.html.erb")
    end
end