vendor/plugins/haml/test/sass/engine_test.rb in radiant-0.9.1 vs vendor/plugins/haml/test/sass/engine_test.rb in radiant-1.0.0.rc1
- old
+ new
@@ -1,73 +1,79 @@
#!/usr/bin/env ruby
+# -*- coding: utf-8 -*-
require File.dirname(__FILE__) + '/../test_helper'
require 'sass/engine'
require 'stringio'
+module Sass::Script::Functions::UserFunctions
+ def option(name)
+ Sass::Script::String.new(@options[name.value.to_sym].to_s)
+ end
+end
+
class SassEngineTest < Test::Unit::TestCase
# A map of erroneous Sass 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 = {
- "!a = 1 + " => 'Expected expression, was end of text.',
- "!a = 1 + 2 +" => 'Expected expression, was end of text.',
- "!a = 1 + 2 + %" => 'Expected expression, was mod token.',
- "!a = foo(\"bar\"" => 'Expected rparen token, was end of text.',
- "!a = 1 }" => 'Unexpected end_interpolation token.',
- "!a = 1 }foo\"" => 'Unexpected end_interpolation token.',
+ "$a: 1 + " => 'Invalid CSS after "1 +": expected expression (e.g. 1px, bold), was ""',
+ "$a: 1 + 2 +" => 'Invalid CSS after "1 + 2 +": expected expression (e.g. 1px, bold), was ""',
+ "$a: 1 + 2 + %" => 'Invalid CSS after "1 + 2 + ": expected expression (e.g. 1px, bold), was "%"',
+ "$a: foo(\"bar\"" => 'Invalid CSS after "foo("bar"": expected ")", was ""',
+ "$a: 1 }" => 'Invalid CSS after "1 ": expected expression (e.g. 1px, bold), was "}"',
+ "$a: 1 }foo\"" => 'Invalid CSS after "1 ": expected expression (e.g. 1px, bold), was "}foo""',
":" => 'Invalid property: ":".',
": a" => 'Invalid property: ": a".',
- ":= a" => 'Invalid property: ":= a".',
"a\n :b" => <<MSG,
Invalid property: ":b" (no value).
If ":b" should be a selector, use "\\:b" instead.
MSG
"a\n b:" => 'Invalid property: "b:" (no value).',
"a\n :b: c" => 'Invalid property: ":b: c".',
"a\n :b:c d" => 'Invalid property: ":b:c d".',
- "a\n :b=c d" => 'Invalid property: ":b=c d".',
- "a\n :b c;" => 'Invalid property: ":b c;" (no ";" required at end-of-line).',
- "a\n b: c;" => 'Invalid property: "b: c;" (no ";" required at end-of-line).',
- "a\n b : c" => 'Invalid property: "b : c".',
- "a\n b=c: d" => 'Invalid property: "b=c: d".',
+ "a\n :b c;" => 'Invalid CSS after "c": expected expression (e.g. 1px, bold), was ";"',
+ "a\n b: c;" => 'Invalid CSS after "c": expected expression (e.g. 1px, bold), was ";"',
+ ".foo ^bar\n a: b" => ['Invalid CSS after ".foo ": expected selector, was "^bar"', 1],
+ "a\n @extend .foo ^bar" => 'Invalid CSS after ".foo ": expected selector, was "^bar"',
+ "a: b" => 'Properties aren\'t allowed at the root of a document.',
":a b" => 'Properties aren\'t allowed at the root of a document.',
- "a:" => 'Properties aren\'t allowed at the root of a document.',
- ":a" => <<MSG,
-Properties aren't allowed at the root of a document.
-If ":a" should be a selector, use "\\:a" instead.
-MSG
"!" => 'Invalid variable: "!".',
- "!a" => 'Invalid variable: "!a".',
+ "$a" => 'Invalid variable: "$a".',
"! a" => 'Invalid variable: "! a".',
- "!a b" => 'Invalid variable: "!a b".',
- "!a = 1b + 2c" => "Incompatible units: 'c' and 'b'.",
- "!a = 1b < 2c" => "Incompatible units: 'c' and 'b'.",
- "!a = 1b > 2c" => "Incompatible units: 'c' and 'b'.",
- "!a = 1b <= 2c" => "Incompatible units: 'c' and 'b'.",
- "!a = 1b >= 2c" => "Incompatible units: 'c' and 'b'.",
- "a\n :b= 1b * 2c" => "2b*c isn't a valid CSS value.",
- "a\n :b= 1b % 2c" => "Cannot modulo by a number with units: 2c.",
- "!a = 2px + #ccc" => "Cannot add a number with units (2px) to a color (#cccccc).",
- "!a = #ccc + 2px" => "Cannot add a number with units (2px) to a color (#cccccc).",
+ "$a b" => 'Invalid variable: "$a b".',
+ "$a: 1b + 2c" => "Incompatible units: 'c' and 'b'.",
+ "$a: 1b < 2c" => "Incompatible units: 'c' and 'b'.",
+ "$a: 1b > 2c" => "Incompatible units: 'c' and 'b'.",
+ "$a: 1b <= 2c" => "Incompatible units: 'c' and 'b'.",
+ "$a: 1b >= 2c" => "Incompatible units: 'c' and 'b'.",
+ "a\n b: 1b * 2c" => "2b*c isn't a valid CSS value.",
+ "a\n b: 1b % 2c" => "Cannot modulo by a number with units: 2c.",
+ "$a: 2px + #ccc" => "Cannot add a number with units (2px) to a color (#cccccc).",
+ "$a: #ccc + 2px" => "Cannot add a number with units (2px) to a color (#cccccc).",
"& a\n :b c" => ["Base-level rules cannot contain the parent-selector-referencing character '&'.", 1],
"a\n :b\n c" => "Illegal nesting: Only properties may be nested beneath properties.",
"a,\n :b c" => ["Rules can\'t end in commas.", 1],
"a," => "Rules can\'t end in commas.",
- "a,\n!b = 1" => ["Rules can\'t end in commas.", 1],
- "!a = b\n :c d\n" => "Illegal nesting: Nothing may be nested beneath variable declarations.",
+ "a,\n$b: 1" => ["Rules can\'t end in commas.", 1],
+ "$a: b\n :c d\n" => "Illegal nesting: Nothing may be nested beneath variable declarations.",
+ "@import foo.sass" => "File to import not found or unreadable: foo.sass.",
+ "a,\n$b: 1" => ["Rules can\'t end in commas.", 1],
+ "$a: b\n :c d\n" => "Illegal nesting: Nothing may be nested beneath variable declarations.",
"@import foo.sass" => <<MSG,
File to import not found or unreadable: foo.sass.
Load paths:
#{File.dirname(__FILE__)}
.
MSG
"@import templates/basic\n foo" => "Illegal nesting: Nothing may be nested beneath import directives.",
"foo\n @import templates/basic" => "Import directives may only be used at the root of a document.",
"foo\n @import #{File.dirname(__FILE__)}/templates/basic" => "Import directives may only be used at the root of a document.",
- %Q{!foo = "bar" "baz" !} => %Q{Syntax error in '"bar" "baz" !' at character 20.},
+ '$foo: "bar" "baz" !' => %Q{Invalid CSS after ""bar" "baz" ": expected expression (e.g. 1px, bold), was "!"},
"=foo\n :color red\n.bar\n +bang" => "Undefined mixin 'bang'.",
+ "=foo\n :color red\n.bar\n +bang_bop" => "Undefined mixin 'bang_bop'.",
+ "=foo\n :color red\n.bar\n +bang-bop" => "Undefined mixin 'bang-bop'.",
".bar\n =foo\n :color red\n" => ["Mixins may only be defined at the root of a document.", 2],
"=foo\n :color red\n.bar\n +foo\n :color red" => "Illegal nesting: Nothing may be nested beneath mixin directives.",
" a\n b: c" => ["Indenting at the beginning of the document is illegal.", 1],
" \n \n\t\n a\n b: c" => ["Indenting at the beginning of the document is illegal.", 4],
"a\n b: c\n b: c" => ["Inconsistent indentation: 1 space was used for indentation, but the rest of the document was indented using 2 spaces.", 3],
@@ -76,33 +82,39 @@
"a\n b: c\n b: c" => ["Inconsistent indentation: 3 spaces were used for indentation, but the rest of the document was indented using 2 spaces.", 3],
"a\n b: c\n a\n d: e" => ["Inconsistent indentation: 3 spaces were used for indentation, but the rest of the document was indented using 2 spaces.", 4],
"a\n b: c\na\n d: e" => ["The line was indented 2 levels deeper than the previous line.", 4],
"a\n b: c\n a\n d: e" => ["The line was indented 3 levels deeper than the previous line.", 4],
"a\n \tb: c" => ["Indentation can't use both tabs and spaces.", 2],
- "=a(" => 'Expected rparen token, was end of text.',
- "=a(b)" => 'Expected rparen token, was ident token.',
- "=a(,)" => "Expected rparen token, was comma token.",
- "=a(!)" => "Syntax error in '(!)' at character 4.",
- "=a(!foo bar)" => "Expected rparen token, was ident token.",
+ "=a(" => 'Invalid CSS after "(": expected variable (e.g. $foo), was ""',
+ "=a(b)" => 'Invalid CSS after "(": expected variable (e.g. $foo), was "b)"',
+ "=a(,)" => 'Invalid CSS after "(": expected variable (e.g. $foo), was ",)"',
+ "=a($)" => 'Invalid CSS after "(": expected variable (e.g. $foo), was "$)"',
+ "=a($foo bar)" => 'Invalid CSS after "($foo ": expected ")", was "bar)"',
"=foo\n bar: baz\n+foo" => ["Properties aren't allowed at the root of a document.", 2],
- "a-\#{!b\n c: d" => ["Expected end_interpolation token, was end of text.", 1],
- "=a(!b = 1, !c)" => "Required argument !c must come before any optional arguments.",
- "=a(!b = 1)\n :a= !b\ndiv\n +a(1,2)" => "Mixin a takes 1 argument but 2 were passed.",
- "=a(!b)\n :a= !b\ndiv\n +a" => "Mixin a is missing parameter !b.",
+ "a-\#{$b\n c: d" => ['Invalid CSS after "a-#{$b": expected "}", was ""', 1],
+ "=a($b = 1, $c)" => "Required argument $c must come before any optional arguments.",
+ "=a($b = 1)\n a: $b\ndiv\n +a(1,2)" => "Mixin a takes 1 argument but 2 were passed.",
+ "=a($b)\n a: $b\ndiv\n +a" => "Mixin a is missing parameter $b.",
"@else\n a\n b: c" => ["@else must come after @if.", 1],
"@if false\n@else foo" => "Invalid else directive '@else foo': expected 'if <expr>'.",
"@if false\n@else if " => "Invalid else directive '@else if': expected 'if <expr>'.",
- "a\n !b = 12\nc\n d = !b" => 'Undefined variable: "!b".',
- "=foo\n !b = 12\nc\n +foo\n d = !b" => 'Undefined variable: "!b".',
- '@for !a from "foo" to 1' => '"foo" is not an integer.',
- '@for !a from 1 to "2"' => '"2" is not an integer.',
- '@for !a from 1 to "foo"' => '"foo" is not an integer.',
- '@for !a from 1 to 1.232323' => '1.232 is not an integer.',
- '@for !a from 1px to 3em' => "Incompatible units: 'em' and 'px'.",
+ "a\n !b: 12\nc\n d: !b" => 'Undefined variable: "$b".',
+ "a\n $b: 12\nc\n d: $b" => 'Undefined variable: "$b".',
+ "=foo\n $b: 12\nc\n +foo\n d: $b" => 'Undefined variable: "$b".',
+ "c\n d: $b-foo" => 'Undefined variable: "$b-foo".',
+ "c\n d: $b_foo" => 'Undefined variable: "$b_foo".',
+ '@for $a from "foo" to 1' => '"foo" is not an integer.',
+ '@for $a from 1 to "2"' => '"2" is not an integer.',
+ '@for $a from 1 to "foo"' => '"foo" is not an integer.',
+ '@for $a from 1 to 1.232323' => '1.232 is not an integer.',
+ '@for $a from 1px to 3em' => "Incompatible units: 'em' and 'px'.",
'@if' => "Invalid if directive '@if': expected expression.",
'@while' => "Invalid while directive '@while': expected expression.",
'@debug' => "Invalid debug directive '@debug': expected expression.",
+ %Q{@debug "a message"\n "nested message"} => "Illegal nesting: Nothing may be nested beneath debug directives.",
+ '@warn' => "Invalid warn directive '@warn': expected expression.",
+ %Q{@warn "a message"\n "nested message"} => "Illegal nesting: Nothing may be nested beneath warn directives.",
"/* foo\n bar\n baz" => "Inconsistent indentation: previous line was indented by 4 spaces, but this line was indented by 2 spaces.",
# Regression tests
"a\n b:\n c\n d" => ["Illegal nesting: Only properties may be nested beneath properties.", 3],
"& foo\n bar: baz\n blat: bang" => ["Base-level rules cannot contain the parent-selector-referencing character '&'.", 1],
@@ -192,36 +204,312 @@
assert(false, "Exception not raised for '#{to_render}'!")
end
end
def test_imported_exception
- [nil, 2].each do |i|
+ [1, 2, 3, 4].each do |i|
begin
Sass::Engine.new("@import bork#{i}", :load_paths => [File.dirname(__FILE__) + '/templates/']).render
rescue Sass::SyntaxError => err
assert_equal(2, err.sass_line)
- assert_match(/bork#{i}\.sass$/, err.sass_filename)
+ assert_match(/(\/|^)bork#{i}\.sass$/, err.sass_filename)
+
+ assert_hash_has(err.sass_backtrace.first,
+ :filename => err.sass_filename, :line => err.sass_line)
+
+ assert_nil(err.sass_backtrace[1][:filename])
+ assert_equal(1, err.sass_backtrace[1][:line])
+
+ assert_match(/(\/|^)bork#{i}\.sass:2$/, err.backtrace.first)
+ assert_equal("(sass):1", err.backtrace[1])
else
assert(false, "Exception not raised for imported template: bork#{i}")
end
end
end
+ def test_double_imported_exception
+ [1, 2, 3, 4].each do |i|
+ begin
+ Sass::Engine.new("@import nested_bork#{i}", :load_paths => [File.dirname(__FILE__) + '/templates/']).render
+ rescue Sass::SyntaxError => err
+ assert_equal(2, err.sass_line)
+ assert_match(/(\/|^)bork#{i}\.sass$/, err.sass_filename)
+
+ assert_hash_has(err.sass_backtrace.first,
+ :filename => err.sass_filename, :line => err.sass_line)
+
+ assert_match(/(\/|^)nested_bork#{i}\.sass$/, err.sass_backtrace[1][:filename])
+ assert_equal(2, err.sass_backtrace[1][:line])
+
+ assert_nil(err.sass_backtrace[2][:filename])
+ assert_equal(1, err.sass_backtrace[2][:line])
+
+ assert_match(/(\/|^)bork#{i}\.sass:2$/, err.backtrace.first)
+ assert_match(/(\/|^)nested_bork#{i}\.sass:2$/, err.backtrace[1])
+ assert_equal("(sass):1", err.backtrace[2])
+ else
+ assert(false, "Exception not raised for imported template: bork#{i}")
+ end
+ end
+ end
+
+ def test_mixin_exception
+ render(<<SASS)
+=error-mixin($a)
+ color: $a * 1em * 1px
+
+=outer-mixin($a)
+ +error-mixin($a)
+
+.error
+ +outer-mixin(12)
+SASS
+ assert(false, "Exception not raised")
+ rescue Sass::SyntaxError => err
+ assert_equal(2, err.sass_line)
+ assert_equal(filename_for_test, err.sass_filename)
+ assert_equal("error-mixin", err.sass_mixin)
+
+ assert_hash_has(err.sass_backtrace.first, :line => err.sass_line,
+ :filename => err.sass_filename, :mixin => err.sass_mixin)
+ assert_hash_has(err.sass_backtrace[1], :line => 5,
+ :filename => filename_for_test, :mixin => "outer-mixin")
+ assert_hash_has(err.sass_backtrace[2], :line => 8,
+ :filename => filename_for_test, :mixin => nil)
+
+ assert_equal("#{filename_for_test}:2:in `error-mixin'", err.backtrace.first)
+ assert_equal("#{filename_for_test}:5:in `outer-mixin'", err.backtrace[1])
+ assert_equal("#{filename_for_test}:8", err.backtrace[2])
+ end
+
+ def test_mixin_callsite_exception
+ render(<<SASS)
+=one-arg-mixin($a)
+ color: $a
+
+=outer-mixin($a)
+ +one-arg-mixin($a, 12)
+
+.error
+ +outer-mixin(12)
+SASS
+ assert(false, "Exception not raised")
+ rescue Sass::SyntaxError => err
+ assert_hash_has(err.sass_backtrace.first, :line => 5,
+ :filename => filename_for_test, :mixin => "one-arg-mixin")
+ assert_hash_has(err.sass_backtrace[1], :line => 5,
+ :filename => filename_for_test, :mixin => "outer-mixin")
+ assert_hash_has(err.sass_backtrace[2], :line => 8,
+ :filename => filename_for_test, :mixin => nil)
+ end
+
+ def test_mixin_exception_cssize
+ render(<<SASS)
+=parent-ref-mixin
+ & foo
+ a: b
+
+=outer-mixin
+ +parent-ref-mixin
+
++outer-mixin
+SASS
+ assert(false, "Exception not raised")
+ rescue Sass::SyntaxError => err
+ assert_hash_has(err.sass_backtrace.first, :line => 2,
+ :filename => filename_for_test, :mixin => "parent-ref-mixin")
+ assert_hash_has(err.sass_backtrace[1], :line => 6,
+ :filename => filename_for_test, :mixin => "outer-mixin")
+ assert_hash_has(err.sass_backtrace[2], :line => 8,
+ :filename => filename_for_test, :mixin => nil)
+ end
+
+ def test_mixin_and_import_exception
+ Sass::Engine.new("@import nested_mixin_bork", :load_paths => [File.dirname(__FILE__) + '/templates/']).render
+ assert(false, "Exception not raised")
+ rescue Sass::SyntaxError => err
+ assert_match(/(\/|^)nested_mixin_bork\.sass$/, err.sass_backtrace.first[:filename])
+ assert_hash_has(err.sass_backtrace.first, :mixin => "error-mixin", :line => 4)
+
+ assert_match(/(\/|^)mixin_bork\.sass$/, err.sass_backtrace[1][:filename])
+ assert_hash_has(err.sass_backtrace[1], :mixin => "outer-mixin", :line => 2)
+
+ assert_match(/(\/|^)mixin_bork\.sass$/, err.sass_backtrace[2][:filename])
+ assert_hash_has(err.sass_backtrace[2], :mixin => nil, :line => 5)
+
+ assert_match(/(\/|^)nested_mixin_bork\.sass$/, err.sass_backtrace[3][:filename])
+ assert_hash_has(err.sass_backtrace[3], :mixin => nil, :line => 6)
+
+ assert_hash_has(err.sass_backtrace[4], :filename => nil, :mixin => nil, :line => 1)
+ end
+
+ def test_basic_mixin_loop_exception
+ render <<SASS
+@mixin foo
+ @include foo
+@include foo
+SASS
+ assert(false, "Exception not raised")
+ rescue Sass::SyntaxError => err
+ assert_equal("An @include loop has been found: foo includes itself", err.message)
+ assert_hash_has(err.sass_backtrace[0], :mixin => "foo", :line => 2)
+ end
+
+ def test_double_mixin_loop_exception
+ render <<SASS
+@mixin foo
+ @include bar
+@mixin bar
+ @include foo
+@include foo
+SASS
+ assert(false, "Exception not raised")
+ rescue Sass::SyntaxError => err
+ assert_equal(<<MESSAGE.rstrip, err.message)
+An @include loop has been found:
+ foo includes bar
+ bar includes foo
+MESSAGE
+ assert_hash_has(err.sass_backtrace[0], :mixin => "bar", :line => 4)
+ assert_hash_has(err.sass_backtrace[1], :mixin => "foo", :line => 2)
+ end
+
+ def test_deep_mixin_loop_exception
+ render <<SASS
+@mixin foo
+ @include bar
+
+@mixin bar
+ @include baz
+
+@mixin baz
+ @include foo
+
+@include foo
+SASS
+ assert(false, "Exception not raised")
+ rescue Sass::SyntaxError => err
+ assert_equal(<<MESSAGE.rstrip, err.message)
+An @include loop has been found:
+ foo includes bar
+ bar includes baz
+ baz includes foo
+MESSAGE
+ assert_hash_has(err.sass_backtrace[0], :mixin => "baz", :line => 8)
+ assert_hash_has(err.sass_backtrace[1], :mixin => "bar", :line => 5)
+ assert_hash_has(err.sass_backtrace[2], :mixin => "foo", :line => 2)
+ end
+
+ def test_exception_css_with_offset
+ opts = {:full_exception => true, :line => 362}
+ render(("a\n b: c\n" * 10) + "d\n e:\n" + ("f\n g: h\n" * 10), opts)
+ rescue Sass::SyntaxError => e
+ assert_equal(<<CSS, Sass::SyntaxError.exception_to_css(e, opts).split("\n")[0..15].join("\n"))
+/*
+Syntax error: Invalid property: "e:" (no value).
+ on line 383 of test_exception_css_with_offset_inline.sass
+
+378: a
+379: b: c
+380: a
+381: b: c
+382: d
+383: e:
+384: f
+385: g: h
+386: f
+387: g: h
+388: f
+CSS
+ else
+ assert(false, "Exception not raised for test_exception_css_with_offset")
+ end
+
+ def test_exception_css_with_mixins
+ opts = {:full_exception => true}
+ render(<<SASS, opts)
+=error-mixin($a)
+ color: $a * 1em * 1px
+
+=outer-mixin($a)
+ +error-mixin($a)
+
+.error
+ +outer-mixin(12)
+SASS
+ rescue Sass::SyntaxError => e
+ assert_equal(<<CSS, Sass::SyntaxError.exception_to_css(e, opts).split("\n")[0..13].join("\n"))
+/*
+Syntax error: 12em*px isn't a valid CSS value.
+ on line 2 of test_exception_css_with_mixins_inline.sass, in `error-mixin'
+ from line 5 of test_exception_css_with_mixins_inline.sass, in `outer-mixin'
+ from line 8 of test_exception_css_with_mixins_inline.sass
+
+1: =error-mixin($a)
+2: color: $a * 1em * 1px
+3:
+4: =outer-mixin($a)
+5: +error-mixin($a)
+6:
+7: .error
+CSS
+ else
+ assert(false, "Exception not raised")
+ end
+
+ def test_cssize_exception_css
+ opts = {:full_exception => true}
+ render(<<SASS, opts)
+.filler
+ stuff: "stuff!"
+
+a: b
+
+.more.filler
+ a: b
+SASS
+ rescue Sass::SyntaxError => e
+ assert_equal(<<CSS, Sass::SyntaxError.exception_to_css(e, opts).split("\n")[0..11].join("\n"))
+/*
+Syntax error: Properties aren't allowed at the root of a document.
+ on line 4 of test_cssize_exception_css_inline.sass
+
+1: .filler
+2: stuff: "stuff!"
+3:
+4: a: b
+5:
+6: .more.filler
+7: a: b
+CSS
+ else
+ assert(false, "Exception not raised")
+ end
+
def test_css_import
- assert_equal("@import url(./fonts.css) screen;\n", render("@import url(./fonts.css) screen"))
- assert_equal("@import \"./fonts.css\" screen;\n", render("@import \"./fonts.css\" screen"))
+ assert_equal("@import url(./fonts.css);\n", render("@import \"./fonts.css\""))
end
+ def test_http_import
+ assert_equal("@import url(http://fonts.googleapis.com/css?family=Droid+Sans);\n",
+ render("@import \"http://fonts.googleapis.com/css?family=Droid+Sans\""))
+ end
+
+ def test_url_import
+ assert_equal("@import url(fonts.sass);\n", render("@import url(fonts.sass)"))
+ end
+
def test_sass_import
assert !File.exists?(sassc_path("importee"))
renders_correctly "import", { :style => :compact, :load_paths => [File.dirname(__FILE__) + "/templates"] }
assert File.exists?(sassc_path("importee"))
end
def test_nonexistent_extensionless_import
assert_warning(<<WARN) do
-WARNING: nonexistent.sass not found. Using nonexistent.css instead.
+WARNING: Neither nonexistent.sass nor .scss found. Using nonexistent.css instead.
This behavior is deprecated and will be removed in a future version.
If you really need nonexistent.css, import it explicitly.
WARN
assert_equal("@import url(nonexistent.css);\n", render("@import nonexistent"))
end
@@ -239,22 +527,28 @@
def test_units
renders_correctly "units"
end
def test_default_function
- assert_equal("foo {\n bar: url(foo.png); }\n", render(%Q{foo\n bar = url("foo.png")\n}));
- assert_equal("foo {\n bar: url(); }\n", render("foo\n bar = url()\n"));
+ assert_equal(<<CSS, render(<<SASS))
+foo {
+ bar: url("foo.png"); }
+CSS
+foo
+ bar: url("foo.png")
+SASS
+ assert_equal("foo {\n bar: url(); }\n", render("foo\n bar: url()\n"));
end
def test_string_minus
- assert_equal("foo {\n bar: baz-boom-bat; }\n", render(%Q{foo\n bar = "baz"-"boom"-"bat"}))
- assert_equal("foo {\n bar: -baz-boom; }\n", render(%Q{foo\n bar = -"baz"-"boom"}))
+ assert_equal("foo {\n bar: baz-boom-bat; }\n", render(%Q{foo\n bar: baz-boom-bat}))
+ assert_equal("foo {\n bar: -baz-boom; }\n", render(%Q{foo\n bar: -baz-boom}))
end
def test_string_div
- assert_equal("foo {\n bar: baz/boom/bat; }\n", render(%Q{foo\n bar = "baz"/"boom"/"bat"}))
- assert_equal("foo {\n bar: /baz/boom; }\n", render(%Q{foo\n bar = /"baz"/"boom"}))
+ assert_equal("foo {\n bar: baz/boom/bat; }\n", render(%Q{foo\n bar: baz/boom/bat}))
+ assert_equal("foo {\n bar: /baz/boom; }\n", render(%Q{foo\n bar: /baz/boom}))
end
def test_basic_multiline_selector
assert_equal("#foo #bar,\n#baz #boom {\n foo: bar; }\n",
render("#foo #bar,\n#baz #boom\n :foo bar"))
@@ -364,10 +658,44 @@
assert_equal(rendered, render(to_render, :style => :compact))
assert_equal("@a{b:c;#d{e:f}g:h}\n", render(to_render, :style => :compressed))
end
+ def test_property_hacks
+ assert_equal(<<CSS, render(<<SASS))
+foo {
+ _name: val;
+ *name: val;
+ #name: val;
+ .name: val;
+ name/**/: val;
+ name/*\\**/: val;
+ name: val; }
+CSS
+foo
+ _name: val
+ *name: val
+ #name: val
+ .name: val
+ name/**/: val
+ name/*\\**/: val
+ name: val
+SASS
+ end
+
+ def test_properties_with_space_after_colon
+ assert_equal <<CSS, render(<<SASS)
+foo {
+ bar: baz;
+ bizz: bap; }
+CSS
+foo
+ bar : baz
+ bizz : bap
+SASS
+ end
+
def test_line_annotations
assert_equal(<<CSS, render(<<SASS, :line_comments => true, :style => :compact))
/* line 2, test_line_annotations_inline.sass */
foo bar { foo: bar; }
/* line 5, test_line_annotations_inline.sass */
@@ -412,10 +740,89 @@
def test_line_annotations_with_filename
renders_correctly "line_numbers", :line_comments => true, :load_paths => [File.dirname(__FILE__) + "/templates"]
end
+ def test_debug_info
+ esc_file_name = Sass::SCSS::RX.escape_ident(Haml::Util.scope("test_debug_info_inline.sass"))
+
+ assert_equal(<<CSS, render(<<SASS, :debug_info => true, :style => :compact))
+@media -sass-debug-info{filename{font-family:file\\:\\/\\/#{esc_file_name}}line{font-family:\\000032}}
+foo bar { foo: bar; }
+@media -sass-debug-info{filename{font-family:file\\:\\/\\/#{esc_file_name}}line{font-family:\\000035}}
+foo baz { blip: blop; }
+
+@media -sass-debug-info{filename{font-family:file\\:\\/\\/#{esc_file_name}}line{font-family:\\000039}}
+floodle { flop: blop; }
+
+@media -sass-debug-info{filename{font-family:file\\:\\/\\/#{esc_file_name}}line{font-family:\\0000318}}
+bup { mix: on; }
+@media -sass-debug-info{filename{font-family:file\\:\\/\\/#{esc_file_name}}line{font-family:\\0000315}}
+bup mixin { moop: mup; }
+
+@media -sass-debug-info{filename{font-family:file\\:\\/\\/#{esc_file_name}}line{font-family:\\0000322}}
+bip hop, skip hop { a: b; }
+CSS
+foo
+ bar
+ foo: bar
+
+ baz
+ blip: blop
+
+
+floodle
+
+ flop: blop
+
+=mxn
+ mix: on
+ mixin
+ moop: mup
+
+bup
+ +mxn
+
+bip, skip
+ hop
+ a: b
+SASS
+ end
+
+ def test_debug_info_without_filename
+ assert_equal(<<CSS, Sass::Engine.new(<<SASS, :debug_info => true).render)
+@media -sass-debug-info{filename{font-family:}line{font-family:\\000031}}
+foo {
+ a: b; }
+CSS
+foo
+ a: b
+SASS
+ end
+
+ def test_debug_info_with_compressed
+ assert_equal(<<CSS, render(<<SASS, :debug_info => true, :style => :compressed))
+foo{a:b}
+CSS
+foo
+ a: b
+SASS
+ end
+
+ def test_debug_info_with_line_annotations
+ esc_file_name = Sass::SCSS::RX.escape_ident(Haml::Util.scope("test_debug_info_with_line_annotations_inline.sass"))
+
+ assert_equal(<<CSS, render(<<SASS, :debug_info => true, :line_comments => true))
+@media -sass-debug-info{filename{font-family:file\\:\\/\\/#{esc_file_name}}line{font-family:\\000031}}
+foo {
+ a: b; }
+CSS
+foo
+ a: b
+SASS
+ end
+
def test_empty_first_line
assert_equal("#a {\n b: c; }\n", render("#a\n\n b: c"))
end
def test_escaped_rule
@@ -425,53 +832,162 @@
def test_cr_newline
assert_equal("foo {\n a: b;\n c: d;\n e: f; }\n", render("foo\r a: b\r\n c: d\n\r e: f"))
end
- def test_or_eq
- assert_equal("foo {\n a: b; }\n", render(%Q{!foo = "b"\n!foo ||= "c"\nfoo\n a = !foo}))
- assert_equal("foo {\n a: b; }\n", render(%Q{!foo ||= "b"\nfoo\n a = !foo}))
+ def test_property_with_content_and_nested_props
+ assert_equal(<<CSS, render(<<SASS))
+foo {
+ a: b;
+ a-c: d;
+ a-c-e: f; }
+CSS
+foo
+ a: b
+ c: d
+ e: f
+SASS
+
+ assert_equal(<<CSS, render(<<SASS))
+foo {
+ a: b;
+ a-c-e: f; }
+CSS
+foo
+ a: b
+ c:
+ e: f
+SASS
end
+
+ def test_equals_warning_for_properties
+ assert_warning(<<WARN) {assert_equal(<<CSS, render(<<SASS))}
+DEPRECATION WARNING:
+On line 3, character 3 of 'test_equals_warning_for_properties_inline.sass'
+Setting properties with = has been deprecated and will be removed in version 3.2.
+Use "a: $var" instead.
+
+You can use `sass-convert --in-place --from sass2 file.sass' to convert files automatically.
+WARN
+foo {
+ a: 2px 3px; }
+CSS
+$var: 2px 3px
+foo
+ a = $var
+SASS
+ end
+
+ def test_equals_warning_for_dynamic_properties
+ assert_warning(<<WARN) {assert_equal(<<CSS, render(<<SASS))}
+DEPRECATION WARNING:
+On line 4, character 3 of 'test_equals_warning_for_dynamic_properties_inline.sass'
+Setting properties with = has been deprecated and will be removed in version 3.2.
+Use "a-\#{$i}: $var" instead.
+
+You can use `sass-convert --in-place --from sass2 file.sass' to convert files automatically.
+WARN
+foo {
+ a-12: 2px 3px; }
+CSS
+$var: 2px 3px
+$i: 12
+foo
+ a-\#{$i} = $var
+SASS
+ end
+
+ def test_equals_warning_for_property_with_string
+ assert_warning(<<WARN) {assert_equal(<<CSS, render(<<SASS))}
+DEPRECATION WARNING:
+On line 2, character 3 of 'test_equals_warning_for_property_with_string_inline.sass'
+Setting properties with = has been deprecated and will be removed in version 3.2.
+Use "a: foo" instead.
+
+You can use `sass-convert --in-place --from sass2 file.sass' to convert files automatically.
+WARN
+foo {
+ a: foo; }
+CSS
+foo
+ a = "foo"
+SASS
+ end
+
+ def test_equals_warning_for_property_with_division
+ assert_warning(<<WARN) {assert_equal(<<CSS, render(<<SASS))}
+DEPRECATION WARNING:
+On line 2, character 3 of 'test_equals_warning_for_property_with_division_inline.sass'
+Setting properties with = has been deprecated and will be removed in version 3.2.
+Use "a: (1px / 2px)" instead.
+
+You can use `sass-convert --in-place --from sass2 file.sass' to convert files automatically.
+WARN
+foo {
+ a: 0.5; }
+CSS
+foo
+ a = 1px/2px
+SASS
+ end
+
+ def test_guarded_assign
+ assert_equal("foo {\n a: b; }\n", render(%Q{$foo: b\n$foo: c !default\nfoo\n a: $foo}))
+ assert_equal("foo {\n a: b; }\n", render(%Q{$foo: b !default\nfoo\n a: $foo}))
+ end
def test_mixins
renders_correctly "mixins", { :style => :expanded }
end
+ def test_directive_style_mixins
+ assert_equal <<CSS, render(<<SASS)
+bar {
+ prop: baz; }
+CSS
+@mixin foo($arg)
+ prop: $arg
+
+bar
+ @include foo(baz)
+SASS
+ end
+
def test_mixins_dont_interfere_with_sibling_combinator
assert_equal("foo + bar {\n a: b; }\nfoo + baz {\n c: d; }\n",
render("foo\n +\n bar\n a: b\n baz\n c: d"))
end
def test_mixin_args
assert_equal("blat {\n baz: hi; }\n", render(<<SASS))
-=foo(!bar)
- baz = !bar
+=foo($bar)
+ baz: $bar
blat
- +foo(\"hi\")
+ +foo(hi)
SASS
assert_equal("blat {\n baz: 3; }\n", render(<<SASS))
-=foo(!a, !b)
- baz = !a + !b
+=foo($a, $b)
+ baz: $a + $b
blat
+foo(1, 2)
SASS
assert_equal("blat {\n baz: 4;\n baz: 3;\n baz: 5;\n bang: 3; }\n", render(<<SASS))
-=foo(!c = (6 + 4) / 2)
- baz = !c
-!c = 3
+=foo($c: (6 + 4) / 2)
+ baz: $c
+$c: 3
blat
- +foo(!c + 1)
- +foo((!c + 3)/2)
+ +foo($c + 1)
+ +foo(($c + 3)/2)
+foo
- bang = !c
+ bang: $c
SASS
end
def test_default_values_for_mixin_arguments
assert_equal("white {\n color: white; }\n\nblack {\n color: black; }\n", render(<<SASS))
-=foo(!a = #FFF)
- :color= !a
+=foo($a: #FFF)
+ :color $a
white
+foo
black
+foo(#000)
SASS
@@ -489,58 +1005,122 @@
three {
color: white;
padding: 2px;
margin: 3px; }
CSS
-!a = 5px
-=foo(!a, !b = 1px, !c = 3px + !b)
- :color= !a
- :padding= !b
- :margin= !c
+$a: 5px
+=foo($a, $b: 1px, $c: 3px + $b)
+ :color $a
+ :padding $b
+ :margin $c
one
+foo(#fff)
two
+foo(#fff, 2px)
three
+foo(#fff, 2px, 3px)
SASS
end
+ def test_hyphen_underscore_insensitive_mixins
+ assert_equal(<<CSS, render(<<SASS))
+a {
+ b: 12;
+ c: foo; }
+CSS
+=mixin-hyphen
+ b: 12
+
+=mixin_under
+ c: foo
+
+a
+ +mixin_hyphen
+ +mixin-under
+SASS
+ end
+
+ def test_equals_warning_for_mixin_args
+ assert_warning(<<WARN) {assert_equal(<<CSS, render(<<SASS))}
+DEPRECATION WARNING:
+On line 1, character 10 of 'test_equals_warning_for_mixin_args_inline.sass'
+Setting mixin argument defaults with = has been deprecated and will be removed in version 3.2.
+Use "$arg: 1px" instead.
+
+You can use `sass-convert --in-place --from sass2 file.sass' to convert files automatically.
+WARN
+bar {
+ a: 1px; }
+CSS
+=foo($arg = 1px)
+ a: $arg
+
+bar
+ +foo
+SASS
+ end
+
+ def test_css_identifier_mixin
+ assert_equal(<<CSS, render(<<SASS))
+a {
+ foo: 12; }
+CSS
+=\\{foo\\(12\\)($a)
+ foo: $a
+
+a
+ +\\{foo\\(12\\)(12)
+SASS
+ end
+
def test_interpolation
assert_equal("a-1 {\n b-2-3: c-3; }\n", render(<<SASS))
-!a = 1
-!b = 2
-!c = 3
-a-\#{!a}
- b-\#{!b}-\#{!c}: c-\#{!a + !b}
+$a: 1
+$b: 2
+$c: 3
+a-\#{$a}
+ b-\#{$b}-\#{$c}: c-\#{$a + $b}
SASS
end
+ def test_complex_property_interpolation
+ assert_equal(<<CSS, render(<<SASS))
+a-1 {
+ b-2 3-fizzap18: c-3; }
+CSS
+$a: 1
+$b: 2
+$c: 3
+a-\#{$a}
+ b-\#{$b $c}-\#{fizzap + ($c + 15)}: c-\#{$a + $b}
+SASS
+ end
+
def test_if_directive
assert_equal("a {\n b: 1; }\n", render(<<SASS))
-!var = true
+$var: true
a
- @if !var
+ @if $var
b: 1
- @if not !var
+ @if not $var
b: 2
SASS
end
def test_for
assert_equal(<<CSS, render(<<SASS))
a-0 {
- 2i: 0; }
+ two-i: 0; }
a-1 {
- 2i: 2; }
+ two-i: 2; }
a-2 {
- 2i: 4; }
+ two-i: 4; }
a-3 {
- 2i: 6; }
+ two-i: 6; }
b-1 {
j-1: 0; }
b-2 {
@@ -550,21 +1130,45 @@
j-1: 2; }
b-4 {
j-1: 3; }
CSS
-!a = 3
-@for !i from 0 to !a + 1
- a-\#{!i}
- 2i = 2 * !i
+$a: 3
+@for $i from 0 to $a + 1
+ a-\#{$i}
+ two-i: 2 * $i
-@for !j from 1 through 4
- b-\#{!j}
- j-1 = !j - 1
+@for $j from 1 through 4
+ b-\#{$j}
+ j-1: $j - 1
SASS
end
+ def test_for_with_bang_var
+ assert_warning(<<WARN) {assert_equal(<<CSS, render(<<SASS))}
+DEPRECATION WARNING:
+On line 1, character 6 of 'test_for_with_bang_var_inline.sass'
+Variables with ! have been deprecated and will be removed in version 3.2.
+Use "$bar" instead.
+
+You can use `sass-convert --in-place --from sass2 file.sass' to convert files automatically.
+WARN
+a-0 {
+ b: c; }
+
+a-1 {
+ b: c; }
+
+a-2 {
+ b: c; }
+CSS
+@for !bar from 0 to 3
+ a-\#{$bar}
+ b: c
+SASS
+ end
+
def test_while
assert_equal(<<CSS, render(<<SASS))
a-5 {
blooble: gloop; }
@@ -578,15 +1182,15 @@
blooble: gloop; }
a-1 {
blooble: gloop; }
CSS
-!a = 5
-@while !a != 0
- a-\#{!a}
+$a: 5
+@while $a != 0
+ a-\#{$a}
blooble: gloop
- !a = !a - 1
+ $a: $a - 1
SASS
end
def test_else
assert_equal(<<CSS, render(<<SASS))
@@ -632,45 +1236,163 @@
assert_equal(<<CSS, render(<<SASS))
a {
b: 1;
c: 2; }
CSS
-!a = 1
+$a: 1
a
- b = !a
- !a = 2
- c = !a
+ b: $a
+ $a: 2
+ c: $a
SASS
end
+ def test_bang_variables
+ assert_warning(<<WARN) {assert_equal(<<CSS, render(<<SASS))}
+DEPRECATION WARNING:
+On line 1, character 1 of 'test_bang_variables_inline.sass'
+Variables with ! have been deprecated and will be removed in version 3.2.
+Use "$bang-var" instead.
+
+You can use `sass-convert --in-place --from sass2 file.sass' to convert files automatically.
+WARN
+foo {
+ a: 1px; }
+CSS
+!bang-var: 1px
+foo
+ a: $bang-var
+SASS
+
+ assert_warning(<<WARN) {assert_equal(<<CSS, render(<<SASS))}
+DEPRECATION WARNING:
+On line 3, character 6 of 'test_bang_variables_inline.sass'
+Variables with ! have been deprecated and will be removed in version 3.2.
+Use "$dollar-var" instead.
+
+You can use `sass-convert --in-place --from sass2 file.sass' to convert files automatically.
+WARN
+foo {
+ a: 1px; }
+CSS
+$dollar-var: 1px
+foo
+ a: !dollar-var
+SASS
+ end
+
+ def test_equals_warning_for_variables
+ assert_warning(<<WARN) {assert_equal(<<CSS, render(<<SASS))}
+DEPRECATION WARNING:
+On line 2, character 1 of 'test_equals_warning_for_variables_inline.sass'
+Setting variables with = has been deprecated and will be removed in version 3.2.
+Use "$equals-var: 2px 3px" instead.
+
+You can use `sass-convert --in-place --from sass2 file.sass' to convert files automatically.
+WARN
+foo {
+ a: 2px 3px; }
+CSS
+
+$equals-var = 2px 3px
+foo
+ a: $equals-var
+SASS
+ end
+
+ def test_equals_warning_for_guarded_variables
+ assert_warning(<<WARN) {assert_equal(<<CSS, render(<<SASS))}
+DEPRECATION WARNING:
+On line 2, character 1 of 'test_equals_warning_for_guarded_variables_inline.sass'
+Setting variable defaults with ||= has been deprecated and will be removed in version 3.2.
+Use "$equals-var: 2px 3px !default" instead.
+
+You can use `sass-convert --in-place --from sass2 file.sass' to convert files automatically.
+WARN
+foo {
+ a: 2px 3px; }
+CSS
+
+$equals-var ||= 2px 3px
+foo
+ a: $equals-var
+SASS
+ end
+
def test_variable_scope
assert_equal(<<CSS, render(<<SASS))
a {
b-1: c;
b-2: c;
d: 12; }
b {
d: 17; }
CSS
-!i = 12
+$i: 12
a
- @for !i from 1 through 2
- b-\#{!i}: c
- d = !i
+ @for $i from 1 through 2
+ b-\#{$i}: c
+ d: $i
=foo
- !i = 17
+ $i: 17
b
+foo
- d = !i
+ d: $i
SASS
end
+ def test_hyphen_underscore_insensitive_variables
+ assert_equal(<<CSS, render(<<SASS))
+a {
+ b: c; }
+
+d {
+ e: 13;
+ f: foobar; }
+CSS
+$var-hyphen: 12
+$var_under: foo
+
+a
+ $var_hyphen: 1 + $var_hyphen
+ $var-under: $var-under + bar
+ b: c
+
+d
+ e: $var-hyphen
+ f: $var_under
+SASS
+ end
+
+ def test_css_identifier_variable
+ assert_equal(<<CSS, render(<<SASS))
+a {
+ b: 12; }
+CSS
+$\\{foo\\(12\\): 12
+
+a
+ b: $\\{foo\\(12\\)
+SASS
+ end
+
+ def test_important
+ assert_equal(<<CSS, render(<<SASS))
+a {
+ b: 12px !important; }
+CSS
+$foo: 12px
+a
+ b: $foo !important
+SASS
+ end
+
def test_argument_error
- assert_raise(Sass::SyntaxError) { render("a\n b = hsl(1)") }
+ assert_raise(Sass::SyntaxError) { render("a\n b: hsl(1)") }
end
def test_comments_at_the_top_of_a_document
render(<<SASS)
//
@@ -695,10 +1417,34 @@
foo {
bar: baz; }
CSS
end
+ def test_loud_comments_with_starred_lines
+ assert_equal(<<CSS, render(<<SASS))
+/* This is a comment that
+ * continues to the second line.
+ * And even to the third! */
+CSS
+/* This is a comment that
+ * continues to the second line.
+ * And even to the third!
+SASS
+ end
+
+ def test_loud_comments_with_no_space_after_starred_lines
+ assert_equal(<<CSS, render(<<SASS))
+/*bip bop
+ *beep boop
+ *bap blimp */
+CSS
+/*bip bop
+ *beep boop
+ *bap blimp
+SASS
+ end
+
def test_comment_indentation_at_beginning_of_doc
assert_equal <<CSS, render(<<SASS)
/* foo
* bar
* baz */
@@ -725,13 +1471,39 @@
bar
baz
SASS
end
+ def test_loud_comment_with_close
+ assert_equal <<CSS, render(<<SASS)
+foo {
+ /* foo
+ * bar */ }
+CSS
+foo
+ /* foo
+ bar */
+SASS
+ end
+
+ def test_loud_comment_with_separate_line_close
+ assert_equal <<CSS, render(<<SASS)
+foo {
+ /* foo
+ * bar
+ */ }
+CSS
+foo
+ /* foo
+ * bar
+ */
+SASS
+ end
+
def test_attribute_selector_with_spaces
assert_equal(<<CSS, render(<<SASS))
-a b[foo = bar] {
+a b[foo=bar] {
c: d; }
CSS
a
b[foo = bar]
c: d
@@ -772,30 +1544,12 @@
end
def test_empty_selector_warning
assert_warning(<<END) {render("foo bar")}
WARNING on line 1 of test_empty_selector_warning_inline.sass:
-Selector "foo bar" doesn't have any properties and will not be rendered.
+This selector doesn't have any properties and will not be rendered.
END
-
- assert_warning(<<END) {render(<<SASS)}
-WARNING on line 3 of test_empty_selector_warning_inline.sass:
-Selector
- foo, bar, baz,
- bang, bip, bop
-doesn't have any properties and will not be rendered.
-END
-
-
-foo, bar, baz,
-bang, bip, bop
-SASS
-
- assert_warning(<<END) {render("foo bar", :filename => nil)}
-WARNING on line 1:
-Selector "foo bar" doesn't have any properties and will not be rendered.
-END
end
def test_root_level_pseudo_class_with_new_properties
assert_equal(<<CSS, render(<<SASS, :property_syntax => :new))
:focus {
@@ -825,21 +1579,371 @@
foo
a: b
SASS
end
+ def test_interpolation_in_raw_functions
+ assert_equal(<<CSS, render(<<SASS))
+foo {
+ filter: progid:Microsoft.foo.bar.Baz(flip=foobar, bang=#00ff00cc); }
+CSS
+foo
+ filter: progid:Microsoft.foo.bar.Baz(flip=\#{foo + bar}, bang=#00ff00cc)
+SASS
+ end
+
+ # SassScript string behavior
+
+ def test_plus_preserves_quotedness
+ assert_equal(<<CSS, render(<<SASS))
+foo {
+ a: "foo1";
+ b: "1foo";
+ c: foo1;
+ d: 1foo;
+ e: "foobar";
+ f: foobar; }
+CSS
+foo
+ a: "foo" + 1
+ b: 1 + "foo"
+ c: foo + 1
+ d: 1 + foo
+ e: "foo" + bar
+ f: foo + "bar"
+SASS
+ end
+
+ def test_colon_properties_preserve_quotedness
+ assert_equal(<<CSS, render(<<SASS))
+foo {
+ a: "foo";
+ b: bar;
+ c: "foo" bar;
+ d: foo, "bar"; }
+CSS
+foo
+ a: "foo"
+ b: bar
+ c: "foo" bar
+ d: foo, "bar"
+SASS
+ end
+
+ def test_colon_variables_preserve_quotedness
+ assert_equal(<<CSS, render(<<SASS))
+foo {
+ a: "foo";
+ b: bar; }
+CSS
+$a: "foo"
+$b: bar
+
+foo
+ a: $a
+ b: $b
+SASS
+ end
+
+ def test_colon_args_preserve_quotedness
+ assert_equal(<<CSS, render(<<SASS))
+foo {
+ a: "foo";
+ b: bar;
+ c: "foo" bar;
+ d: foo, "bar"; }
+CSS
+=foo($a: "foo", $b: bar, $c: "foo" bar, $d: (foo, "bar"))
+ foo
+ a: $a
+ b: $b
+ c: $c
+ d: $d
+
++foo
+SASS
+ end
+
+ def test_interpolation_unquotes_strings
+ assert_equal(<<CSS, render(<<SASS))
+.foo-bar {
+ a: b; }
+CSS
+.foo-\#{"bar"}
+ a: b
+SASS
+
+ assert_equal(<<CSS, render(<<SASS))
+.foo {
+ a: b c; }
+CSS
+.foo
+ a: b \#{"c"}
+SASS
+ end
+
+ def test_interpolation_unquotes_strings_in_vars
+ assert_equal(<<CSS, render(<<SASS))
+.foo-bar {
+ a: b; }
+CSS
+$var: "bar"
+
+.foo-\#{$var}
+ a: b
+SASS
+ end
+
+ def test_interpolation_doesnt_deep_unquote_strings
+ assert_equal(<<CSS, render(<<SASS))
+.foo- "bar" "baz" {
+ a: b; }
+CSS
+.foo-\#{"bar" "baz"}
+ a: b
+SASS
+ end
+
+ # Deprecated equals behavior
+
+ def test_equals_properties_unquote_strings
+ silence_warnings do
+ assert_equal(<<CSS, render(<<SASS))
+foo {
+ a: foo;
+ b: bar;
+ c: foo bar;
+ d: foo, bar baz;
+ e: foo bar, bar; }
+CSS
+foo
+ a= "foo"
+ b= bar
+ c= "foo" bar
+ d= foo, "bar baz"
+ e= "foo bar", bar
+SASS
+ end
+ end
+
+ def test_equals_properties_unquote_value
+ silence_warnings do
+ assert_equal(<<CSS, render(<<SASS))
+foo {
+ a: foo; }
+CSS
+$var: "foo"
+
+foo
+ a= $var
+SASS
+ end
+ end
+
+ def test_equals_properties_deep_unquote_vars
+ silence_warnings do
+ assert_equal(<<CSS, render(<<SASS))
+foo {
+ a: foo bar;
+ b: bar foo; }
+CSS
+$var: "foo"
+
+foo
+ a= $var "bar"
+ b= "bar" $var
+SASS
+ end
+ end
+
+ def test_equals_vars_unquote_strings
+ silence_warnings do
+ assert_equal(<<CSS, render(<<SASS))
+foo {
+ a: foo;
+ b: bar;
+ c: foo bar;
+ d: foo, bar; }
+CSS
+$a = "foo"
+$b = bar
+$c = "foo" bar
+$d = foo, "bar"
+
+foo
+ a: $a
+ b: $b
+ c: $c
+ d: $d
+SASS
+ end
+ end
+
+ def test_equals_vars_unquote_value
+ silence_warnings do
+ assert_equal(<<CSS, render(<<SASS))
+foo {
+ a: foo; }
+CSS
+$var1: "foo"
+$var2 = $var1
+
+foo
+ a: $var2
+SASS
+ end
+ end
+
+ def test_equals_vars_deep_unquote_vars
+ silence_warnings do
+ assert_equal(<<CSS, render(<<SASS))
+foo {
+ a: foo bar;
+ b: bar foo; }
+CSS
+$var: "foo"
+$a = $var "bar"
+$b = "bar" $var
+
+foo
+ a: $a
+ b: $b
+SASS
+ end
+ end
+
+ def test_equals_args_unquote_strings
+ silence_warnings do
+ assert_equal(<<CSS, render(<<SASS))
+foo {
+ a: foo;
+ b: bar;
+ c: foo bar;
+ d: foo, bar; }
+CSS
+=foo($a = "foo", $b = bar, $c = "foo" bar, $d = (foo, "bar"))
+ foo
+ a: $a
+ b: $b
+ c: $c
+ d: $d
+
++foo
+SASS
+ end
+ end
+
+ def test_equals_args_unquote_value
+ silence_warnings do
+ assert_equal(<<CSS, render(<<SASS))
+foo {
+ a: foo; }
+CSS
+$var1: "foo"
+
+=foo($var2 = $var1)
+ foo
+ a: $var2
+
++foo
+SASS
+ end
+ end
+
+ def test_equals_args_deep_unquote_vars
+ silence_warnings do
+ assert_equal(<<CSS, render(<<SASS))
+foo {
+ a: foo bar;
+ b: bar foo; }
+CSS
+$var: "foo"
+=foo($a = $var "bar", $b = "bar" $var)
+ foo
+ a: $a
+ b: $b
+
++foo
+SASS
+ end
+ end
+
+ def test_equals_properties_force_division
+ silence_warnings do
+ assert_equal(<<CSS, render(<<SASS))
+foo {
+ a: 0.5; }
+CSS
+foo
+ a = 1px/2px
+SASS
+ end
+ end
+
+ def test_warn_directive
+ expected_warning = <<EXPECTATION
+WARNING: this is a warning
+ on line 4 of test_warn_directive_inline.sass
+
+WARNING: this is a mixin warning
+ on line 2 of test_warn_directive_inline.sass, in `foo'
+ from line 7 of test_warn_directive_inline.sass
+EXPECTATION
+ assert_warning expected_warning do
+ assert_equal <<CSS, render(<<SASS)
+bar {
+ c: d; }
+CSS
+=foo
+ @warn "this is a mixin warning"
+
+@warn "this is a warning"
+bar
+ c: d
+ +foo
+SASS
+ end
+ end
+
+ def test_warn_directive_when_quiet
+ assert_warning "" do
+ assert_equal <<CSS, render(<<SASS, :quiet => true)
+CSS
+@warn "this is a warning"
+SASS
+ end
+ end
+
+ def test_warn_with_imports
+ expected_warning = <<WARN
+WARNING: In the main file
+ on line 1 of #{File.dirname(__FILE__)}/templates/warn.sass
+
+WARNING: Imported
+ on line 1 of #{File.dirname(__FILE__)}/templates/warn_imported.sass
+ from line 2 of #{File.dirname(__FILE__)}/templates/warn.sass
+
+WARNING: In an imported mixin
+ on line 4 of #{File.dirname(__FILE__)}/templates/warn_imported.sass, in `emits-a-warning'
+ from line 3 of #{File.dirname(__FILE__)}/templates/warn.sass
+WARN
+ assert_warning expected_warning do
+ renders_correctly "warn", :style => :compact, :load_paths => [File.dirname(__FILE__) + "/templates"]
+ end
+ end
+
# Regression tests
def test_parens_in_mixins
assert_equal(<<CSS, render(<<SASS))
.foo {
color: #01ff7f;
background-color: #000102; }
CSS
-=foo(!c1, !c2 = rgb(0, 1, 2))
- color = !c1
- background-color = !c2
+=foo($c1, $c2: rgb(0, 1, 2))
+ color: $c1
+ background-color: $c2
.foo
+foo(rgb(1,255,127))
SASS
end
@@ -860,11 +1964,11 @@
/* :color black */
border-style: solid; }
RESULT
.box
:border
- /*:color black
+ /* :color black
:style solid
SOURCE
assert_equal(<<RESULT, render(<<SOURCE, :style => :compressed))
.box{border-style:solid}
@@ -940,12 +2044,195 @@
/*
b: c
SASS
end
+ def test_options_available_in_environment
+ assert_equal(<<CSS, render(<<SASS))
+a {
+ b: nested; }
+CSS
+a
+ b: option("style")
+SASS
+ end
+
+ def test_mixin_no_arg_error
+ assert_raise_message(Sass::SyntaxError, 'Invalid CSS after "($bar,": expected variable (e.g. $foo), was ")"') do
+ render(<<SASS)
+=foo($bar,)
+ bip: bap
+SASS
+ end
+ end
+
+ def test_import_with_commas_in_url
+ assert_equal <<CSS, render(<<SASS)
+@import url(foo.css?bar,baz);
+CSS
+@import url(foo.css?bar,baz)
+SASS
+ end
+
+ def test_function_output_with_comma
+ assert_equal <<CSS, render(<<SASS)
+foo {
+ a: b(c), d(e); }
+CSS
+foo
+ a: b(c), d(e)
+SASS
+ end
+
+ def test_interpolation_with_comma
+ assert_equal <<CSS, render(<<SASS)
+foo {
+ a: foo, bar; }
+CSS
+$foo: foo
+foo
+ a: \#{$foo}, bar
+SASS
+ end
+
+ def test_string_interpolation_with_comma
+ assert_equal <<CSS, render(<<SASS)
+foo {
+ a: "bip foo bap", bar; }
+CSS
+$foo: foo
+foo
+ a: "bip \#{$foo} bap", bar
+SASS
+ end
+
+ # Encodings
+
+ unless Haml::Util.ruby1_8?
+ def test_encoding_error
+ render("foo\nbar\nb\xFEaz".force_encoding("utf-8"))
+ assert(false, "Expected exception")
+ rescue Sass::SyntaxError => e
+ assert_equal(3, e.sass_line)
+ assert_equal('Invalid UTF-8 character "\xFE"', e.message)
+ end
+
+ def test_ascii_incompatible_encoding_error
+ template = "foo\nbar\nb_z".encode("utf-16le")
+ template[9] = "\xFE".force_encoding("utf-16le")
+ render(template)
+ assert(false, "Expected exception")
+ rescue Sass::SyntaxError => e
+ assert_equal(3, e.sass_line)
+ assert_equal('Invalid UTF-16LE character "\xFE"', e.message)
+ end
+
+ def test_same_charset_as_encoding
+ assert_renders_encoded(<<CSS, <<SASS)
+@charset "UTF-8";
+fóó {
+ a: b; }
+CSS
+@charset "utf-8"
+fóó
+ a: b
+SASS
+ end
+
+ def test_different_charset_than_encoding
+ assert_renders_encoded(<<CSS.force_encoding("IBM866"), <<SASS)
+@charset "IBM866";
+fóó {
+ a: b; }
+CSS
+@charset "ibm866"
+fóó
+ a: b
+SASS
+ end
+
+ def test_different_encoding_than_system
+ assert_renders_encoded(<<CSS.encode("IBM866"), <<SASS.encode("IBM866"))
+@charset "IBM866";
+тАЬ {
+ a: b; }
+CSS
+тАЬ
+ a: b
+SASS
+ end
+
+ def test_multibyte_charset
+ assert_renders_encoded(<<CSS.encode("UTF-16LE"), <<SASS.encode("UTF-16LE").force_encoding("UTF-8"))
+@charset "UTF-16LE";
+fóó {
+ a: b; }
+CSS
+@charset "utf-16le"
+fóó
+ a: b
+SASS
+ end
+
+ def test_multibyte_charset_without_endian_specifier
+ assert_renders_encoded(<<CSS.encode("UTF-32BE"), <<SASS.encode("UTF-32BE").force_encoding("UTF-8"))
+@charset "UTF-32BE";
+fóó {
+ a: b; }
+CSS
+@charset "utf-32"
+fóó
+ a: b
+SASS
+ end
+
+ def test_utf8_bom
+ assert_renders_encoded(<<CSS, <<SASS.force_encoding("BINARY"))
+@charset "UTF-8";
+fóó {
+ a: b; }
+CSS
+\uFEFFfóó
+ a: b
+SASS
+ end
+
+ def test_utf16le_bom
+ assert_renders_encoded(<<CSS.encode("UTF-16LE"), <<SASS.encode("UTF-16LE").force_encoding("BINARY"))
+@charset "UTF-16LE";
+fóó {
+ a: b; }
+CSS
+\uFEFFfóó
+ a: b
+SASS
+ end
+
+ def test_utf32be_bom
+ assert_renders_encoded(<<CSS.encode("UTF-32BE"), <<SASS.encode("UTF-32BE").force_encoding("BINARY"))
+@charset "UTF-32BE";
+fóó {
+ a: b; }
+CSS
+\uFEFFfóó
+ a: b
+SASS
+ end
+ end
+
private
-
+
+ def assert_hash_has(hash, expected)
+ expected.each {|k, v| assert_equal(v, hash[k])}
+ end
+
+ def assert_renders_encoded(css, sass)
+ result = render(sass)
+ assert_equal css.encoding, result.encoding
+ assert_equal css, result
+ end
+
def render(sass, options = {})
munge_filename options
Sass::Engine.new(sass, options).render
end
@@ -971,5 +2258,6 @@
def sassc_path(template)
sassc_path = File.join(File.dirname(__FILE__) + "/templates/#{template}.sass")
Sass::Files.send(:sassc_filename, sassc_path, Sass::Engine::DEFAULT_OPTIONS)
end
end
+