PageRenderTime 89ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/training-web/vendor/bundle/gems/sass-3.2.9/test/sass/engine_test.rb

https://bitbucket.org/ohimmelreich/asalia-training
Ruby | 1726 lines | 1718 code | 2 blank | 6 comment | 0 complexity | 99e299d2096cc733cf158484b3f6f14b MD5 | raw file
  1. #!/usr/bin/env ruby
  2. # -*- coding: utf-8 -*-
  3. require File.dirname(__FILE__) + '/../test_helper'
  4. require File.dirname(__FILE__) + '/test_helper'
  5. require 'sass/engine'
  6. require 'stringio'
  7. require 'mock_importer'
  8. require 'pathname'
  9. module Sass::Script::Functions::UserFunctions
  10. def option(name)
  11. Sass::Script::String.new(@options[name.value.to_sym].to_s)
  12. end
  13. end
  14. class SassEngineTest < Test::Unit::TestCase
  15. FAKE_FILE_NAME = __FILE__.gsub(/rb$/,"sass")
  16. # A map of erroneous Sass documents to the error messages they should produce.
  17. # The error messages may be arrays;
  18. # if so, the second element should be the line number that should be reported for the error.
  19. # If this isn't provided, the tests will assume the line number should be the last line of the document.
  20. EXCEPTION_MAP = {
  21. "$a: 1 + " => 'Invalid CSS after "1 +": expected expression (e.g. 1px, bold), was ""',
  22. "$a: 1 + 2 +" => 'Invalid CSS after "1 + 2 +": expected expression (e.g. 1px, bold), was ""',
  23. "$a: 1 + 2 + %" => 'Invalid CSS after "1 + 2 + ": expected expression (e.g. 1px, bold), was "%"',
  24. "$a: foo(\"bar\"" => 'Invalid CSS after "foo("bar"": expected ")", was ""',
  25. "$a: 1 }" => 'Invalid CSS after "1 ": expected expression (e.g. 1px, bold), was "}"',
  26. "$a: 1 }foo\"" => 'Invalid CSS after "1 ": expected expression (e.g. 1px, bold), was "}foo""',
  27. ":" => 'Invalid property: ":".',
  28. ": a" => 'Invalid property: ": a".',
  29. "a\n :b" => <<MSG,
  30. Invalid property: ":b" (no value).
  31. If ":b" should be a selector, use "\\:b" instead.
  32. MSG
  33. "a\n b:" => 'Invalid property: "b:" (no value).',
  34. "a\n :b: c" => 'Invalid property: ":b: c".',
  35. "a\n :b:c d" => 'Invalid property: ":b:c d".',
  36. "a\n :b c;" => 'Invalid CSS after "c": expected expression (e.g. 1px, bold), was ";"',
  37. "a\n b: c;" => 'Invalid CSS after "c": expected expression (e.g. 1px, bold), was ";"',
  38. ".foo ^bar\n a: b" => ['Invalid CSS after ".foo ": expected selector, was "^bar"', 1],
  39. "a\n @extend .foo ^bar" => 'Invalid CSS after ".foo ": expected selector, was "^bar"',
  40. "a\n @extend .foo .bar" => "Can't extend .foo .bar: can't extend nested selectors",
  41. "a\n @extend >" => "Can't extend >: invalid selector",
  42. "a\n @extend &.foo" => "Can't extend &.foo: can't extend parent selectors",
  43. "a: b" => 'Properties are only allowed within rules, directives, mixin includes, or other properties.',
  44. ":a b" => 'Properties are only allowed within rules, directives, mixin includes, or other properties.',
  45. "$" => 'Invalid variable: "$".',
  46. "$a" => 'Invalid variable: "$a".',
  47. "$ a" => 'Invalid variable: "$ a".',
  48. "$a b" => 'Invalid variable: "$a b".',
  49. "$a: 1b + 2c" => "Incompatible units: 'c' and 'b'.",
  50. "$a: 1b < 2c" => "Incompatible units: 'c' and 'b'.",
  51. "$a: 1b > 2c" => "Incompatible units: 'c' and 'b'.",
  52. "$a: 1b <= 2c" => "Incompatible units: 'c' and 'b'.",
  53. "$a: 1b >= 2c" => "Incompatible units: 'c' and 'b'.",
  54. "a\n b: 1b * 2c" => "2b*c isn't a valid CSS value.",
  55. "a\n b: 1b % 2c" => "Cannot modulo by a number with units: 2c.",
  56. "$a: 2px + #ccc" => "Cannot add a number with units (2px) to a color (#cccccc).",
  57. "$a: #ccc + 2px" => "Cannot add a number with units (2px) to a color (#cccccc).",
  58. "& a\n :b c" => ["Base-level rules cannot contain the parent-selector-referencing character '&'.", 1],
  59. "a\n :b\n c" => "Illegal nesting: Only properties may be nested beneath properties.",
  60. "$a: b\n :c d\n" => "Illegal nesting: Nothing may be nested beneath variable declarations.",
  61. "$a: b\n :c d\n" => "Illegal nesting: Nothing may be nested beneath variable declarations.",
  62. "@import templates/basic\n foo" => "Illegal nesting: Nothing may be nested beneath import directives.",
  63. "foo\n @import foo.css" => "CSS import directives may only be used at the root of a document.",
  64. "@if true\n @import foo" => "Import directives may not be used within control directives or mixins.",
  65. "@if true\n .foo\n @import foo" => "Import directives may not be used within control directives or mixins.",
  66. "@mixin foo\n @import foo" => "Import directives may not be used within control directives or mixins.",
  67. "@mixin foo\n .foo\n @import foo" => "Import directives may not be used within control directives or mixins.",
  68. "@import foo;" => "Invalid @import: expected end of line, was \";\".",
  69. '$foo: "bar" "baz" !' => %Q{Invalid CSS after ""bar" "baz" ": expected expression (e.g. 1px, bold), was "!"},
  70. '$foo: "bar" "baz" $' => %Q{Invalid CSS after ""bar" "baz" ": expected expression (e.g. 1px, bold), was "$"}, #'
  71. "=foo\n :color red\n.bar\n +bang" => "Undefined mixin 'bang'.",
  72. "=foo\n :color red\n.bar\n +bang_bop" => "Undefined mixin 'bang_bop'.",
  73. "=foo\n :color red\n.bar\n +bang-bop" => "Undefined mixin 'bang-bop'.",
  74. ".foo\n =foo\n :color red\n.bar\n +foo" => "Undefined mixin 'foo'.",
  75. " a\n b: c" => ["Indenting at the beginning of the document is illegal.", 1],
  76. " \n \n\t\n a\n b: c" => ["Indenting at the beginning of the document is illegal.", 4],
  77. "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],
  78. "a\n b: c\na\n b: c" => ["Inconsistent indentation: 1 space was used for indentation, but the rest of the document was indented using 2 spaces.", 4],
  79. "a\n\t\tb: c\n\tb: c" => ["Inconsistent indentation: 1 tab was used for indentation, but the rest of the document was indented using 2 tabs.", 3],
  80. "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],
  81. "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],
  82. "a\n b: c\na\n d: e" => ["The line was indented 2 levels deeper than the previous line.", 4],
  83. "a\n b: c\n a\n d: e" => ["The line was indented 3 levels deeper than the previous line.", 4],
  84. "a\n \tb: c" => ["Indentation can't use both tabs and spaces.", 2],
  85. "=a(" => 'Invalid CSS after "(": expected variable (e.g. $foo), was ""',
  86. "=a(b)" => 'Invalid CSS after "(": expected variable (e.g. $foo), was "b)"',
  87. "=a(,)" => 'Invalid CSS after "(": expected variable (e.g. $foo), was ",)"',
  88. "=a($)" => 'Invalid CSS after "(": expected variable (e.g. $foo), was "$)"',
  89. "=a($foo bar)" => 'Invalid CSS after "($foo ": expected ")", was "bar)"',
  90. "=foo\n bar: baz\n+foo" => ["Properties are only allowed within rules, directives, mixin includes, or other properties.", 2],
  91. "a-\#{$b\n c: d" => ['Invalid CSS after "a-#{$b": expected "}", was ""', 1],
  92. "=a($b: 1, $c)" => "Required argument $c must come before any optional arguments.",
  93. "=a($b: 1)\n a: $b\ndiv\n +a(1,2)" => "Mixin a takes 1 argument but 2 were passed.",
  94. "=a($b: 1)\n a: $b\ndiv\n +a(1,$c: 3)" => "Mixin a doesn't have an argument named $c.",
  95. "=a($b)\n a: $b\ndiv\n +a" => "Mixin a is missing argument $b.",
  96. "@function foo()\n 1 + 2" => "Functions can only contain variable declarations and control directives.",
  97. "@function foo()\n foo: bar" => "Functions can only contain variable declarations and control directives.",
  98. "@function foo()\n foo: bar\n @return 3" => ["Functions can only contain variable declarations and control directives.", 2],
  99. "@function foo\n @return 1" => ['Invalid CSS after "": expected "(", was ""', 1],
  100. "@function foo(\n @return 1" => ['Invalid CSS after "(": expected variable (e.g. $foo), was ""', 1],
  101. "@function foo(b)\n @return 1" => ['Invalid CSS after "(": expected variable (e.g. $foo), was "b)"', 1],
  102. "@function foo(,)\n @return 1" => ['Invalid CSS after "(": expected variable (e.g. $foo), was ",)"', 1],
  103. "@function foo($)\n @return 1" => ['Invalid CSS after "(": expected variable (e.g. $foo), was "$)"', 1],
  104. "@function foo()\n @return" => 'Invalid @return: expected expression.',
  105. "@function foo()\n @return 1\n $var: val" => 'Illegal nesting: Nothing may be nested beneath return directives.',
  106. "@function foo($a)\n @return 1\na\n b: foo()" => 'Function foo is missing argument $a.',
  107. "@function foo()\n @return 1\na\n b: foo(2)" => 'Function foo takes 0 arguments but 1 was passed.',
  108. "@function foo()\n @return 1\na\n b: foo($a: 1)" => "Function foo doesn't have an argument named $a.",
  109. "@function foo()\n @return 1\na\n b: foo($a: 1, $b: 2)" => "Function foo doesn't have the following arguments: $a, $b.",
  110. "@return 1" => '@return may only be used within a function.',
  111. "@if true\n @return 1" => '@return may only be used within a function.',
  112. "@mixin foo\n @return 1\n@include foo" => ['@return may only be used within a function.', 2],
  113. "@else\n a\n b: c" => ["@else must come after @if.", 1],
  114. "@if false\n@else foo" => "Invalid else directive '@else foo': expected 'if <expr>'.",
  115. "@if false\n@else if " => "Invalid else directive '@else if': expected 'if <expr>'.",
  116. "a\n $b: 12\nc\n d: $b" => 'Undefined variable: "$b".',
  117. "=foo\n $b: 12\nc\n +foo\n d: $b" => 'Undefined variable: "$b".',
  118. "c\n d: $b-foo" => 'Undefined variable: "$b-foo".',
  119. "c\n d: $b_foo" => 'Undefined variable: "$b_foo".',
  120. '@for $a from "foo" to 1' => '"foo" is not an integer.',
  121. '@for $a from 1 to "2"' => '"2" is not an integer.',
  122. '@for $a from 1 to "foo"' => '"foo" is not an integer.',
  123. '@for $a from 1 to 1.232323' => '1.23232 is not an integer.',
  124. '@for $a from 1px to 3em' => "Incompatible units: 'em' and 'px'.",
  125. '@if' => "Invalid if directive '@if': expected expression.",
  126. '@while' => "Invalid while directive '@while': expected expression.",
  127. '@debug' => "Invalid debug directive '@debug': expected expression.",
  128. %Q{@debug "a message"\n "nested message"} => "Illegal nesting: Nothing may be nested beneath debug directives.",
  129. '@warn' => "Invalid warn directive '@warn': expected expression.",
  130. %Q{@warn "a message"\n "nested message"} => "Illegal nesting: Nothing may be nested beneath warn directives.",
  131. "/* foo\n bar\n baz" => "Inconsistent indentation: previous line was indented by 4 spaces, but this line was indented by 2 spaces.",
  132. '+foo(1 + 1: 2)' => 'Invalid CSS after "(1 + 1": expected comma, was ": 2)"',
  133. '+foo($var: )' => 'Invalid CSS after "($var: ": expected mixin argument, was ")"',
  134. '+foo($var: a, $var: b)' => 'Keyword argument "$var" passed more than once',
  135. '+foo($var-var: a, $var_var: b)' => 'Keyword argument "$var_var" passed more than once',
  136. '+foo($var_var: a, $var-var: b)' => 'Keyword argument "$var-var" passed more than once',
  137. "a\n b: foo(1 + 1: 2)" => 'Invalid CSS after "foo(1 + 1": expected comma, was ": 2)"',
  138. "a\n b: foo($var: )" => 'Invalid CSS after "foo($var: ": expected function argument, was ")"',
  139. "a\n b: foo($var: a, $var: b)" => 'Keyword argument "$var" passed more than once',
  140. "a\n b: foo($var-var: a, $var_var: b)" => 'Keyword argument "$var_var" passed more than once',
  141. "a\n b: foo($var_var: a, $var-var: b)" => 'Keyword argument "$var-var" passed more than once',
  142. "@if foo\n @extend .bar" => ["Extend directives may only be used within rules.", 2],
  143. "$var: true\n@while $var\n @extend .bar\n $var: false" => ["Extend directives may only be used within rules.", 3],
  144. "@for $i from 0 to 1\n @extend .bar" => ["Extend directives may only be used within rules.", 2],
  145. "@mixin foo\n @extend .bar\n@include foo" => ["Extend directives may only be used within rules.", 2],
  146. "foo\n &a\n b: c" => ["Invalid CSS after \"&\": expected \"{\", was \"a\"\n\n\"a\" may only be used at the beginning of a compound selector.", 2],
  147. "foo\n &1\n b: c" => ["Invalid CSS after \"&\": expected \"{\", was \"1\"\n\n\"1\" may only be used at the beginning of a compound selector.", 2],
  148. "foo %\n a: b" => ['Invalid CSS after "foo %": expected placeholder name, was ""', 1],
  149. "=foo\n @content error" => "Invalid content directive. Trailing characters found: \"error\".",
  150. "=foo\n @content\n b: c" => "Illegal nesting: Nothing may be nested beneath @content directives.",
  151. "@content" => '@content may only be used within a mixin.',
  152. "=simple\n .simple\n color: red\n+simple\n color: blue" => ['Mixin "simple" does not accept a content block.', 4],
  153. "@import \"foo\" // bar" => "Invalid CSS after \"\"foo\" \": expected media query list, was \"// bar\"",
  154. # Regression tests
  155. "a\n b:\n c\n d" => ["Illegal nesting: Only properties may be nested beneath properties.", 3],
  156. "& foo\n bar: baz\n blat: bang" => ["Base-level rules cannot contain the parent-selector-referencing character '&'.", 1],
  157. "a\n b: c\n& foo\n bar: baz\n blat: bang" => ["Base-level rules cannot contain the parent-selector-referencing character '&'.", 3],
  158. }
  159. def teardown
  160. clean_up_sassc
  161. end
  162. def test_basic_render
  163. renders_correctly "basic", { :style => :compact }
  164. end
  165. def test_empty_render
  166. assert_equal "", render("")
  167. end
  168. def test_multiple_calls_to_render
  169. sass = Sass::Engine.new("a\n b: c")
  170. assert_equal sass.render, sass.render
  171. end
  172. def test_alternate_styles
  173. renders_correctly "expanded", { :style => :expanded }
  174. renders_correctly "compact", { :style => :compact }
  175. renders_correctly "nested", { :style => :nested }
  176. renders_correctly "compressed", { :style => :compressed }
  177. end
  178. def test_compile
  179. assert_equal "div { hello: world; }\n", Sass.compile("$who: world\ndiv\n hello: $who", :syntax => :sass, :style => :compact)
  180. assert_equal "div { hello: world; }\n", Sass.compile("$who: world; div { hello: $who }", :style => :compact)
  181. end
  182. def test_compile_file
  183. FileUtils.mkdir_p(absolutize("tmp"))
  184. open(absolutize("tmp/test_compile_file.sass"), "w") {|f| f.write("$who: world\ndiv\n hello: $who")}
  185. open(absolutize("tmp/test_compile_file.scss"), "w") {|f| f.write("$who: world; div { hello: $who }")}
  186. assert_equal "div { hello: world; }\n", Sass.compile_file(absolutize("tmp/test_compile_file.sass"), :style => :compact)
  187. assert_equal "div { hello: world; }\n", Sass.compile_file(absolutize("tmp/test_compile_file.scss"), :style => :compact)
  188. ensure
  189. FileUtils.rm_rf(absolutize("tmp"))
  190. end
  191. def test_compile_file_to_css_file
  192. FileUtils.mkdir_p(absolutize("tmp"))
  193. open(absolutize("tmp/test_compile_file.sass"), "w") {|f| f.write("$who: world\ndiv\n hello: $who")}
  194. open(absolutize("tmp/test_compile_file.scss"), "w") {|f| f.write("$who: world; div { hello: $who }")}
  195. Sass.compile_file(absolutize("tmp/test_compile_file.sass"), absolutize("tmp/test_compile_file_sass.css"), :style => :compact)
  196. Sass.compile_file(absolutize("tmp/test_compile_file.scss"), absolutize("tmp/test_compile_file_scss.css"), :style => :compact)
  197. assert_equal "div { hello: world; }\n", File.read(absolutize("tmp/test_compile_file_sass.css"))
  198. assert_equal "div { hello: world; }\n", File.read(absolutize("tmp/test_compile_file_scss.css"))
  199. ensure
  200. FileUtils.rm_rf(absolutize("tmp"))
  201. end
  202. def test_flexible_tabulation
  203. assert_equal("p {\n a: b; }\n p q {\n c: d; }\n",
  204. render("p\n a: b\n q\n c: d\n"))
  205. assert_equal("p {\n a: b; }\n p q {\n c: d; }\n",
  206. render("p\n\ta: b\n\tq\n\t\tc: d\n"))
  207. end
  208. def test_import_same_name_different_ext
  209. assert_warning <<WARNING do
  210. WARNING: On line 1 of test_import_same_name_different_ext_inline.sass:
  211. It's not clear which file to import for '@import "same_name_different_ext"'.
  212. Candidates:
  213. same_name_different_ext.sass
  214. same_name_different_ext.scss
  215. For now I'll choose same_name_different_ext.sass.
  216. This will be an error in future versions of Sass.
  217. WARNING
  218. options = {:load_paths => [File.dirname(__FILE__) + '/templates/']}
  219. munge_filename options
  220. result = Sass::Engine.new("@import 'same_name_different_ext'", options).render
  221. assert_equal(<<CSS, result)
  222. .foo {
  223. ext: sass; }
  224. CSS
  225. end
  226. end
  227. def test_import_same_name_different_partiality
  228. assert_warning <<WARNING do
  229. WARNING: On line 1 of test_import_same_name_different_partiality_inline.sass:
  230. It's not clear which file to import for '@import "same_name_different_partiality"'.
  231. Candidates:
  232. _same_name_different_partiality.scss
  233. same_name_different_partiality.scss
  234. For now I'll choose _same_name_different_partiality.scss.
  235. This will be an error in future versions of Sass.
  236. WARNING
  237. options = {:load_paths => [File.dirname(__FILE__) + '/templates/']}
  238. munge_filename options
  239. result = Sass::Engine.new("@import 'same_name_different_partiality'", options).render
  240. assert_equal(<<CSS, result)
  241. .foo {
  242. partial: yes; }
  243. CSS
  244. end
  245. end
  246. EXCEPTION_MAP.each do |key, value|
  247. define_method("test_exception (#{key.inspect})") do
  248. line = 10
  249. begin
  250. silence_warnings {Sass::Engine.new(key, :filename => FAKE_FILE_NAME, :line => line).render}
  251. rescue Sass::SyntaxError => err
  252. value = [value] unless value.is_a?(Array)
  253. assert_equal(value.first.rstrip, err.message, "Line: #{key}")
  254. assert_equal(FAKE_FILE_NAME, err.sass_filename)
  255. assert_equal((value[1] || key.split("\n").length) + line - 1, err.sass_line, "Line: #{key}")
  256. assert_match(/#{Regexp.escape(FAKE_FILE_NAME)}:[0-9]+/, err.backtrace[0], "Line: #{key}")
  257. else
  258. assert(false, "Exception not raised for\n#{key}")
  259. end
  260. end
  261. end
  262. def test_exception_line
  263. to_render = <<SASS
  264. rule
  265. :prop val
  266. // comment!
  267. :broken
  268. SASS
  269. begin
  270. Sass::Engine.new(to_render).render
  271. rescue Sass::SyntaxError => err
  272. assert_equal(5, err.sass_line)
  273. else
  274. assert(false, "Exception not raised for '#{to_render}'!")
  275. end
  276. end
  277. def test_exception_location
  278. to_render = <<SASS
  279. rule
  280. :prop val
  281. // comment!
  282. :broken
  283. SASS
  284. begin
  285. Sass::Engine.new(to_render, :filename => FAKE_FILE_NAME, :line => (__LINE__-7)).render
  286. rescue Sass::SyntaxError => err
  287. assert_equal(FAKE_FILE_NAME, err.sass_filename)
  288. assert_equal((__LINE__-6), err.sass_line)
  289. else
  290. assert(false, "Exception not raised for '#{to_render}'!")
  291. end
  292. end
  293. def test_imported_exception
  294. [1, 2, 3, 4].each do |i|
  295. begin
  296. Sass::Engine.new("@import bork#{i}", :load_paths => [File.dirname(__FILE__) + '/templates/']).render
  297. rescue Sass::SyntaxError => err
  298. assert_equal(2, err.sass_line)
  299. assert_match(/(\/|^)bork#{i}\.sass$/, err.sass_filename)
  300. assert_hash_has(err.sass_backtrace.first,
  301. :filename => err.sass_filename, :line => err.sass_line)
  302. assert_nil(err.sass_backtrace[1][:filename])
  303. assert_equal(1, err.sass_backtrace[1][:line])
  304. assert_match(/(\/|^)bork#{i}\.sass:2$/, err.backtrace.first)
  305. assert_equal("(sass):1", err.backtrace[1])
  306. else
  307. assert(false, "Exception not raised for imported template: bork#{i}")
  308. end
  309. end
  310. end
  311. def test_double_imported_exception
  312. [1, 2, 3, 4].each do |i|
  313. begin
  314. Sass::Engine.new("@import nested_bork#{i}", :load_paths => [File.dirname(__FILE__) + '/templates/']).render
  315. rescue Sass::SyntaxError => err
  316. assert_equal(2, err.sass_line)
  317. assert_match(/(\/|^)bork#{i}\.sass$/, err.sass_filename)
  318. assert_hash_has(err.sass_backtrace.first,
  319. :filename => err.sass_filename, :line => err.sass_line)
  320. assert_match(/(\/|^)nested_bork#{i}\.sass$/, err.sass_backtrace[1][:filename])
  321. assert_equal(2, err.sass_backtrace[1][:line])
  322. assert_nil(err.sass_backtrace[2][:filename])
  323. assert_equal(1, err.sass_backtrace[2][:line])
  324. assert_match(/(\/|^)bork#{i}\.sass:2$/, err.backtrace.first)
  325. assert_match(/(\/|^)nested_bork#{i}\.sass:2$/, err.backtrace[1])
  326. assert_equal("(sass):1", err.backtrace[2])
  327. else
  328. assert(false, "Exception not raised for imported template: bork#{i}")
  329. end
  330. end
  331. end
  332. def test_selector_tracing
  333. actual_css = render(<<-SCSS, :syntax => :scss, :trace_selectors => true)
  334. @mixin mixed {
  335. .mixed { color: red; }
  336. }
  337. .context {
  338. @include mixed;
  339. }
  340. SCSS
  341. assert_equal(<<CSS,actual_css)
  342. /* on line 2 of test_selector_tracing_inline.scss, in `mixed'
  343. from line 5 of test_selector_tracing_inline.scss */
  344. .context .mixed {
  345. color: red; }
  346. CSS
  347. end
  348. def test_mixin_exception
  349. render(<<SASS)
  350. =error-mixin($a)
  351. color: $a * 1em * 1px
  352. =outer-mixin($a)
  353. +error-mixin($a)
  354. .error
  355. +outer-mixin(12)
  356. SASS
  357. assert(false, "Exception not raised")
  358. rescue Sass::SyntaxError => err
  359. assert_equal(2, err.sass_line)
  360. assert_equal(filename_for_test, err.sass_filename)
  361. assert_equal("error-mixin", err.sass_mixin)
  362. assert_hash_has(err.sass_backtrace.first, :line => err.sass_line,
  363. :filename => err.sass_filename, :mixin => err.sass_mixin)
  364. assert_hash_has(err.sass_backtrace[1], :line => 5,
  365. :filename => filename_for_test, :mixin => "outer-mixin")
  366. assert_hash_has(err.sass_backtrace[2], :line => 8,
  367. :filename => filename_for_test, :mixin => nil)
  368. assert_equal("#{filename_for_test}:2:in `error-mixin'", err.backtrace.first)
  369. assert_equal("#{filename_for_test}:5:in `outer-mixin'", err.backtrace[1])
  370. assert_equal("#{filename_for_test}:8", err.backtrace[2])
  371. end
  372. def test_mixin_callsite_exception
  373. render(<<SASS)
  374. =one-arg-mixin($a)
  375. color: $a
  376. =outer-mixin($a)
  377. +one-arg-mixin($a, 12)
  378. .error
  379. +outer-mixin(12)
  380. SASS
  381. assert(false, "Exception not raised")
  382. rescue Sass::SyntaxError => err
  383. assert_hash_has(err.sass_backtrace.first, :line => 5,
  384. :filename => filename_for_test, :mixin => "one-arg-mixin")
  385. assert_hash_has(err.sass_backtrace[1], :line => 5,
  386. :filename => filename_for_test, :mixin => "outer-mixin")
  387. assert_hash_has(err.sass_backtrace[2], :line => 8,
  388. :filename => filename_for_test, :mixin => nil)
  389. end
  390. def test_mixin_exception_cssize
  391. render(<<SASS)
  392. =parent-ref-mixin
  393. & foo
  394. a: b
  395. =outer-mixin
  396. +parent-ref-mixin
  397. +outer-mixin
  398. SASS
  399. assert(false, "Exception not raised")
  400. rescue Sass::SyntaxError => err
  401. assert_hash_has(err.sass_backtrace.first, :line => 2,
  402. :filename => filename_for_test, :mixin => "parent-ref-mixin")
  403. assert_hash_has(err.sass_backtrace[1], :line => 6,
  404. :filename => filename_for_test, :mixin => "outer-mixin")
  405. assert_hash_has(err.sass_backtrace[2], :line => 8,
  406. :filename => filename_for_test, :mixin => nil)
  407. end
  408. def test_mixin_and_import_exception
  409. Sass::Engine.new("@import nested_mixin_bork", :load_paths => [File.dirname(__FILE__) + '/templates/']).render
  410. assert(false, "Exception not raised")
  411. rescue Sass::SyntaxError => err
  412. assert_match(/(\/|^)nested_mixin_bork\.sass$/, err.sass_backtrace.first[:filename])
  413. assert_hash_has(err.sass_backtrace.first, :mixin => "error-mixin", :line => 4)
  414. assert_match(/(\/|^)mixin_bork\.sass$/, err.sass_backtrace[1][:filename])
  415. assert_hash_has(err.sass_backtrace[1], :mixin => "outer-mixin", :line => 2)
  416. assert_match(/(\/|^)mixin_bork\.sass$/, err.sass_backtrace[2][:filename])
  417. assert_hash_has(err.sass_backtrace[2], :mixin => nil, :line => 5)
  418. assert_match(/(\/|^)nested_mixin_bork\.sass$/, err.sass_backtrace[3][:filename])
  419. assert_hash_has(err.sass_backtrace[3], :mixin => nil, :line => 6)
  420. assert_hash_has(err.sass_backtrace[4], :filename => nil, :mixin => nil, :line => 1)
  421. end
  422. def test_basic_mixin_loop_exception
  423. render <<SASS
  424. @mixin foo
  425. @include foo
  426. @include foo
  427. SASS
  428. assert(false, "Exception not raised")
  429. rescue Sass::SyntaxError => err
  430. assert_equal("An @include loop has been found: foo includes itself", err.message)
  431. assert_hash_has(err.sass_backtrace[0], :mixin => "foo", :line => 2)
  432. end
  433. def test_double_mixin_loop_exception
  434. render <<SASS
  435. @mixin foo
  436. @include bar
  437. @mixin bar
  438. @include foo
  439. @include foo
  440. SASS
  441. assert(false, "Exception not raised")
  442. rescue Sass::SyntaxError => err
  443. assert_equal(<<MESSAGE.rstrip, err.message)
  444. An @include loop has been found:
  445. foo includes bar
  446. bar includes foo
  447. MESSAGE
  448. assert_hash_has(err.sass_backtrace[0], :mixin => "bar", :line => 4)
  449. assert_hash_has(err.sass_backtrace[1], :mixin => "foo", :line => 2)
  450. end
  451. def test_deep_mixin_loop_exception
  452. render <<SASS
  453. @mixin foo
  454. @include bar
  455. @mixin bar
  456. @include baz
  457. @mixin baz
  458. @include foo
  459. @include foo
  460. SASS
  461. assert(false, "Exception not raised")
  462. rescue Sass::SyntaxError => err
  463. assert_equal(<<MESSAGE.rstrip, err.message)
  464. An @include loop has been found:
  465. foo includes bar
  466. bar includes baz
  467. baz includes foo
  468. MESSAGE
  469. assert_hash_has(err.sass_backtrace[0], :mixin => "baz", :line => 8)
  470. assert_hash_has(err.sass_backtrace[1], :mixin => "bar", :line => 5)
  471. assert_hash_has(err.sass_backtrace[2], :mixin => "foo", :line => 2)
  472. end
  473. def test_mixin_loop_with_content
  474. render <<SASS
  475. =foo
  476. @content
  477. =bar
  478. +foo
  479. +bar
  480. +bar
  481. SASS
  482. assert(false, "Exception not raised")
  483. rescue Sass::SyntaxError => err
  484. assert_equal("An @include loop has been found: bar includes itself", err.message)
  485. assert_hash_has(err.sass_backtrace[0], :mixin => "@content", :line => 5)
  486. end
  487. def test_basic_import_loop_exception
  488. import = filename_for_test
  489. importer = MockImporter.new
  490. importer.add_import(import, "@import '#{import}'")
  491. engine = Sass::Engine.new("@import '#{import}'", :filename => import,
  492. :load_paths => [importer])
  493. assert_raise_message(Sass::SyntaxError, <<ERR.rstrip) {engine.render}
  494. An @import loop has been found: #{import} imports itself
  495. ERR
  496. end
  497. def test_double_import_loop_exception
  498. importer = MockImporter.new
  499. importer.add_import("foo", "@import 'bar'")
  500. importer.add_import("bar", "@import 'foo'")
  501. engine = Sass::Engine.new('@import "foo"', :filename => filename_for_test,
  502. :load_paths => [importer])
  503. assert_raise_message(Sass::SyntaxError, <<ERR.rstrip) {engine.render}
  504. An @import loop has been found:
  505. #{filename_for_test} imports foo
  506. foo imports bar
  507. bar imports foo
  508. ERR
  509. end
  510. def test_deep_import_loop_exception
  511. importer = MockImporter.new
  512. importer.add_import("foo", "@import 'bar'")
  513. importer.add_import("bar", "@import 'baz'")
  514. importer.add_import("baz", "@import 'foo'")
  515. engine = Sass::Engine.new('@import "foo"', :filename => filename_for_test,
  516. :load_paths => [importer])
  517. assert_raise_message(Sass::SyntaxError, <<ERR.rstrip) {engine.render}
  518. An @import loop has been found:
  519. #{filename_for_test} imports foo
  520. foo imports bar
  521. bar imports baz
  522. baz imports foo
  523. ERR
  524. end
  525. def test_exception_css_with_offset
  526. opts = {:full_exception => true, :line => 362}
  527. render(("a\n b: c\n" * 10) + "d\n e:\n" + ("f\n g: h\n" * 10), opts)
  528. rescue Sass::SyntaxError => e
  529. assert_equal(<<CSS, Sass::SyntaxError.exception_to_css(e, opts).split("\n")[0..15].join("\n"))
  530. /*
  531. Syntax error: Invalid property: "e:" (no value).
  532. on line 383 of test_exception_css_with_offset_inline.sass
  533. 378: a
  534. 379: b: c
  535. 380: a
  536. 381: b: c
  537. 382: d
  538. 383: e:
  539. 384: f
  540. 385: g: h
  541. 386: f
  542. 387: g: h
  543. 388: f
  544. CSS
  545. else
  546. assert(false, "Exception not raised for test_exception_css_with_offset")
  547. end
  548. def test_exception_css_with_mixins
  549. opts = {:full_exception => true}
  550. render(<<SASS, opts)
  551. =error-mixin($a)
  552. color: $a * 1em * 1px
  553. =outer-mixin($a)
  554. +error-mixin($a)
  555. .error
  556. +outer-mixin(12)
  557. SASS
  558. rescue Sass::SyntaxError => e
  559. assert_equal(<<CSS, Sass::SyntaxError.exception_to_css(e, opts).split("\n")[0..13].join("\n"))
  560. /*
  561. Syntax error: 12em*px isn't a valid CSS value.
  562. on line 2 of test_exception_css_with_mixins_inline.sass, in `error-mixin'
  563. from line 5 of test_exception_css_with_mixins_inline.sass, in `outer-mixin'
  564. from line 8 of test_exception_css_with_mixins_inline.sass
  565. 1: =error-mixin($a)
  566. 2: color: $a * 1em * 1px
  567. 3:
  568. 4: =outer-mixin($a)
  569. 5: +error-mixin($a)
  570. 6:
  571. 7: .error
  572. CSS
  573. else
  574. assert(false, "Exception not raised")
  575. end
  576. def test_cssize_exception_css
  577. opts = {:full_exception => true}
  578. render(<<SASS, opts)
  579. .filler
  580. stuff: "stuff!"
  581. a: b
  582. .more.filler
  583. a: b
  584. SASS
  585. rescue Sass::SyntaxError => e
  586. assert_equal(<<CSS, Sass::SyntaxError.exception_to_css(e, opts).split("\n")[0..11].join("\n"))
  587. /*
  588. Syntax error: Properties are only allowed within rules, directives, mixin includes, or other properties.
  589. on line 4 of test_cssize_exception_css_inline.sass
  590. 1: .filler
  591. 2: stuff: "stuff!"
  592. 3:
  593. 4: a: b
  594. 5:
  595. 6: .more.filler
  596. 7: a: b
  597. CSS
  598. else
  599. assert(false, "Exception not raised")
  600. end
  601. def test_css_import
  602. assert_equal("@import url(./fonts.css);\n", render("@import \"./fonts.css\""))
  603. end
  604. def test_http_import
  605. assert_equal("@import url(http://fonts.googleapis.com/css?family=Droid+Sans);\n",
  606. render("@import \"http://fonts.googleapis.com/css?family=Droid+Sans\""))
  607. end
  608. def test_protocol_relative_import
  609. assert_equal("@import url(//fonts.googleapis.com/css?family=Droid+Sans);\n",
  610. render("@import \"//fonts.googleapis.com/css?family=Droid+Sans\""))
  611. end
  612. def test_import_with_interpolation
  613. assert_equal(<<CSS, render(<<SASS))
  614. @import url("http://fonts.googleapis.com/css?family=Droid+Sans");
  615. CSS
  616. $family: unquote("Droid+Sans")
  617. @import url("http://fonts.googleapis.com/css?family=\#{$family}")
  618. SASS
  619. end
  620. def test_import_with_dynamic_media_query
  621. assert_equal(<<CSS, render(<<SASS))
  622. @import "foo" print and (-webkit-min-device-pixel-ratio-foo: 25);
  623. CSS
  624. $media: print
  625. $key: -webkit-min-device-pixel-ratio
  626. $value: 20
  627. @import "foo" \#{$media} and ($key + "-foo": $value + 5)
  628. SASS
  629. end
  630. def test_url_import
  631. assert_equal("@import url(fonts.sass);\n", render("@import url(fonts.sass)"))
  632. end
  633. def test_sass_import
  634. sassc_file = sassc_path("importee")
  635. assert !File.exists?(sassc_file)
  636. renders_correctly "import", { :style => :compact, :load_paths => [File.dirname(__FILE__) + "/templates"] }
  637. assert File.exists?(sassc_file)
  638. end
  639. def test_sass_pathname_import
  640. sassc_file = sassc_path("importee")
  641. assert !File.exists?(sassc_file)
  642. renders_correctly("import",
  643. :style => :compact,
  644. :load_paths => [Pathname.new(File.dirname(__FILE__) + "/templates")])
  645. assert File.exists?(sassc_file)
  646. end
  647. def test_import_from_global_load_paths
  648. importer = MockImporter.new
  649. importer.add_import("imported", "div{color:red}")
  650. Sass.load_paths << importer
  651. assert_equal "div {\n color: red; }\n", Sass::Engine.new('@import "imported"').render
  652. ensure
  653. Sass.load_paths.clear
  654. end
  655. def test_nonexistent_import
  656. assert_raise_message(Sass::SyntaxError, <<ERR.rstrip) do
  657. File to import not found or unreadable: nonexistent.sass.
  658. Load path: #{Dir.pwd}
  659. ERR
  660. render("@import nonexistent.sass")
  661. end
  662. end
  663. def test_nonexistent_extensionless_import
  664. assert_raise_message(Sass::SyntaxError, <<ERR.rstrip) do
  665. File to import not found or unreadable: nonexistent.
  666. Load path: #{Dir.pwd}
  667. ERR
  668. render("@import nonexistent")
  669. end
  670. end
  671. def test_no_cache
  672. assert !File.exists?(sassc_path("importee"))
  673. renders_correctly("import", {
  674. :style => :compact, :cache => false,
  675. :load_paths => [File.dirname(__FILE__) + "/templates"],
  676. })
  677. assert !File.exists?(sassc_path("importee"))
  678. end
  679. def test_import_in_rule
  680. assert_equal(<<CSS, render(<<SASS, :load_paths => [File.dirname(__FILE__) + '/templates/']))
  681. .foo #foo {
  682. background-color: #bbaaff; }
  683. .bar {
  684. a: b; }
  685. .bar #foo {
  686. background-color: #bbaaff; }
  687. CSS
  688. .foo
  689. @import partial
  690. .bar
  691. a: b
  692. @import partial
  693. SASS
  694. end
  695. def test_units
  696. renders_correctly "units"
  697. end
  698. def test_default_function
  699. assert_equal(<<CSS, render(<<SASS))
  700. foo {
  701. bar: url("foo.png"); }
  702. CSS
  703. foo
  704. bar: url("foo.png")
  705. SASS
  706. assert_equal("foo {\n bar: url(); }\n", render("foo\n bar: url()\n"));
  707. end
  708. def test_string_minus
  709. assert_equal("foo {\n bar: baz-boom-bat; }\n", render(%Q{foo\n bar: baz-boom-bat}))
  710. assert_equal("foo {\n bar: -baz-boom; }\n", render(%Q{foo\n bar: -baz-boom}))
  711. end
  712. def test_string_div
  713. assert_equal("foo {\n bar: baz/boom/bat; }\n", render(%Q{foo\n bar: baz/boom/bat}))
  714. assert_equal("foo {\n bar: /baz/boom; }\n", render(%Q{foo\n bar: /baz/boom}))
  715. end
  716. def test_basic_multiline_selector
  717. assert_equal("#foo #bar,\n#baz #boom {\n foo: bar; }\n",
  718. render("#foo #bar,\n#baz #boom\n :foo bar"))
  719. assert_equal("#foo #bar,\n#foo #baz {\n foo: bar; }\n",
  720. render("#foo\n #bar,\n #baz\n :foo bar"))
  721. assert_equal("#foo,\n#bar {\n foo: bar; }\n #foo #baz,\n #bar #baz {\n foo: bar; }\n",
  722. render("#foo,\n#bar\n :foo bar\n #baz\n :foo bar"))
  723. assert_equal("#foo #bar, #baz #boom { foo: bar; }\n",
  724. render("#foo #bar,\n#baz #boom\n :foo bar", :style => :compact))
  725. assert_equal("#foo #bar,#baz #boom{foo:bar}\n",
  726. render("#foo #bar,\n#baz #boom\n :foo bar", :style => :compressed))
  727. assert_equal("#foo #bar,\n#baz #boom {\n foo: bar; }\n",
  728. render("#foo #bar,,\n,#baz #boom,\n :foo bar"))
  729. assert_equal("#bip #bop {\n foo: bar; }\n",
  730. render("#bip #bop,, ,\n :foo bar"))
  731. end
  732. def test_complex_multiline_selector
  733. renders_correctly "multiline"
  734. end
  735. def test_colon_only
  736. begin
  737. render("a\n b: c", :property_syntax => :old)
  738. rescue Sass::SyntaxError => e
  739. assert_equal("Illegal property syntax: can't use new syntax when :property_syntax => :old is set.",
  740. e.message)
  741. assert_equal(2, e.sass_line)
  742. else
  743. assert(false, "SyntaxError not raised for :property_syntax => :old")
  744. end
  745. begin
  746. render("a\n :b c", :property_syntax => :new)
  747. assert_equal(2, e.sass_line)
  748. rescue Sass::SyntaxError => e
  749. assert_equal("Illegal property syntax: can't use old syntax when :property_syntax => :new is set.",
  750. e.message)
  751. else
  752. assert(false, "SyntaxError not raised for :property_syntax => :new")
  753. end
  754. end
  755. def test_pseudo_elements
  756. assert_equal(<<CSS, render(<<SASS))
  757. ::first-line {
  758. size: 10em; }
  759. CSS
  760. ::first-line
  761. size: 10em
  762. SASS
  763. end
  764. def test_directive
  765. assert_equal("@a b;\n", render("@a b"))
  766. assert_equal("@a {\n b: c; }\n", render("@a\n :b c"))
  767. assert_equal("@a { b: c; }\n", render("@a\n :b c", :style => :compact))
  768. assert_equal("@a {\n b: c;\n}\n", render("@a\n :b c", :style => :expanded))
  769. assert_equal("@a{b:c}\n", render("@a\n :b c", :style => :compressed))
  770. assert_equal("@a {\n b: c;\n d: e; }\n",
  771. render("@a\n :b c\n :d e"))
  772. assert_equal("@a { b: c; d: e; }\n",
  773. render("@a\n :b c\n :d e", :style => :compact))
  774. assert_equal("@a {\n b: c;\n d: e;\n}\n",
  775. render("@a\n :b c\n :d e", :style => :expanded))
  776. assert_equal("@a{b:c;d:e}\n",
  777. render("@a\n :b c\n :d e", :style => :compressed))
  778. assert_equal("@a {\n #b {\n c: d; } }\n",
  779. render("@a\n #b\n :c d"))
  780. assert_equal("@a { #b { c: d; } }\n",
  781. render("@a\n #b\n :c d", :style => :compact))
  782. assert_equal("@a {\n #b {\n c: d;\n }\n}\n",
  783. render("@a\n #b\n :c d", :style => :expanded))
  784. assert_equal("@a{#b{c:d}}\n",
  785. render("@a\n #b\n :c d", :style => :compressed))
  786. assert_equal("@a {\n #b {\n a: b; }\n #b #c {\n d: e; } }\n",
  787. render("@a\n #b\n :a b\n #c\n :d e"))
  788. assert_equal("@a { #b { a: b; }\n #b #c { d: e; } }\n",
  789. render("@a\n #b\n :a b\n #c\n :d e", :style => :compact))
  790. assert_equal("@a {\n #b {\n a: b;\n }\n #b #c {\n d: e;\n }\n}\n",
  791. render("@a\n #b\n :a b\n #c\n :d e", :style => :expanded))
  792. assert_equal("@a{#b{a:b}#b #c{d:e}}\n",
  793. render("@a\n #b\n :a b\n #c\n :d e", :style => :compressed))
  794. assert_equal("@a {\n #foo,\n #bar {\n b: c; } }\n",
  795. render("@a\n #foo, \n #bar\n :b c"))
  796. assert_equal("@a { #foo, #bar { b: c; } }\n",
  797. render("@a\n #foo, \n #bar\n :b c", :style => :compact))
  798. assert_equal("@a {\n #foo,\n #bar {\n b: c;\n }\n}\n",
  799. render("@a\n #foo, \n #bar\n :b c", :style => :expanded))
  800. assert_equal("@a{#foo,#bar{b:c}}\n",
  801. render("@a\n #foo, \n #bar\n :b c", :style => :compressed))
  802. to_render = <<END
  803. @a
  804. :b c
  805. #d
  806. :e f
  807. :g h
  808. END
  809. rendered = <<END
  810. @a { b: c;
  811. #d { e: f; }
  812. g: h; }
  813. END
  814. assert_equal(rendered, render(to_render, :style => :compact))
  815. assert_equal("@a{b:c;#d{e:f}g:h}\n", render(to_render, :style => :compressed))
  816. end
  817. def test_property_hacks
  818. assert_equal(<<CSS, render(<<SASS))
  819. foo {
  820. _name: val;
  821. *name: val;
  822. #name: val;
  823. .name: val;
  824. name/**/: val;
  825. name/*\\**/: val;
  826. name: val; }
  827. CSS
  828. foo
  829. _name: val
  830. *name: val
  831. #name: val
  832. .name: val
  833. name/**/: val
  834. name/*\\**/: val
  835. name: val
  836. SASS
  837. end
  838. def test_properties_with_space_after_colon
  839. assert_equal <<CSS, render(<<SASS)
  840. foo {
  841. bar: baz;
  842. bizz: bap; }
  843. CSS
  844. foo
  845. bar : baz
  846. bizz : bap
  847. SASS
  848. end
  849. def test_line_annotations
  850. assert_equal(<<CSS, render(<<SASS, :line_comments => true, :style => :compact))
  851. /* line 2, test_line_annotations_inline.sass */
  852. foo bar { foo: bar; }
  853. /* line 5, test_line_annotations_inline.sass */
  854. foo baz { blip: blop; }
  855. /* line 9, test_line_annotations_inline.sass */
  856. floodle { flop: blop; }
  857. /* line 18, test_line_annotations_inline.sass */
  858. bup { mix: on; }
  859. /* line 15, test_line_annotations_inline.sass */
  860. bup mixin { moop: mup; }
  861. /* line 22, test_line_annotations_inline.sass */
  862. bip hop, skip hop { a: b; }
  863. CSS
  864. foo
  865. bar
  866. foo: bar
  867. baz
  868. blip: blop
  869. floodle
  870. flop: blop
  871. =mxn
  872. mix: on
  873. mixin
  874. moop: mup
  875. bup
  876. +mxn
  877. bip, skip
  878. hop
  879. a: b
  880. SASS
  881. end
  882. def test_line_annotations_with_filename
  883. renders_correctly "line_numbers", :line_comments => true, :load_paths => [File.dirname(__FILE__) + "/templates"]
  884. end
  885. def test_debug_info
  886. esc_file_name = Sass::SCSS::RX.escape_ident(Sass::Util.scope("test_debug_info_inline.sass"))
  887. assert_equal(<<CSS, render(<<SASS, :debug_info => true, :style => :compact))
  888. @media -sass-debug-info{filename{font-family:file\\:\\/\\/#{esc_file_name}}line{font-family:\\000032}}
  889. foo bar { foo: bar; }
  890. @media -sass-debug-info{filename{font-family:file\\:\\/\\/#{esc_file_name}}line{font-family:\\000035}}
  891. foo baz { blip: blop; }
  892. @media -sass-debug-info{filename{font-family:file\\:\\/\\/#{esc_file_name}}line{font-family:\\000039}}
  893. floodle { flop: blop; }
  894. @media -sass-debug-info{filename{font-family:file\\:\\/\\/#{esc_file_name}}line{font-family:\\0000318}}
  895. bup { mix: on; }
  896. @media -sass-debug-info{filename{font-family:file\\:\\/\\/#{esc_file_name}}line{font-family:\\0000315}}
  897. bup mixin { moop: mup; }
  898. @media -sass-debug-info{filename{font-family:file\\:\\/\\/#{esc_file_name}}line{font-family:\\0000322}}
  899. bip hop, skip hop { a: b; }
  900. CSS
  901. foo
  902. bar
  903. foo: bar
  904. baz
  905. blip: blop
  906. floodle
  907. flop: blop
  908. =mxn
  909. mix: on
  910. mixin
  911. moop: mup
  912. bup
  913. +mxn
  914. bip, skip
  915. hop
  916. a: b
  917. SASS
  918. end
  919. def test_debug_info_without_filename
  920. assert_equal(<<CSS, Sass::Engine.new(<<SASS, :debug_info => true).render)
  921. @media -sass-debug-info{filename{}line{font-family:\\000031}}
  922. foo {
  923. a: b; }
  924. CSS
  925. foo
  926. a: b
  927. SASS
  928. end
  929. def test_debug_info_with_compressed
  930. assert_equal(<<CSS, render(<<SASS, :debug_info => true, :style => :compressed))
  931. foo{a:b}
  932. CSS
  933. foo
  934. a: b
  935. SASS
  936. end
  937. def test_debug_info_with_line_annotations
  938. esc_file_name = Sass::SCSS::RX.escape_ident(Sass::Util.scope("test_debug_info_with_line_annotations_inline.sass"))
  939. assert_equal(<<CSS, render(<<SASS, :debug_info => true, :line_comments => true))
  940. @media -sass-debug-info{filename{font-family:file\\:\\/\\/#{esc_file_name}}line{font-family:\\000031}}
  941. foo {
  942. a: b; }
  943. CSS
  944. foo
  945. a: b
  946. SASS
  947. end
  948. def test_debug_info_in_keyframes
  949. assert_equal(<<CSS, render(<<SASS, :debug_info => true))
  950. @-webkit-keyframes warm {
  951. from {
  952. color: black; }
  953. to {
  954. color: red; } }
  955. CSS
  956. @-webkit-keyframes warm
  957. from
  958. color: black
  959. to
  960. color: red
  961. SASS
  962. end
  963. def test_empty_first_line
  964. assert_equal("#a {\n b: c; }\n", render("#a\n\n b: c"))
  965. end
  966. def test_escaped_rule
  967. assert_equal(":focus {\n a: b; }\n", render("\\:focus\n a: b"))
  968. assert_equal("a {\n b: c; }\n a :focus {\n d: e; }\n", render("\\a\n b: c\n \\:focus\n d: e"))
  969. end
  970. def test_cr_newline
  971. 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"))
  972. end
  973. def test_property_with_content_and_nested_props
  974. assert_equal(<<CSS, render(<<SASS))
  975. foo {
  976. a: b;
  977. a-c: d;
  978. a-c-e: f; }
  979. CSS
  980. foo
  981. a: b
  982. c: d
  983. e: f
  984. SASS
  985. assert_equal(<<CSS, render(<<SASS))
  986. foo {
  987. a: b;
  988. a-c-e: f; }
  989. CSS
  990. foo
  991. a: b
  992. c:
  993. e: f
  994. SASS
  995. end
  996. def test_guarded_assign
  997. assert_equal("foo {\n a: b; }\n", render(%Q{$foo: b\n$foo: c !default\nfoo\n a: $foo}))
  998. assert_equal("foo {\n a: b; }\n", render(%Q{$foo: b !default\nfoo\n a: $foo}))
  999. assert_equal("foo {\n a: b; }\n", render(%Q{$foo: null\n$foo: b !default\nfoo\n a: $foo}))
  1000. end
  1001. def test_mixins
  1002. renders_correctly "mixins", { :style => :expanded }
  1003. end
  1004. def test_directive_style_mixins
  1005. assert_equal <<CSS, render(<<SASS)
  1006. bar {
  1007. prop: baz; }
  1008. CSS
  1009. @mixin foo($arg)
  1010. prop: $arg
  1011. bar
  1012. @include foo(baz)
  1013. SASS
  1014. end
  1015. def test_mixins_dont_interfere_with_sibling_combinator
  1016. assert_equal("foo + bar {\n a: b; }\nfoo + baz {\n c: d; }\n",
  1017. render("foo\n +\n bar\n a: b\n baz\n c: d"))
  1018. end
  1019. def test_mixin_args
  1020. assert_equal("blat {\n baz: hi; }\n", render(<<SASS))
  1021. =foo($bar)
  1022. baz: $bar
  1023. blat
  1024. +foo(hi)
  1025. SASS
  1026. assert_equal("blat {\n baz: 3; }\n", render(<<SASS))
  1027. =foo($a, $b)
  1028. baz: $a + $b
  1029. blat
  1030. +foo(1, 2)
  1031. SASS
  1032. assert_equal("blat {\n baz: 4;\n baz: 3;\n baz: 5;\n bang: 3; }\n", render(<<SASS))
  1033. =foo($c: (6 + 4) / 2)
  1034. baz: $c
  1035. $c: 3
  1036. blat
  1037. +foo($c + 1)
  1038. +foo(($c + 3)/2)
  1039. +foo
  1040. bang: $c
  1041. SASS
  1042. end
  1043. def test_default_values_for_mixin_arguments
  1044. assert_equal("white {\n color: white; }\n\nblack {\n color: black; }\n", render(<<SASS))
  1045. =foo($a: #FFF)
  1046. :color $a
  1047. white
  1048. +foo
  1049. black
  1050. +foo(#000)
  1051. SASS
  1052. assert_equal(<<CSS, render(<<SASS))
  1053. one {
  1054. color: white;
  1055. padding: 1px;
  1056. margin: 4px; }
  1057. two {
  1058. color: white;
  1059. padding: 2px;
  1060. margin: 5px; }
  1061. three {
  1062. color: white;
  1063. padding: 2px;
  1064. margin: 3px; }
  1065. CSS
  1066. $a: 5px
  1067. =foo($a, $b: 1px, $c: 3px + $b)
  1068. :color $a
  1069. :padding $b
  1070. :margin $c
  1071. one
  1072. +foo(#fff)
  1073. two
  1074. +foo(#fff, 2px)
  1075. three
  1076. +foo(#fff, 2px, 3px)
  1077. SASS
  1078. assert_equal(<<CSS, render(<<SASS))
  1079. one {
  1080. color: white;
  1081. padding: 1px;
  1082. margin: 4px; }
  1083. two {
  1084. color: white;
  1085. padding: 2px;
  1086. margin: 5px; }
  1087. three {
  1088. color: white;
  1089. padding: 2px;
  1090. margin: 3px; }
  1091. CSS
  1092. $a: 5px
  1093. =foo($a, $b: 1px, $c: null)
  1094. $c: 3px + $b !default
  1095. color: $a
  1096. padding: $b
  1097. margin: $c
  1098. one
  1099. +foo(#fff)
  1100. two
  1101. +foo(#fff, 2px)
  1102. three
  1103. +foo(#fff, 2px, 3px)
  1104. SASS
  1105. end
  1106. def test_hyphen_underscore_insensitive_mixins
  1107. assert_equal(<<CSS, render(<<SASS))
  1108. a {
  1109. b: 12;
  1110. c: foo; }
  1111. CSS
  1112. =mixin-hyphen
  1113. b: 12
  1114. =mixin_under
  1115. c: foo
  1116. a
  1117. +mixin_hyphen
  1118. +mixin-under
  1119. SASS
  1120. end
  1121. def test_css_identifier_mixin
  1122. assert_equal(<<CSS, render(<<SASS))
  1123. a {
  1124. foo: 12; }
  1125. CSS
  1126. =\\{foo\\(12\\)($a)
  1127. foo: $a
  1128. a
  1129. +\\{foo\\(12\\)(12)
  1130. SASS
  1131. end
  1132. def test_basic_function
  1133. assert_equal(<<CSS, render(<<SASS))
  1134. bar {
  1135. a: 3; }
  1136. CSS
  1137. @function foo()
  1138. @return 1 + 2
  1139. bar
  1140. a: foo()
  1141. SASS
  1142. end
  1143. def test_function_args
  1144. assert_equal(<<CSS, render(<<SASS))
  1145. bar {
  1146. a: 3; }
  1147. CSS
  1148. @function plus($var1, $var2)
  1149. @return $var1 + $var2
  1150. bar
  1151. a: plus(1, 2)
  1152. SASS
  1153. end
  1154. def test_function_arg_default
  1155. assert_equal(<<CSS, render(<<SASS))
  1156. bar {
  1157. a: 3; }
  1158. CSS
  1159. @function plus($var1, $var2: 2)
  1160. @return $var1 + $var2
  1161. bar
  1162. a: plus(1)
  1163. SASS
  1164. end
  1165. def test_function_arg_keyword
  1166. assert_equal(<<CSS, render(<<SASS))
  1167. bar {
  1168. a: 1bar; }
  1169. CSS
  1170. @function plus($var1: 1, $var2: 2)
  1171. @return $var1 + $var2
  1172. bar
  1173. a: plus($var2: bar)
  1174. SASS
  1175. end
  1176. def test_function_with_missing_argument
  1177. render(<<SASS)
  1178. @function plus($var1, $var2)
  1179. @return $var1 + $var2
  1180. bar
  1181. a: plus($var2: bar)
  1182. SASS
  1183. flunk("Expected exception")
  1184. rescue Sass::SyntaxError => e
  1185. assert_equal("Function plus is missing argument $var1.", e.message)
  1186. end
  1187. def test_function_with_extra_argument
  1188. render(<<SASS)
  1189. @function plus($var1, $var2)
  1190. @return $var1 + $var2
  1191. bar
  1192. a: plus($var1: foo, $var2: bar, $var3: baz)
  1193. SASS
  1194. flunk("Expected exception")
  1195. rescue Sass::SyntaxError => e
  1196. assert_equal("Function plus doesn't have an argument named $var3.", e.message)
  1197. end
  1198. def test_function_with_positional_and_keyword_argument
  1199. render(<<SASS)
  1200. @function plus($var1, $var2)
  1201. @return $var1 + $var2
  1202. bar
  1203. a: plus(foo, bar, $var2: baz)
  1204. SASS
  1205. flunk("Expected exception")
  1206. rescue Sass::SyntaxError => e
  1207. assert_equal("Function plus was passed argument $var2 both by position and by name.", e.message)
  1208. end
  1209. def test_function_with_keyword_before_positional_argument
  1210. render(<<SASS)
  1211. @function plus($var1, $var2)
  1212. @return $var1 + $var2
  1213. bar
  1214. a: plus($var2: foo, bar)
  1215. SASS
  1216. flunk("Expected exception")
  1217. rescue Sass::SyntaxError => e
  1218. assert_equal("Positional arguments must come before keyword arguments.", e.message)
  1219. end
  1220. def test_function_with_if
  1221. assert_equal(<<CSS, render(<<SASS))
  1222. bar {
  1223. a: foo;
  1224. b: bar; }
  1225. CSS
  1226. @function my-if($cond, $val1, $val2)
  1227. @if $cond
  1228. @return $val1
  1229. @else
  1230. @return $val2
  1231. bar
  1232. a: my-if(true, foo, bar)
  1233. b: my-if(false, foo, bar)
  1234. SASS
  1235. end
  1236. def test_function_with_var
  1237. assert_equal(<<CSS, render(<<SASS))
  1238. bar {
  1239. a: 1; }
  1240. CSS
  1241. @function foo($val1, $val2)
  1242. $intermediate: $val1 + $val2
  1243. @return $intermediate/3
  1244. bar
  1245. a: foo(1, 2)
  1246. SASS
  1247. end
  1248. def test_control_directive_in_nested_property
  1249. assert_equal(<<CSS, render(<<SASS))
  1250. foo {
  1251. a-b: c; }
  1252. CSS
  1253. foo
  1254. a:
  1255. @if true
  1256. b: c
  1257. SASS
  1258. end
  1259. def test_interpolation
  1260. assert_equal("a-1 {\n b-2-3: c-3; }\n", render(<<SASS))
  1261. $a: 1
  1262. $b: 2
  1263. $c: 3
  1264. a-\#{$a}
  1265. b-\#{$b}-\#{$c}: c-\#{$a + $b}
  1266. SASS
  1267. end
  1268. def test_complex_property_interpolation
  1269. assert_equal(<<CSS, render(<<SASS))
  1270. a-1 {
  1271. b-2 3-fizzap18: c-3; }
  1272. CSS
  1273. $a: 1
  1274. $b: 2
  1275. $c: 3
  1276. a-\#{$a}
  1277. b-\#{$b $c}-\#{fizzap + ($c + 15)}: c-\#{$a + $b}
  1278. SASS
  1279. end
  1280. def test_if_directive
  1281. assert_equal("a {\n b: 1; }\n", render(<<SASS))
  1282. $var: true
  1283. a
  1284. @if $var
  1285. b: 1
  1286. @if not $var
  1287. b: 2
  1288. SASS
  1289. assert_equal("a {\n b: 2; }\n", render(<<SASS))
  1290. $var: null
  1291. a
  1292. @if $var
  1293. b: 1
  1294. @if not $var
  1295. b: 2
  1296. SASS
  1297. end
  1298. def test_for
  1299. assert_equal(<<CSS, render(<<SASS))
  1300. a-0 {
  1301. two-i: 0; }
  1302. a-1 {
  1303. two-i: 2; }
  1304. a-2 {
  1305. two-i: 4; }
  1306. a-3 {
  1307. two-i: 6; }
  1308. b-1 {
  1309. j-1: 0; }
  1310. b-2 {
  1311. j-1: 1; }
  1312. b-3 {
  1313. j-1: 2; }
  1314. b-4 {
  1315. j-1: 3; }
  1316. CSS
  1317. $a: 3
  1318. @for $i from 0 to $a + 1
  1319. a-\#{$i}
  1320. two-i: 2 * $i
  1321. @for $j from 1 through 4
  1322. b-\#{$j}
  1323. j-1: $j - 1
  1324. SASS
  1325. end
  1326. def test_while
  1327. assert_equal(<<CSS, render(<<SASS))
  1328. a-5 {
  1329. blooble: gloop; }
  1330. a-4 {
  1331. blooble: gloop; }
  1332. a-3 {
  1333. blooble: gloop; }
  1334. a-2 {
  1335. blooble: gloop; }
  1336. a-1 {
  1337. blooble: gloop; }
  1338. CSS
  1339. $a: 5
  1340. @while $a != 0
  1341. a-\#{$a}
  1342. blooble: gloop
  1343. $a: $a - 1
  1344. SASS
  1345. end
  1346. def test_else
  1347. assert_equal(<<CSS, render(<<SASS))
  1348. a {
  1349. t1: t;
  1350. t2: t;
  1351. t3: t;
  1352. t4: t; }
  1353. CSS
  1354. a
  1355. @if true
  1356. t1: t
  1357. @else
  1358. f1: f
  1359. @if false
  1360. f2: f
  1361. @else
  1362. t2: t
  1363. @if false
  1364. f3: f1
  1365. @else if 1 + 1 == 3
  1366. f3: f2
  1367. @else
  1368. t3: t
  1369. @if false
  1370. f4: f1
  1371. @else if 1 + 1 == 2
  1372. t4: t
  1373. @else
  1374. f4: f2
  1375. @if false
  1376. f5: f1
  1377. @else if false
  1378. f5: f2
  1379. SASS
  1380. end
  1381. def test_each
  1382. assert_equal(<<CSS, render(<<SASS))
  1383. a {
  1384. b: 1px;
  1385. b: 2px;
  1386. b: 3px;
  1387. b: 4px;
  1388. c: foo;
  1389. c: bar;
  1390. c: baz;
  1391. c: bang;
  1392. d: blue; }
  1393. CSS
  1394. a
  1395. @each $number in 1px 2px 3px 4px
  1396. b: $number
  1397. @each $str in foo, bar, baz, bang
  1398. c: $str
  1399. @each $single in blue
  1400. d: $single
  1401. SASS
  1402. end
  1403. def test_variable_reassignment
  1404. assert_equal(<<CSS, render(<<SASS))
  1405. a {
  1406. b: 1;
  1407. c: 2; }
  1408. CSS
  1409. $a: 1
  1410. a
  1411. b: $a
  1412. $a: 2
  1413. c: $a
  1414. SASS
  1415. end
  1416. def test_variable_scope
  1417. assert_equal(<<CSS, render(<<SASS))
  1418. a {
  1419. b-1: c;
  1420. b-2: c;
  1421. d: 12; }
  1422. b {
  1423. d: 17; }
  1424. CSS
  1425. $i: 12
  1426. a
  1427. @for $i from 1 through 2
  1428. b-\#{$i}: c
  1429. d: $i
  1430. =foo
  1431. $i: 17
  1432. b
  1433. +foo
  1434. d: $i
  1435. SASS
  1436. end
  1437. def test_hyphen_underscore_insensitive_variables
  1438. assert_equal(<<CSS, render(<<SASS))
  1439. a {
  1440. b: c; }
  1441. d {
  1442. e: 13;
  1443. f: foobar; }
  1444. CSS
  1445. $var-hyphen: 12
  1446. $var_under: foo
  1447. a
  1448. $var_hyphen: 1 + $var_hyphen
  1449. $var-under: $var-under + bar
  1450. b: c
  1451. d
  1452. e: $var-hyphen
  1453. f: $var_under
  1454. SASS
  1455. end
  1456. def test_css_identifier_variable
  1457. assert_equal(<<CSS, render(<<SASS))
  1458. a {
  1459. b: 12; }
  1460. CSS
  1461. $\\{foo\\(12\\): 12
  1462. a
  1463. b: $\\{foo\\(12\\)
  1464. SASS
  1465. end
  1466. def test_important
  1467. assert_equal(<<CSS, render(<<SASS))
  1468. a {
  1469. b: 12px !important; }
  1470. CSS
  1471. $foo: 12px
  1472. a
  1473. b: $foo !important
  1474. SASS
  1475. end
  1476. def test_argument_error
  1477. assert_raise(Sass::SyntaxError) { render("a\n b: hsl(1)") }
  1478. end
  1479. def test_comments_at_the_top_of_a_document
  1480. render(<<SASS)
  1481. //
  1482. This is a comment that
  1483. continues to the second line.
  1484. foo
  1485. bar: baz
  1486. SASS
  1487. end
  1488. def test_loud_comments_containing_a_comment_close
  1489. actual_css = render(<<SASS)
  1490. /*
  1491. This is a comment that
  1492. continues to the second line. */
  1493. foo
  1494. bar: baz
  1495. SASS
  1496. assert_equal(<<CSS, actual_css)
  1497. /* This is a comment that
  1498. * continues to the second line. */
  1499. foo {
  1500. bar: baz; }
  1501. CSS
  1502. end
  1503. def test_loud_comments_with_starred_lines