require File.dirname(__FILE__) + '/../test_helper' require File.dirname(__FILE__) + '/test_helper' require 'sass/plugin' require 'fileutils' module Sass::Script::Functions def filename filename = options[:filename].gsub(%r{.*((/[^/]+){4})}, '\1') Sass::Script::Value::String.new(filename) end def whatever custom = options[:custom] whatever = custom && custom[:whatever] Sass::Script::Value::String.new(whatever || "incorrect") end end class SassPluginTest < MiniTest::Test @@templates = %w{ complex script parent_ref import scss_import alt subdir/subdir subdir/nested_subdir/nested_subdir options import_content filename_fn import_charset import_charset_ibm866 } @@cache_store = Sass::CacheStores::Memory.new def setup Sass::Util.retry_on_windows {FileUtils.mkdir_p tempfile_loc} Sass::Util.retry_on_windows {FileUtils.mkdir_p tempfile_loc(nil,"more_")} set_plugin_opts check_for_updates! reset_mtimes end def teardown clean_up_sassc Sass::Plugin.reset! Sass::Util.retry_on_windows {FileUtils.rm_r tempfile_loc} Sass::Util.retry_on_windows {FileUtils.rm_r tempfile_loc(nil,"more_")} end @@templates.each do |name| define_method("test_template_renders_correctly (#{name})") do silence_warnings {assert_renders_correctly(name)} end end def test_no_update File.delete(tempfile_loc('basic')) assert_needs_update 'basic' check_for_updates! assert_stylesheet_updated 'basic' end def test_update_needed_when_modified touch 'basic' assert_needs_update 'basic' check_for_updates! assert_stylesheet_updated 'basic' end def test_update_needed_when_dependency_modified touch 'basic' assert_needs_update 'import' check_for_updates! assert_stylesheet_updated 'basic' assert_stylesheet_updated 'import' end def test_update_needed_when_scss_dependency_modified touch 'scss_importee' assert_needs_update 'import' check_for_updates! assert_stylesheet_updated 'scss_importee' assert_stylesheet_updated 'import' end def test_scss_update_needed_when_dependency_modified touch 'basic' assert_needs_update 'scss_import' check_for_updates! assert_stylesheet_updated 'basic' assert_stylesheet_updated 'scss_import' end def test_update_needed_when_nested_import_dependency_modified touch 'basic' assert_needs_update 'nested_import' check_for_updates! assert_stylesheet_updated 'basic' assert_stylesheet_updated 'scss_import' end def test_no_updates_when_always_check_and_always_update_both_false Sass::Plugin.options[:always_update] = false Sass::Plugin.options[:always_check] = false touch 'basic' assert_needs_update 'basic' check_for_updates! # Check it's still stale assert_needs_update 'basic' end def test_full_exception_handling File.delete(tempfile_loc('bork1')) check_for_updates! File.open(tempfile_loc('bork1')) do |file| assert_equal(< { template_loc => tempfile_loc, template_loc(nil,'more_') => tempfile_loc(nil,'more_') } check_for_updates! ['more1', 'more_import'].each { |name| assert_renders_correctly(name, :prefix => 'more_') } end def test_two_template_directories_with_line_annotations set_plugin_opts :line_comments => true, :style => :nested, :template_location => { template_loc => tempfile_loc, template_loc(nil,'more_') => tempfile_loc(nil,'more_') } check_for_updates! assert_renders_correctly('more1_with_line_comments', 'more1', :prefix => 'more_') end def test_doesnt_render_partials assert !File.exist?(tempfile_loc('_partial')) end def test_template_location_array assert_equal [[template_loc, tempfile_loc]], Sass::Plugin.template_location_array end def test_add_template_location Sass::Plugin.add_template_location(template_loc(nil, "more_"), tempfile_loc(nil, "more_")) assert_equal( [[template_loc, tempfile_loc], [template_loc(nil, "more_"), tempfile_loc(nil, "more_")]], Sass::Plugin.template_location_array) touch 'more1', 'more_' touch 'basic' assert_needs_update "more1", "more_" assert_needs_update "basic" check_for_updates! assert_doesnt_need_update "more1", "more_" assert_doesnt_need_update "basic" end def test_remove_template_location Sass::Plugin.add_template_location(template_loc(nil, "more_"), tempfile_loc(nil, "more_")) Sass::Plugin.remove_template_location(template_loc, tempfile_loc) assert_equal( [[template_loc(nil, "more_"), tempfile_loc(nil, "more_")]], Sass::Plugin.template_location_array) touch 'more1', 'more_' touch 'basic' assert_needs_update "more1", "more_" assert_needs_update "basic" check_for_updates! assert_doesnt_need_update "more1", "more_" assert_needs_update "basic" end def test_import_same_name assert_warning < [template_loc(nil, "more_")] touch 'basic', 'more_' assert_needs_update "import" check_for_updates! assert_renders_correctly("import") ensure FileUtils.mv(template_loc("basic", "more_"), template_loc("basic")) end def test_cached_relative_import old_always_update = Sass::Plugin.options[:always_update] Sass::Plugin.options[:always_update] = true check_for_updates! assert_renders_correctly('subdir/subdir') ensure Sass::Plugin.options[:always_update] = old_always_update end def test_cached_if set_plugin_opts :cache_store => Sass::CacheStores::Filesystem.new(tempfile_loc + '/cache') check_for_updates! assert_renders_correctly 'if' check_for_updates! assert_renders_correctly 'if' ensure set_plugin_opts end def test_cached_import_option set_plugin_opts :custom => {:whatever => "correct"} check_for_updates! assert_renders_correctly "cached_import_option" @@cache_store.reset! set_plugin_opts :custom => nil, :always_update => false check_for_updates! assert_renders_correctly "cached_import_option" set_plugin_opts :custom => {:whatever => "correct"}, :always_update => true check_for_updates! assert_renders_correctly "cached_import_option" ensure set_plugin_opts :custom => nil end private def assert_renders_correctly(*arguments) options = arguments.last.is_a?(Hash) ? arguments.pop : {} prefix = options[:prefix] result_name = arguments.shift tempfile_name = arguments.shift || result_name expected_str = File.read(result_loc(result_name, prefix)) actual_str = File.read(tempfile_loc(tempfile_name, prefix)) expected_str = expected_str.force_encoding('IBM866') if result_name == 'import_charset_ibm866' actual_str = actual_str.force_encoding('IBM866') if tempfile_name == 'import_charset_ibm866' expected_lines = expected_str.split("\n") actual_lines = actual_str.split("\n") if actual_lines.first == "/*" && expected_lines.first != "/*" assert(false, actual_lines[0..actual_lines.each_with_index.find {|l, i| l == "*/"}.last].join("\n")) end expected_lines.zip(actual_lines).each_with_index do |pair, line| message = "template: #{result_name}\nline: #{line + 1}" assert_equal(pair.first, pair.last, message) end if expected_lines.size < actual_lines.size assert(false, "#{actual_lines.size - expected_lines.size} Trailing lines found in #{tempfile_name}.css: #{actual_lines[expected_lines.size..-1].join('\n')}") end end def assert_stylesheet_updated(name) assert_doesnt_need_update name # Make sure it isn't an exception expected_lines = File.read(result_loc(name)).split("\n") actual_lines = File.read(tempfile_loc(name)).split("\n") if actual_lines.first == "/*" && expected_lines.first != "/*" assert(false, actual_lines[0..actual_lines.each_with_index.find {|l, i| l == "*/"}.last].join("\n")) end end def assert_callback(name, *expected_args) run = false received_args = nil Sass::Plugin.send("on_#{name}") do |*args| received_args = args run ||= expected_args.zip(received_args).all? do |ea, ra| ea.respond_to?(:call) ? ea.call(ra) : ea == ra end end if block_given? Sass::Util.silence_sass_warnings {yield} else check_for_updates! end assert run, "Expected #{name} callback to be run with arguments:\n #{expected_args.inspect}\nHowever, it got:\n #{received_args.inspect}" end def assert_no_callback(name, *unexpected_args) Sass::Plugin.send("on_#{name}") do |*a| next unless unexpected_args.empty? || a == unexpected_args msg = "Expected #{name} callback not to be run" if !unexpected_args.empty? msg << " with arguments #{unexpected_args.inspect}" elsif !a.empty? msg << ",\n was run with arguments #{a.inspect}" end flunk msg end if block_given? yield else check_for_updates! end end def assert_callbacks(*args) return check_for_updates! if args.empty? assert_callback(*args.pop) {assert_callbacks(*args)} end def assert_no_callbacks(*args) return check_for_updates! if args.empty? assert_no_callback(*args.pop) {assert_no_callbacks(*args)} end def check_for_updates! Sass::Util.silence_sass_warnings do Sass::Plugin.check_for_updates end end def assert_needs_update(*args) assert(Sass::Plugin::StalenessChecker.stylesheet_needs_update?(tempfile_loc(*args), template_loc(*args)), "Expected #{template_loc(*args)} to need an update.") end def assert_doesnt_need_update(*args) assert(!Sass::Plugin::StalenessChecker.stylesheet_needs_update?(tempfile_loc(*args), template_loc(*args)), "Expected #{template_loc(*args)} not to need an update.") end def touch(*args) FileUtils.touch(template_loc(*args)) end def reset_mtimes Sass::Plugin::StalenessChecker.dependencies_cache = {} atime = Time.now mtime = Time.now - 5 Dir["{#{template_loc},#{tempfile_loc}}/**/*.{css,sass,scss}"].each do |f| Sass::Util.retry_on_windows {File.utime(atime, mtime, f)} end end def template_loc(name = nil, prefix = nil) if name scss = absolutize "#{prefix}templates/#{name}.scss" File.exist?(scss) ? scss : absolutize("#{prefix}templates/#{name}.sass") else absolutize "#{prefix}templates" end end def tempfile_loc(name = nil, prefix = nil) if name absolutize "#{prefix}tmp/#{name}.css" else absolutize "#{prefix}tmp" end end def result_loc(name = nil, prefix = nil) if name absolutize "#{prefix}results/#{name}.css" else absolutize "#{prefix}results" end end def set_plugin_opts(overrides = {}) Sass::Plugin.options.merge!( :template_location => template_loc, :css_location => tempfile_loc, :style => :compact, :always_update => true, :never_update => false, :full_exception => true, :cache_store => @@cache_store, :sourcemap => :none ) Sass::Plugin.options.merge!(overrides) end end class Sass::Engine alias_method :old_render, :render def render raise "bork bork bork!" if @template[0] == "{bork now!}" old_render end end