oreorenasass 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (268) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +11 -0
  3. data/CONTRIBUTING +3 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.md +221 -0
  6. data/Rakefile +370 -0
  7. data/VERSION +1 -0
  8. data/VERSION_NAME +1 -0
  9. data/bin/sass +13 -0
  10. data/bin/sass-convert +12 -0
  11. data/bin/scss +13 -0
  12. data/extra/update_watch.rb +13 -0
  13. data/init.rb +18 -0
  14. data/lib/sass/cache_stores/base.rb +88 -0
  15. data/lib/sass/cache_stores/chain.rb +34 -0
  16. data/lib/sass/cache_stores/filesystem.rb +60 -0
  17. data/lib/sass/cache_stores/memory.rb +47 -0
  18. data/lib/sass/cache_stores/null.rb +25 -0
  19. data/lib/sass/cache_stores.rb +15 -0
  20. data/lib/sass/callbacks.rb +67 -0
  21. data/lib/sass/css.rb +407 -0
  22. data/lib/sass/engine.rb +1181 -0
  23. data/lib/sass/environment.rb +191 -0
  24. data/lib/sass/error.rb +198 -0
  25. data/lib/sass/exec/base.rb +187 -0
  26. data/lib/sass/exec/sass_convert.rb +264 -0
  27. data/lib/sass/exec/sass_scss.rb +424 -0
  28. data/lib/sass/exec.rb +9 -0
  29. data/lib/sass/features.rb +47 -0
  30. data/lib/sass/importers/base.rb +182 -0
  31. data/lib/sass/importers/filesystem.rb +211 -0
  32. data/lib/sass/importers.rb +22 -0
  33. data/lib/sass/logger/base.rb +30 -0
  34. data/lib/sass/logger/log_level.rb +45 -0
  35. data/lib/sass/logger.rb +12 -0
  36. data/lib/sass/media.rb +210 -0
  37. data/lib/sass/plugin/compiler.rb +565 -0
  38. data/lib/sass/plugin/configuration.rb +118 -0
  39. data/lib/sass/plugin/generic.rb +15 -0
  40. data/lib/sass/plugin/merb.rb +48 -0
  41. data/lib/sass/plugin/rack.rb +60 -0
  42. data/lib/sass/plugin/rails.rb +47 -0
  43. data/lib/sass/plugin/staleness_checker.rb +199 -0
  44. data/lib/sass/plugin.rb +133 -0
  45. data/lib/sass/railtie.rb +10 -0
  46. data/lib/sass/repl.rb +57 -0
  47. data/lib/sass/root.rb +7 -0
  48. data/lib/sass/script/css_lexer.rb +33 -0
  49. data/lib/sass/script/css_parser.rb +34 -0
  50. data/lib/sass/script/functions.rb +2626 -0
  51. data/lib/sass/script/lexer.rb +449 -0
  52. data/lib/sass/script/parser.rb +637 -0
  53. data/lib/sass/script/tree/funcall.rb +306 -0
  54. data/lib/sass/script/tree/interpolation.rb +118 -0
  55. data/lib/sass/script/tree/list_literal.rb +77 -0
  56. data/lib/sass/script/tree/literal.rb +45 -0
  57. data/lib/sass/script/tree/map_literal.rb +64 -0
  58. data/lib/sass/script/tree/node.rb +109 -0
  59. data/lib/sass/script/tree/operation.rb +103 -0
  60. data/lib/sass/script/tree/selector.rb +26 -0
  61. data/lib/sass/script/tree/string_interpolation.rb +104 -0
  62. data/lib/sass/script/tree/unary_operation.rb +69 -0
  63. data/lib/sass/script/tree/variable.rb +57 -0
  64. data/lib/sass/script/tree.rb +16 -0
  65. data/lib/sass/script/value/arg_list.rb +36 -0
  66. data/lib/sass/script/value/base.rb +240 -0
  67. data/lib/sass/script/value/bool.rb +35 -0
  68. data/lib/sass/script/value/color.rb +680 -0
  69. data/lib/sass/script/value/helpers.rb +262 -0
  70. data/lib/sass/script/value/list.rb +113 -0
  71. data/lib/sass/script/value/map.rb +70 -0
  72. data/lib/sass/script/value/null.rb +44 -0
  73. data/lib/sass/script/value/number.rb +530 -0
  74. data/lib/sass/script/value/string.rb +97 -0
  75. data/lib/sass/script/value.rb +11 -0
  76. data/lib/sass/script.rb +66 -0
  77. data/lib/sass/scss/css_parser.rb +42 -0
  78. data/lib/sass/scss/parser.rb +1209 -0
  79. data/lib/sass/scss/rx.rb +141 -0
  80. data/lib/sass/scss/script_lexer.rb +15 -0
  81. data/lib/sass/scss/script_parser.rb +25 -0
  82. data/lib/sass/scss/static_parser.rb +368 -0
  83. data/lib/sass/scss.rb +16 -0
  84. data/lib/sass/selector/abstract_sequence.rb +109 -0
  85. data/lib/sass/selector/comma_sequence.rb +175 -0
  86. data/lib/sass/selector/pseudo.rb +256 -0
  87. data/lib/sass/selector/sequence.rb +600 -0
  88. data/lib/sass/selector/simple.rb +117 -0
  89. data/lib/sass/selector/simple_sequence.rb +325 -0
  90. data/lib/sass/selector.rb +326 -0
  91. data/lib/sass/shared.rb +76 -0
  92. data/lib/sass/source/map.rb +210 -0
  93. data/lib/sass/source/position.rb +39 -0
  94. data/lib/sass/source/range.rb +41 -0
  95. data/lib/sass/stack.rb +120 -0
  96. data/lib/sass/supports.rb +227 -0
  97. data/lib/sass/tree/at_root_node.rb +83 -0
  98. data/lib/sass/tree/charset_node.rb +22 -0
  99. data/lib/sass/tree/comment_node.rb +82 -0
  100. data/lib/sass/tree/content_node.rb +9 -0
  101. data/lib/sass/tree/css_import_node.rb +60 -0
  102. data/lib/sass/tree/debug_node.rb +18 -0
  103. data/lib/sass/tree/directive_node.rb +59 -0
  104. data/lib/sass/tree/each_node.rb +24 -0
  105. data/lib/sass/tree/error_node.rb +18 -0
  106. data/lib/sass/tree/extend_node.rb +43 -0
  107. data/lib/sass/tree/for_node.rb +36 -0
  108. data/lib/sass/tree/function_node.rb +39 -0
  109. data/lib/sass/tree/if_node.rb +52 -0
  110. data/lib/sass/tree/import_node.rb +74 -0
  111. data/lib/sass/tree/keyframe_rule_node.rb +15 -0
  112. data/lib/sass/tree/media_node.rb +48 -0
  113. data/lib/sass/tree/mixin_def_node.rb +38 -0
  114. data/lib/sass/tree/mixin_node.rb +52 -0
  115. data/lib/sass/tree/node.rb +238 -0
  116. data/lib/sass/tree/prop_node.rb +171 -0
  117. data/lib/sass/tree/return_node.rb +19 -0
  118. data/lib/sass/tree/root_node.rb +44 -0
  119. data/lib/sass/tree/rule_node.rb +145 -0
  120. data/lib/sass/tree/supports_node.rb +38 -0
  121. data/lib/sass/tree/trace_node.rb +33 -0
  122. data/lib/sass/tree/variable_node.rb +36 -0
  123. data/lib/sass/tree/visitors/base.rb +72 -0
  124. data/lib/sass/tree/visitors/check_nesting.rb +177 -0
  125. data/lib/sass/tree/visitors/convert.rb +334 -0
  126. data/lib/sass/tree/visitors/cssize.rb +369 -0
  127. data/lib/sass/tree/visitors/deep_copy.rb +107 -0
  128. data/lib/sass/tree/visitors/extend.rb +68 -0
  129. data/lib/sass/tree/visitors/perform.rb +539 -0
  130. data/lib/sass/tree/visitors/set_options.rb +139 -0
  131. data/lib/sass/tree/visitors/to_css.rb +381 -0
  132. data/lib/sass/tree/warn_node.rb +18 -0
  133. data/lib/sass/tree/while_node.rb +18 -0
  134. data/lib/sass/util/cross_platform_random.rb +19 -0
  135. data/lib/sass/util/multibyte_string_scanner.rb +157 -0
  136. data/lib/sass/util/normalized_map.rb +130 -0
  137. data/lib/sass/util/ordered_hash.rb +192 -0
  138. data/lib/sass/util/subset_map.rb +110 -0
  139. data/lib/sass/util/test.rb +9 -0
  140. data/lib/sass/util.rb +1318 -0
  141. data/lib/sass/version.rb +124 -0
  142. data/lib/sass.rb +102 -0
  143. data/rails/init.rb +1 -0
  144. data/test/sass/cache_test.rb +131 -0
  145. data/test/sass/callbacks_test.rb +61 -0
  146. data/test/sass/compiler_test.rb +232 -0
  147. data/test/sass/conversion_test.rb +2054 -0
  148. data/test/sass/css2sass_test.rb +477 -0
  149. data/test/sass/data/hsl-rgb.txt +319 -0
  150. data/test/sass/encoding_test.rb +219 -0
  151. data/test/sass/engine_test.rb +3301 -0
  152. data/test/sass/exec_test.rb +86 -0
  153. data/test/sass/extend_test.rb +1661 -0
  154. data/test/sass/fixtures/test_staleness_check_across_importers.css +1 -0
  155. data/test/sass/fixtures/test_staleness_check_across_importers.scss +1 -0
  156. data/test/sass/functions_test.rb +1926 -0
  157. data/test/sass/importer_test.rb +412 -0
  158. data/test/sass/logger_test.rb +58 -0
  159. data/test/sass/mock_importer.rb +49 -0
  160. data/test/sass/more_results/more1.css +9 -0
  161. data/test/sass/more_results/more1_with_line_comments.css +26 -0
  162. data/test/sass/more_results/more_import.css +29 -0
  163. data/test/sass/more_templates/_more_partial.sass +2 -0
  164. data/test/sass/more_templates/more1.sass +23 -0
  165. data/test/sass/more_templates/more_import.sass +11 -0
  166. data/test/sass/plugin_test.rb +554 -0
  167. data/test/sass/results/alt.css +4 -0
  168. data/test/sass/results/basic.css +9 -0
  169. data/test/sass/results/cached_import_option.css +3 -0
  170. data/test/sass/results/compact.css +5 -0
  171. data/test/sass/results/complex.css +86 -0
  172. data/test/sass/results/compressed.css +1 -0
  173. data/test/sass/results/expanded.css +19 -0
  174. data/test/sass/results/filename_fn.css +3 -0
  175. data/test/sass/results/if.css +3 -0
  176. data/test/sass/results/import.css +31 -0
  177. data/test/sass/results/import_charset.css +5 -0
  178. data/test/sass/results/import_charset_1_8.css +5 -0
  179. data/test/sass/results/import_charset_ibm866.css +5 -0
  180. data/test/sass/results/import_content.css +1 -0
  181. data/test/sass/results/line_numbers.css +49 -0
  182. data/test/sass/results/mixins.css +95 -0
  183. data/test/sass/results/multiline.css +24 -0
  184. data/test/sass/results/nested.css +22 -0
  185. data/test/sass/results/options.css +1 -0
  186. data/test/sass/results/parent_ref.css +13 -0
  187. data/test/sass/results/script.css +16 -0
  188. data/test/sass/results/scss_import.css +31 -0
  189. data/test/sass/results/scss_importee.css +2 -0
  190. data/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
  191. data/test/sass/results/subdir/subdir.css +3 -0
  192. data/test/sass/results/units.css +11 -0
  193. data/test/sass/results/warn.css +0 -0
  194. data/test/sass/results/warn_imported.css +0 -0
  195. data/test/sass/script_conversion_test.rb +328 -0
  196. data/test/sass/script_test.rb +1054 -0
  197. data/test/sass/scss/css_test.rb +1215 -0
  198. data/test/sass/scss/rx_test.rb +156 -0
  199. data/test/sass/scss/scss_test.rb +3900 -0
  200. data/test/sass/scss/test_helper.rb +37 -0
  201. data/test/sass/source_map_test.rb +977 -0
  202. data/test/sass/superselector_test.rb +191 -0
  203. data/test/sass/templates/_cached_import_option_partial.scss +1 -0
  204. data/test/sass/templates/_double_import_loop2.sass +1 -0
  205. data/test/sass/templates/_filename_fn_import.scss +11 -0
  206. data/test/sass/templates/_imported_charset_ibm866.sass +4 -0
  207. data/test/sass/templates/_imported_charset_utf8.sass +4 -0
  208. data/test/sass/templates/_imported_content.sass +3 -0
  209. data/test/sass/templates/_partial.sass +2 -0
  210. data/test/sass/templates/_same_name_different_partiality.scss +1 -0
  211. data/test/sass/templates/alt.sass +16 -0
  212. data/test/sass/templates/basic.sass +23 -0
  213. data/test/sass/templates/bork1.sass +2 -0
  214. data/test/sass/templates/bork2.sass +2 -0
  215. data/test/sass/templates/bork3.sass +2 -0
  216. data/test/sass/templates/bork4.sass +2 -0
  217. data/test/sass/templates/bork5.sass +3 -0
  218. data/test/sass/templates/cached_import_option.scss +3 -0
  219. data/test/sass/templates/compact.sass +17 -0
  220. data/test/sass/templates/complex.sass +305 -0
  221. data/test/sass/templates/compressed.sass +15 -0
  222. data/test/sass/templates/double_import_loop1.sass +1 -0
  223. data/test/sass/templates/expanded.sass +17 -0
  224. data/test/sass/templates/filename_fn.scss +18 -0
  225. data/test/sass/templates/if.sass +11 -0
  226. data/test/sass/templates/import.sass +12 -0
  227. data/test/sass/templates/import_charset.sass +9 -0
  228. data/test/sass/templates/import_charset_1_8.sass +6 -0
  229. data/test/sass/templates/import_charset_ibm866.sass +11 -0
  230. data/test/sass/templates/import_content.sass +4 -0
  231. data/test/sass/templates/importee.less +2 -0
  232. data/test/sass/templates/importee.sass +19 -0
  233. data/test/sass/templates/line_numbers.sass +13 -0
  234. data/test/sass/templates/mixin_bork.sass +5 -0
  235. data/test/sass/templates/mixins.sass +76 -0
  236. data/test/sass/templates/multiline.sass +20 -0
  237. data/test/sass/templates/nested.sass +25 -0
  238. data/test/sass/templates/nested_bork1.sass +2 -0
  239. data/test/sass/templates/nested_bork2.sass +2 -0
  240. data/test/sass/templates/nested_bork3.sass +2 -0
  241. data/test/sass/templates/nested_bork4.sass +2 -0
  242. data/test/sass/templates/nested_import.sass +2 -0
  243. data/test/sass/templates/nested_mixin_bork.sass +6 -0
  244. data/test/sass/templates/options.sass +2 -0
  245. data/test/sass/templates/parent_ref.sass +25 -0
  246. data/test/sass/templates/same_name_different_ext.sass +2 -0
  247. data/test/sass/templates/same_name_different_ext.scss +1 -0
  248. data/test/sass/templates/same_name_different_partiality.scss +1 -0
  249. data/test/sass/templates/script.sass +101 -0
  250. data/test/sass/templates/scss_import.scss +12 -0
  251. data/test/sass/templates/scss_importee.scss +1 -0
  252. data/test/sass/templates/single_import_loop.sass +1 -0
  253. data/test/sass/templates/subdir/import_up1.scss +1 -0
  254. data/test/sass/templates/subdir/import_up2.scss +1 -0
  255. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
  256. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
  257. data/test/sass/templates/subdir/subdir.sass +6 -0
  258. data/test/sass/templates/units.sass +11 -0
  259. data/test/sass/templates/warn.sass +3 -0
  260. data/test/sass/templates/warn_imported.sass +4 -0
  261. data/test/sass/test_helper.rb +8 -0
  262. data/test/sass/util/multibyte_string_scanner_test.rb +147 -0
  263. data/test/sass/util/normalized_map_test.rb +51 -0
  264. data/test/sass/util/subset_map_test.rb +91 -0
  265. data/test/sass/util_test.rb +467 -0
  266. data/test/sass/value_helpers_test.rb +179 -0
  267. data/test/test_helper.rb +109 -0
  268. metadata +386 -0
@@ -0,0 +1,3301 @@
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
+
10
+ module Sass::Script::Functions::UserFunctions
11
+ def option(name)
12
+ Sass::Script::Value::String.new(@options[name.value.to_sym].to_s)
13
+ end
14
+
15
+ def set_a_variable(name, value)
16
+ environment.set_var(name.value, value)
17
+ return Sass::Script::Value::Null.new
18
+ end
19
+
20
+ def set_a_global_variable(name, value)
21
+ environment.set_global_var(name.value, value)
22
+ return Sass::Script::Value::Null.new
23
+ end
24
+
25
+ def get_a_variable(name)
26
+ environment.var(name.value) || Sass::Script::Value::String.new("undefined")
27
+ end
28
+ end
29
+
30
+ module Sass::Script::Functions
31
+ include Sass::Script::Functions::UserFunctions
32
+ end
33
+
34
+ class SassEngineTest < MiniTest::Test
35
+ FAKE_FILE_NAME = __FILE__.gsub(/rb$/,"sass")
36
+ # A map of erroneous Sass documents to the error messages they should produce.
37
+ # The error messages may be arrays;
38
+ # if so, the second element should be the line number that should be reported for the error.
39
+ # If this isn't provided, the tests will assume the line number should be the last line of the document.
40
+ EXCEPTION_MAP = {
41
+ "$a: 1 + " => 'Invalid CSS after "1 +": expected expression (e.g. 1px, bold), was ""',
42
+ "$a: 1 + 2 +" => 'Invalid CSS after "1 + 2 +": expected expression (e.g. 1px, bold), was ""',
43
+ "$a: 1 + 2 + %" => 'Invalid CSS after "1 + 2 + ": expected expression (e.g. 1px, bold), was "%"',
44
+ "$a: foo(\"bar\"" => 'Invalid CSS after "foo("bar"": expected ")", was ""',
45
+ "$a: 1 }" => 'Invalid CSS after "1 ": expected expression (e.g. 1px, bold), was "}"',
46
+ "$a: 1 }foo\"" => 'Invalid CSS after "1 ": expected expression (e.g. 1px, bold), was "}foo""',
47
+ ":" => 'Invalid property: ":".',
48
+ ": a" => 'Invalid property: ": a".',
49
+ "a\n :b" => <<MSG,
50
+ Invalid property: ":b" (no value).
51
+ If ":b" should be a selector, use "\\:b" instead.
52
+ MSG
53
+ "a\n b:" => 'Invalid property: "b:" (no value).',
54
+ "a\n :b: c" => 'Invalid property: ":b: c".',
55
+ "a\n :b:c d" => 'Invalid property: ":b:c d".',
56
+ "a\n :b c;" => 'Invalid CSS after "c": expected expression (e.g. 1px, bold), was ";"',
57
+ "a\n b: c;" => 'Invalid CSS after "c": expected expression (e.g. 1px, bold), was ";"',
58
+ ".foo ^bar\n a: b" => ['Invalid CSS after ".foo ": expected selector, was "^bar"', 1],
59
+ "a\n @extend .foo ^bar" => 'Invalid CSS after ".foo ": expected selector, was "^bar"',
60
+ "a\n @extend .foo .bar" => "Can't extend .foo .bar: can't extend nested selectors",
61
+ "a\n @extend >" => "Can't extend >: invalid selector",
62
+ "a\n @extend &.foo" => "Can't extend &.foo: can't extend parent selectors",
63
+ "a: b" => 'Properties are only allowed within rules, directives, mixin includes, or other properties.',
64
+ ":a b" => 'Properties are only allowed within rules, directives, mixin includes, or other properties.',
65
+ "$" => 'Invalid variable: "$".',
66
+ "$a" => 'Invalid variable: "$a".',
67
+ "$ a" => 'Invalid variable: "$ a".',
68
+ "$a b" => 'Invalid variable: "$a b".',
69
+ "$a: 1b + 2c" => "Incompatible units: 'c' and 'b'.",
70
+ "$a: 1b < 2c" => "Incompatible units: 'c' and 'b'.",
71
+ "$a: 1b > 2c" => "Incompatible units: 'c' and 'b'.",
72
+ "$a: 1b <= 2c" => "Incompatible units: 'c' and 'b'.",
73
+ "$a: 1b >= 2c" => "Incompatible units: 'c' and 'b'.",
74
+ "a\n b: 1b * 2c" => "2b*c isn't a valid CSS value.",
75
+ "a\n b: 1b % 2c" => "Incompatible units: 'c' and 'b'.",
76
+ "$a: 2px + #ccc" => "Cannot add a number with units (2px) to a color (#ccc).",
77
+ "$a: #ccc + 2px" => "Cannot add a number with units (2px) to a color (#ccc).",
78
+ "& a\n :b c" => ["Base-level rules cannot contain the parent-selector-referencing character '&'.", 1],
79
+ "a\n :b\n c" => "Illegal nesting: Only properties may be nested beneath properties.",
80
+ "$a: b\n :c d\n" => "Illegal nesting: Nothing may be nested beneath variable declarations.",
81
+ "$a: b\n :c d\n" => "Illegal nesting: Nothing may be nested beneath variable declarations.",
82
+ "@import templates/basic\n foo" => "Illegal nesting: Nothing may be nested beneath import directives.",
83
+ "foo\n @import foo.css" => "CSS import directives may only be used at the root of a document.",
84
+ "@if true\n @import foo" => "Import directives may not be used within control directives or mixins.",
85
+ "@if true\n .foo\n @import foo" => "Import directives may not be used within control directives or mixins.",
86
+ "@mixin foo\n @import foo" => "Import directives may not be used within control directives or mixins.",
87
+ "@mixin foo\n .foo\n @import foo" => "Import directives may not be used within control directives or mixins.",
88
+ "@import foo;" => "Invalid @import: expected end of line, was \";\".",
89
+ '$foo: "bar" "baz" !' => %Q{Invalid CSS after ""bar" "baz" ": expected expression (e.g. 1px, bold), was "!"},
90
+ '$foo: "bar" "baz" $' => %Q{Invalid CSS after ""bar" "baz" ": expected expression (e.g. 1px, bold), was "$"}, #'
91
+ "=foo\n :color red\n.bar\n +bang" => "Undefined mixin 'bang'.",
92
+ "=foo\n :color red\n.bar\n +bang_bop" => "Undefined mixin 'bang_bop'.",
93
+ "=foo\n :color red\n.bar\n +bang-bop" => "Undefined mixin 'bang-bop'.",
94
+ ".foo\n =foo\n :color red\n.bar\n +foo" => "Undefined mixin 'foo'.",
95
+ " a\n b: c" => ["Indenting at the beginning of the document is illegal.", 1],
96
+ " \n \n\t\n a\n b: c" => ["Indenting at the beginning of the document is illegal.", 4],
97
+ "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],
98
+ "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],
99
+ "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],
100
+ "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],
101
+ "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],
102
+ "a\n b: c\na\n d: e" => ["The line was indented 2 levels deeper than the previous line.", 4],
103
+ "a\n b: c\n a\n d: e" => ["The line was indented 3 levels deeper than the previous line.", 4],
104
+ "a\n \tb: c" => ["Indentation can't use both tabs and spaces.", 2],
105
+ "=a(" => 'Invalid CSS after "(": expected variable (e.g. $foo), was ""',
106
+ "=a(b)" => 'Invalid CSS after "(": expected variable (e.g. $foo), was "b)"',
107
+ "=a(,)" => 'Invalid CSS after "(": expected variable (e.g. $foo), was ",)"',
108
+ "=a($)" => 'Invalid CSS after "(": expected variable (e.g. $foo), was "$)"',
109
+ "=a($foo bar)" => 'Invalid CSS after "($foo ": expected ")", was "bar)"',
110
+ "=foo\n bar: baz\n+foo" => ["Properties are only allowed within rules, directives, mixin includes, or other properties.", 2],
111
+ "a-\#{$b\n c: d" => ['Invalid CSS after "a-#{$b": expected "}", was ""', 1],
112
+ "=a($b: 1, $c)" => "Required argument $c must come before any optional arguments.",
113
+ "=a($b: 1)\n a: $b\ndiv\n +a(1,2)" => "Mixin a takes 1 argument but 2 were passed.",
114
+ "=a($b: 1)\n a: $b\ndiv\n +a(1,$c: 3)" => "Mixin a doesn't have an argument named $c.",
115
+ "=a($b)\n a: $b\ndiv\n +a" => "Mixin a is missing argument $b.",
116
+ "@function foo()\n 1 + 2" => "Functions can only contain variable declarations and control directives.",
117
+ "@function foo()\n foo: bar" => "Functions can only contain variable declarations and control directives.",
118
+ "@function foo()\n foo: bar\n @return 3" => ["Functions can only contain variable declarations and control directives.", 2],
119
+ "@function foo\n @return 1" => ['Invalid CSS after "": expected "(", was ""', 1],
120
+ "@function foo(\n @return 1" => ['Invalid CSS after "(": expected variable (e.g. $foo), was ""', 1],
121
+ "@function foo(b)\n @return 1" => ['Invalid CSS after "(": expected variable (e.g. $foo), was "b)"', 1],
122
+ "@function foo(,)\n @return 1" => ['Invalid CSS after "(": expected variable (e.g. $foo), was ",)"', 1],
123
+ "@function foo($)\n @return 1" => ['Invalid CSS after "(": expected variable (e.g. $foo), was "$)"', 1],
124
+ "@function foo()\n @return" => 'Invalid @return: expected expression.',
125
+ "@function foo()\n @return 1\n $var: val" => 'Illegal nesting: Nothing may be nested beneath return directives.',
126
+ "@function foo($a)\n @return 1\na\n b: foo()" => 'Function foo is missing argument $a.',
127
+ "@function foo()\n @return 1\na\n b: foo(2)" => 'Function foo takes 0 arguments but 1 was passed.',
128
+ "@function foo()\n @return 1\na\n b: foo($a: 1)" => "Function foo doesn't have an argument named $a.",
129
+ "@function foo()\n @return 1\na\n b: foo($a: 1, $b: 2)" => "Function foo doesn't have the following arguments: $a, $b.",
130
+ "@return 1" => '@return may only be used within a function.',
131
+ "@if true\n @return 1" => '@return may only be used within a function.',
132
+ "@mixin foo\n @return 1\n@include foo" => ['@return may only be used within a function.', 2],
133
+ "@else\n a\n b: c" => ["@else must come after @if.", 1],
134
+ "@if false\n@else foo" => "Invalid else directive '@else foo': expected 'if <expr>'.",
135
+ "@if false\n@else if " => "Invalid else directive '@else if': expected 'if <expr>'.",
136
+ "a\n $b: 12\nc\n d: $b" => 'Undefined variable: "$b".',
137
+ "=foo\n $b: 12\nc\n +foo\n d: $b" => 'Undefined variable: "$b".',
138
+ "c\n d: $b-foo" => 'Undefined variable: "$b-foo".',
139
+ "c\n d: $b_foo" => 'Undefined variable: "$b_foo".',
140
+ '@for $a from "foo" to 1' => '"foo" is not an integer.',
141
+ '@for $a from 1 to "2"' => '"2" is not an integer.',
142
+ '@for $a from 1 to "foo"' => '"foo" is not an integer.',
143
+ '@for $a from 1 to 1.232323' => '1.23232 is not an integer.',
144
+ '@for $a from 1px to 3em' => "Incompatible units: 'em' and 'px'.",
145
+ '@if' => "Invalid if directive '@if': expected expression.",
146
+ '@while' => "Invalid while directive '@while': expected expression.",
147
+ '@debug' => "Invalid debug directive '@debug': expected expression.",
148
+ %Q{@debug "a message"\n "nested message"} => "Illegal nesting: Nothing may be nested beneath debug directives.",
149
+ '@error' => "Invalid error directive '@error': expected expression.",
150
+ %Q{@error "a message"\n "nested message"} => "Illegal nesting: Nothing may be nested beneath error directives.",
151
+ '@warn' => "Invalid warn directive '@warn': expected expression.",
152
+ %Q{@warn "a message"\n "nested message"} => "Illegal nesting: Nothing may be nested beneath warn directives.",
153
+ "/* foo\n bar\n baz" => "Inconsistent indentation: previous line was indented by 4 spaces, but this line was indented by 2 spaces.",
154
+ '+foo(1 + 1: 2)' => 'Invalid CSS after "(1 + 1": expected comma, was ": 2)"',
155
+ '+foo($var: )' => 'Invalid CSS after "($var: ": expected mixin argument, was ")"',
156
+ '+foo($var: a, $var: b)' => 'Keyword argument "$var" passed more than once',
157
+ '+foo($var-var: a, $var_var: b)' => 'Keyword argument "$var_var" passed more than once',
158
+ '+foo($var_var: a, $var-var: b)' => 'Keyword argument "$var-var" passed more than once',
159
+ "a\n b: foo(1 + 1: 2)" => 'Invalid CSS after "foo(1 + 1": expected comma, was ": 2)"',
160
+ "a\n b: foo($var: )" => 'Invalid CSS after "foo($var: ": expected function argument, was ")"',
161
+ "a\n b: foo($var: a, $var: b)" => 'Keyword argument "$var" passed more than once',
162
+ "a\n b: foo($var-var: a, $var_var: b)" => 'Keyword argument "$var_var" passed more than once',
163
+ "a\n b: foo($var_var: a, $var-var: b)" => 'Keyword argument "$var-var" passed more than once',
164
+ "@if foo\n @extend .bar" => ["Extend directives may only be used within rules.", 2],
165
+ "$var: true\n@while $var\n @extend .bar\n $var: false" => ["Extend directives may only be used within rules.", 3],
166
+ "@for $i from 0 to 1\n @extend .bar" => ["Extend directives may only be used within rules.", 2],
167
+ "@mixin foo\n @extend .bar\n@include foo" => ["Extend directives may only be used within rules.", 2],
168
+ "foo %\n a: b" => ['Invalid CSS after "foo %": expected placeholder name, was ""', 1],
169
+ "=foo\n @content error" => "Invalid content directive. Trailing characters found: \"error\".",
170
+ "=foo\n @content\n b: c" => "Illegal nesting: Nothing may be nested beneath @content directives.",
171
+ "@content" => '@content may only be used within a mixin.',
172
+ "=simple\n .simple\n color: red\n+simple\n color: blue" => ['Mixin "simple" does not accept a content block.', 4],
173
+ "@import \"foo\" // bar" => "Invalid CSS after \"\"foo\" \": expected media query list, was \"// bar\"",
174
+ "@at-root\n a: b" => "Properties are only allowed within rules, directives, mixin includes, or other properties.",
175
+
176
+ # Regression tests
177
+ "a\n b:\n c\n d" => ["Illegal nesting: Only properties may be nested beneath properties.", 3],
178
+ "& foo\n bar: baz\n blat: bang" => ["Base-level rules cannot contain the parent-selector-referencing character '&'.", 1],
179
+ "a\n b: c\n& foo\n bar: baz\n blat: bang" => ["Base-level rules cannot contain the parent-selector-referencing character '&'.", 3],
180
+ "@" => "Invalid directive: '@'.",
181
+ }
182
+
183
+ def teardown
184
+ clean_up_sassc
185
+ end
186
+
187
+ def test_basic_render
188
+ renders_correctly "basic", { :style => :compact }
189
+ end
190
+
191
+ def test_empty_render
192
+ assert_equal "", render("")
193
+ end
194
+
195
+ def test_multiple_calls_to_render
196
+ sass = Sass::Engine.new("a\n b: c")
197
+ assert_equal sass.render, sass.render
198
+ end
199
+
200
+ def test_alternate_styles
201
+ renders_correctly "expanded", { :style => :expanded }
202
+ renders_correctly "compact", { :style => :compact }
203
+ renders_correctly "nested", { :style => :nested }
204
+ renders_correctly "compressed", { :style => :compressed }
205
+ end
206
+
207
+ def test_compile
208
+ assert_equal "div { hello: world; }\n", Sass.compile("$who: world\ndiv\n hello: $who", :syntax => :sass, :style => :compact)
209
+ assert_equal "div { hello: world; }\n", Sass.compile("$who: world; div { hello: $who }", :style => :compact)
210
+ end
211
+
212
+ def test_compile_file
213
+ FileUtils.mkdir_p(absolutize("tmp"))
214
+ open(absolutize("tmp/test_compile_file.sass"), "w") {|f| f.write("$who: world\ndiv\n hello: $who")}
215
+ open(absolutize("tmp/test_compile_file.scss"), "w") {|f| f.write("$who: world; div { hello: $who }")}
216
+ assert_equal "div { hello: world; }\n", Sass.compile_file(absolutize("tmp/test_compile_file.sass"), :style => :compact)
217
+ assert_equal "div { hello: world; }\n", Sass.compile_file(absolutize("tmp/test_compile_file.scss"), :style => :compact)
218
+ ensure
219
+ FileUtils.rm_rf(absolutize("tmp"))
220
+ end
221
+
222
+ def test_compile_file_to_css_file
223
+ FileUtils.mkdir_p(absolutize("tmp"))
224
+ open(absolutize("tmp/test_compile_file.sass"), "w") {|f| f.write("$who: world\ndiv\n hello: $who")}
225
+ open(absolutize("tmp/test_compile_file.scss"), "w") {|f| f.write("$who: world; div { hello: $who }")}
226
+ Sass.compile_file(absolutize("tmp/test_compile_file.sass"), absolutize("tmp/test_compile_file_sass.css"), :style => :compact)
227
+ Sass.compile_file(absolutize("tmp/test_compile_file.scss"), absolutize("tmp/test_compile_file_scss.css"), :style => :compact)
228
+ assert_equal "div { hello: world; }\n", File.read(absolutize("tmp/test_compile_file_sass.css"))
229
+ assert_equal "div { hello: world; }\n", File.read(absolutize("tmp/test_compile_file_scss.css"))
230
+ ensure
231
+ FileUtils.rm_rf(absolutize("tmp"))
232
+ end
233
+
234
+ def test_flexible_tabulation
235
+ assert_equal("p {\n a: b; }\n p q {\n c: d; }\n",
236
+ render("p\n a: b\n q\n c: d\n"))
237
+ assert_equal("p {\n a: b; }\n p q {\n c: d; }\n",
238
+ render("p\n\ta: b\n\tq\n\t\tc: d\n"))
239
+ end
240
+
241
+ def test_import_same_name_different_ext
242
+ assert_raise_message Sass::SyntaxError, <<ERROR do
243
+ It's not clear which file to import for '@import "same_name_different_ext"'.
244
+ Candidates:
245
+ same_name_different_ext.sass
246
+ same_name_different_ext.scss
247
+ Please delete or rename all but one of these files.
248
+ ERROR
249
+ options = {:load_paths => [File.dirname(__FILE__) + '/templates/']}
250
+ munge_filename options
251
+ Sass::Engine.new("@import 'same_name_different_ext'", options).render
252
+ end
253
+ end
254
+
255
+ def test_import_same_name_different_partiality
256
+ assert_raise_message Sass::SyntaxError, <<ERROR do
257
+ It's not clear which file to import for '@import "same_name_different_partiality"'.
258
+ Candidates:
259
+ _same_name_different_partiality.scss
260
+ same_name_different_partiality.scss
261
+ Please delete or rename all but one of these files.
262
+ ERROR
263
+ options = {:load_paths => [File.dirname(__FILE__) + '/templates/']}
264
+ munge_filename options
265
+ Sass::Engine.new("@import 'same_name_different_partiality'", options).render
266
+ end
267
+ end
268
+
269
+ EXCEPTION_MAP.each do |key, value|
270
+ define_method("test_exception (#{key.inspect})") do
271
+ line = 10
272
+ begin
273
+ silence_warnings {Sass::Engine.new(key, :filename => FAKE_FILE_NAME, :line => line).render}
274
+ rescue Sass::SyntaxError => err
275
+ value = [value] unless value.is_a?(Array)
276
+
277
+ assert_equal(value.first.rstrip, err.message, "Line: #{key}")
278
+ assert_equal(FAKE_FILE_NAME, err.sass_filename)
279
+ assert_equal((value[1] || key.split("\n").length) + line - 1, err.sass_line, "Line: #{key}")
280
+ assert_match(/#{Regexp.escape(FAKE_FILE_NAME)}:[0-9]+/, err.backtrace[0], "Line: #{key}")
281
+ else
282
+ assert(false, "Exception not raised for\n#{key}")
283
+ end
284
+ end
285
+ end
286
+
287
+ def test_exception_line
288
+ to_render = <<SASS
289
+ rule
290
+ :prop val
291
+ // comment!
292
+
293
+ :broken
294
+ SASS
295
+ begin
296
+ Sass::Engine.new(to_render).render
297
+ rescue Sass::SyntaxError => err
298
+ assert_equal(5, err.sass_line)
299
+ else
300
+ assert(false, "Exception not raised for '#{to_render}'!")
301
+ end
302
+ end
303
+
304
+ def test_exception_location
305
+ to_render = <<SASS
306
+ rule
307
+ :prop val
308
+ // comment!
309
+
310
+ :broken
311
+ SASS
312
+ begin
313
+ Sass::Engine.new(to_render, :filename => FAKE_FILE_NAME, :line => (__LINE__-7)).render
314
+ rescue Sass::SyntaxError => err
315
+ assert_equal(FAKE_FILE_NAME, err.sass_filename)
316
+ assert_equal((__LINE__-6), err.sass_line)
317
+ else
318
+ assert(false, "Exception not raised for '#{to_render}'!")
319
+ end
320
+ end
321
+
322
+ def test_imported_exception
323
+ [1, 2, 3, 4].each do |i|
324
+ begin
325
+ Sass::Engine.new("@import bork#{i}", :load_paths => [File.dirname(__FILE__) + '/templates/']).render
326
+ rescue Sass::SyntaxError => err
327
+ assert_equal(2, err.sass_line)
328
+ assert_match(/(\/|^)bork#{i}\.sass$/, err.sass_filename)
329
+
330
+ assert_hash_has(err.sass_backtrace.first,
331
+ :filename => err.sass_filename, :line => err.sass_line)
332
+
333
+ assert_nil(err.sass_backtrace[1][:filename])
334
+ assert_equal(1, err.sass_backtrace[1][:line])
335
+
336
+ assert_match(/(\/|^)bork#{i}\.sass:2$/, err.backtrace.first)
337
+ assert_equal("(sass):1", err.backtrace[1])
338
+ else
339
+ assert(false, "Exception not raised for imported template: bork#{i}")
340
+ end
341
+ end
342
+ end
343
+
344
+ def test_double_imported_exception
345
+ [1, 2, 3, 4].each do |i|
346
+ begin
347
+ Sass::Engine.new("@import nested_bork#{i}", :load_paths => [File.dirname(__FILE__) + '/templates/']).render
348
+ rescue Sass::SyntaxError => err
349
+ assert_equal(2, err.sass_line)
350
+ assert_match(/(\/|^)bork#{i}\.sass$/, err.sass_filename)
351
+
352
+ assert_hash_has(err.sass_backtrace.first,
353
+ :filename => err.sass_filename, :line => err.sass_line)
354
+
355
+ assert_match(/(\/|^)nested_bork#{i}\.sass$/, err.sass_backtrace[1][:filename])
356
+ assert_equal(2, err.sass_backtrace[1][:line])
357
+
358
+ assert_nil(err.sass_backtrace[2][:filename])
359
+ assert_equal(1, err.sass_backtrace[2][:line])
360
+
361
+ assert_match(/(\/|^)bork#{i}\.sass:2$/, err.backtrace.first)
362
+ assert_match(/(\/|^)nested_bork#{i}\.sass:2$/, err.backtrace[1])
363
+ assert_equal("(sass):1", err.backtrace[2])
364
+ else
365
+ assert(false, "Exception not raised for imported template: bork#{i}")
366
+ end
367
+ end
368
+ end
369
+
370
+ def test_selector_tracing
371
+ actual_css = render(<<-SCSS, :syntax => :scss, :trace_selectors => true)
372
+ @mixin mixed {
373
+ .mixed { color: red; }
374
+ }
375
+ .context {
376
+ @include mixed;
377
+ }
378
+ SCSS
379
+ assert_equal(<<CSS,actual_css)
380
+ /* on line 2 of test_selector_tracing_inline.scss, in `mixed'
381
+ from line 5 of test_selector_tracing_inline.scss */
382
+ .context .mixed {
383
+ color: red; }
384
+ CSS
385
+ end
386
+
387
+ def test_mixin_exception
388
+ render(<<SASS)
389
+ =error-mixin($a)
390
+ color: $a * 1em * 1px
391
+
392
+ =outer-mixin($a)
393
+ +error-mixin($a)
394
+
395
+ .error
396
+ +outer-mixin(12)
397
+ SASS
398
+ assert(false, "Exception not raised")
399
+ rescue Sass::SyntaxError => err
400
+ assert_equal(2, err.sass_line)
401
+ assert_equal(filename_for_test, err.sass_filename)
402
+ assert_equal("error-mixin", err.sass_mixin)
403
+
404
+ assert_hash_has(err.sass_backtrace.first, :line => err.sass_line,
405
+ :filename => err.sass_filename, :mixin => err.sass_mixin)
406
+ assert_hash_has(err.sass_backtrace[1], :line => 5,
407
+ :filename => filename_for_test, :mixin => "outer-mixin")
408
+ assert_hash_has(err.sass_backtrace[2], :line => 8,
409
+ :filename => filename_for_test, :mixin => nil)
410
+
411
+ assert_equal("#{filename_for_test}:2:in `error-mixin'", err.backtrace.first)
412
+ assert_equal("#{filename_for_test}:5:in `outer-mixin'", err.backtrace[1])
413
+ assert_equal("#{filename_for_test}:8", err.backtrace[2])
414
+ end
415
+
416
+ def test_mixin_callsite_exception
417
+ render(<<SASS)
418
+ =one-arg-mixin($a)
419
+ color: $a
420
+
421
+ =outer-mixin($a)
422
+ +one-arg-mixin($a, 12)
423
+
424
+ .error
425
+ +outer-mixin(12)
426
+ SASS
427
+ assert(false, "Exception not raised")
428
+ rescue Sass::SyntaxError => err
429
+ assert_hash_has(err.sass_backtrace.first, :line => 5,
430
+ :filename => filename_for_test, :mixin => "one-arg-mixin")
431
+ assert_hash_has(err.sass_backtrace[1], :line => 5,
432
+ :filename => filename_for_test, :mixin => "outer-mixin")
433
+ assert_hash_has(err.sass_backtrace[2], :line => 8,
434
+ :filename => filename_for_test, :mixin => nil)
435
+ end
436
+
437
+ def test_mixin_exception_cssize
438
+ render(<<SASS)
439
+ =parent-ref-mixin
440
+ & foo
441
+ a: b
442
+
443
+ =outer-mixin
444
+ +parent-ref-mixin
445
+
446
+ +outer-mixin
447
+ SASS
448
+ assert(false, "Exception not raised")
449
+ rescue Sass::SyntaxError => err
450
+ assert_hash_has(err.sass_backtrace.first, :line => 2,
451
+ :filename => filename_for_test, :mixin => "parent-ref-mixin")
452
+ assert_hash_has(err.sass_backtrace[1], :line => 6,
453
+ :filename => filename_for_test, :mixin => "outer-mixin")
454
+ assert_hash_has(err.sass_backtrace[2], :line => 8,
455
+ :filename => filename_for_test, :mixin => nil)
456
+ end
457
+
458
+ def test_mixin_and_import_exception
459
+ Sass::Engine.new("@import nested_mixin_bork", :load_paths => [File.dirname(__FILE__) + '/templates/']).render
460
+ assert(false, "Exception not raised")
461
+ rescue Sass::SyntaxError => err
462
+ assert_match(/(\/|^)nested_mixin_bork\.sass$/, err.sass_backtrace.first[:filename])
463
+ assert_hash_has(err.sass_backtrace.first, :mixin => "error-mixin", :line => 4)
464
+
465
+ assert_match(/(\/|^)mixin_bork\.sass$/, err.sass_backtrace[1][:filename])
466
+ assert_hash_has(err.sass_backtrace[1], :mixin => "outer-mixin", :line => 2)
467
+
468
+ assert_match(/(\/|^)mixin_bork\.sass$/, err.sass_backtrace[2][:filename])
469
+ assert_hash_has(err.sass_backtrace[2], :mixin => nil, :line => 5)
470
+
471
+ assert_match(/(\/|^)nested_mixin_bork\.sass$/, err.sass_backtrace[3][:filename])
472
+ assert_hash_has(err.sass_backtrace[3], :mixin => nil, :line => 6)
473
+
474
+ assert_hash_has(err.sass_backtrace[4], :filename => nil, :mixin => nil, :line => 1)
475
+ end
476
+
477
+ def test_recursive_mixin
478
+ assert_equal <<CSS, render(<<SASS)
479
+ .foo .bar .baz {
480
+ color: blue; }
481
+ .foo .bar .qux {
482
+ color: red; }
483
+ .foo .zap {
484
+ color: green; }
485
+ CSS
486
+ @mixin map-to-rule($map-or-color)
487
+ @if type-of($map-or-color) == map
488
+ @each $key, $value in $map-or-color
489
+ .\#{$key}
490
+ @include map-to-rule($value)
491
+ @else
492
+ color: $map-or-color
493
+
494
+ @include map-to-rule((foo: (bar: (baz: blue, qux: red), zap: green)))
495
+ SASS
496
+ end
497
+
498
+ def test_double_import_loop_exception
499
+ importer = MockImporter.new
500
+ importer.add_import("foo", "@import 'bar'")
501
+ importer.add_import("bar", "@import 'foo'")
502
+
503
+ engine = Sass::Engine.new('@import "foo"', :filename => filename_for_test,
504
+ :load_paths => [importer], :importer => importer)
505
+
506
+ assert_raise_message(Sass::SyntaxError, <<ERR.rstrip) {engine.render}
507
+ An @import loop has been found:
508
+ #{filename_for_test} imports foo
509
+ foo imports bar
510
+ bar imports foo
511
+ ERR
512
+ end
513
+
514
+ def test_deep_import_loop_exception
515
+ importer = MockImporter.new
516
+ importer.add_import("foo", "@import 'bar'")
517
+ importer.add_import("bar", "@import 'baz'")
518
+ importer.add_import("baz", "@import 'foo'")
519
+
520
+ engine = Sass::Engine.new('@import "foo"', :filename => filename_for_test,
521
+ :load_paths => [importer], :importer => importer)
522
+
523
+ assert_raise_message(Sass::SyntaxError, <<ERR.rstrip) {engine.render}
524
+ An @import loop has been found:
525
+ #{filename_for_test} imports foo
526
+ foo imports bar
527
+ bar imports baz
528
+ baz imports foo
529
+ ERR
530
+ end
531
+
532
+ def test_exception_css_with_offset
533
+ opts = {:full_exception => true, :line => 362}
534
+ render(("a\n b: c\n" * 10) + "d\n e:\n" + ("f\n g: h\n" * 10), opts)
535
+ rescue Sass::SyntaxError => e
536
+ assert_equal(<<CSS, Sass::SyntaxError.exception_to_css(e, opts[:line]).split("\n")[0..15].join("\n"))
537
+ /*
538
+ Error: Invalid property: "e:" (no value).
539
+ on line 383 of test_exception_css_with_offset_inline.sass
540
+
541
+ 378: a
542
+ 379: b: c
543
+ 380: a
544
+ 381: b: c
545
+ 382: d
546
+ 383: e:
547
+ 384: f
548
+ 385: g: h
549
+ 386: f
550
+ 387: g: h
551
+ 388: f
552
+ CSS
553
+ else
554
+ assert(false, "Exception not raised for test_exception_css_with_offset")
555
+ end
556
+
557
+ def test_exception_css_with_mixins
558
+ render(<<SASS, :full_exception => true)
559
+ =error-mixin($a)
560
+ color: $a * 1em * 1px
561
+
562
+ =outer-mixin($a)
563
+ +error-mixin($a)
564
+
565
+ .error
566
+ +outer-mixin(12)
567
+ SASS
568
+ rescue Sass::SyntaxError => e
569
+ assert_equal(<<CSS, Sass::SyntaxError.exception_to_css(e).split("\n")[0..13].join("\n"))
570
+ /*
571
+ Error: 12em*px isn't a valid CSS value.
572
+ on line 2 of test_exception_css_with_mixins_inline.sass, in `error-mixin'
573
+ from line 5 of test_exception_css_with_mixins_inline.sass, in `outer-mixin'
574
+ from line 8 of test_exception_css_with_mixins_inline.sass
575
+
576
+ 1: =error-mixin($a)
577
+ 2: color: $a * 1em * 1px
578
+ 3:
579
+ 4: =outer-mixin($a)
580
+ 5: +error-mixin($a)
581
+ 6:
582
+ 7: .error
583
+ CSS
584
+ else
585
+ assert(false, "Exception not raised")
586
+ end
587
+
588
+ def test_cssize_exception_css
589
+ render(<<SASS, :full_exception => true)
590
+ .filler
591
+ stuff: "stuff!"
592
+
593
+ a: b
594
+
595
+ .more.filler
596
+ a: b
597
+ SASS
598
+ rescue Sass::SyntaxError => e
599
+ assert_equal(<<CSS, Sass::SyntaxError.exception_to_css(e).split("\n")[0..11].join("\n"))
600
+ /*
601
+ Error: Properties are only allowed within rules, directives, mixin includes, or other properties.
602
+ on line 4 of test_cssize_exception_css_inline.sass
603
+
604
+ 1: .filler
605
+ 2: stuff: "stuff!"
606
+ 3:
607
+ 4: a: b
608
+ 5:
609
+ 6: .more.filler
610
+ 7: a: b
611
+ CSS
612
+ else
613
+ assert(false, "Exception not raised")
614
+ end
615
+
616
+ def test_css_import
617
+ assert_equal("@import url(./fonts.css);\n", render("@import \"./fonts.css\""))
618
+ end
619
+
620
+ def test_http_import
621
+ assert_equal("@import \"http://fonts.googleapis.com/css?family=Droid+Sans\";\n",
622
+ render("@import \"http://fonts.googleapis.com/css?family=Droid+Sans\""))
623
+ end
624
+
625
+ def test_protocol_relative_import
626
+ assert_equal("@import \"//fonts.googleapis.com/css?family=Droid+Sans\";\n",
627
+ render("@import \"//fonts.googleapis.com/css?family=Droid+Sans\""))
628
+ end
629
+
630
+ def test_import_with_interpolation
631
+ assert_equal(<<CSS, render(<<SASS))
632
+ @import url("http://fonts.googleapis.com/css?family=Droid+Sans");
633
+ CSS
634
+ $family: unquote("Droid+Sans")
635
+ @import url("http://fonts.googleapis.com/css?family=\#{$family}")
636
+ SASS
637
+ end
638
+
639
+ def test_import_with_dynamic_media_query
640
+ assert_equal(<<CSS, render(<<SASS))
641
+ @import "foo" print and (-webkit-min-device-pixel-ratio-foo: 25);
642
+ CSS
643
+ $media: print
644
+ $key: -webkit-min-device-pixel-ratio
645
+ $value: 20
646
+ @import "foo" \#{$media} and ($key + "-foo": $value + 5)
647
+ SASS
648
+ end
649
+
650
+ def test_url_import
651
+ assert_equal("@import url(fonts.sass);\n", render("@import url(fonts.sass)"))
652
+ end
653
+
654
+ def test_sass_import
655
+ sassc_file = sassc_path("importee")
656
+ assert !File.exist?(sassc_file)
657
+ renders_correctly "import", { :style => :compact, :load_paths => [File.dirname(__FILE__) + "/templates"] }
658
+ assert File.exist?(sassc_file)
659
+ end
660
+
661
+ def test_sass_pathname_import
662
+ sassc_file = sassc_path("importee")
663
+ assert !File.exist?(sassc_file)
664
+ renders_correctly("import",
665
+ :style => :compact,
666
+ :load_paths => [Pathname.new(File.dirname(__FILE__) + "/templates")])
667
+ assert File.exist?(sassc_file)
668
+ end
669
+
670
+ def test_import_from_global_load_paths
671
+ importer = MockImporter.new
672
+ importer.add_import("imported", "div{color:red}")
673
+ Sass.load_paths << importer
674
+
675
+ assert_equal "div {\n color: red; }\n", Sass::Engine.new('@import "imported"', :importer => importer).render
676
+ ensure
677
+ Sass.load_paths.clear
678
+ end
679
+
680
+ def test_nonexistent_import
681
+ assert_raise_message(Sass::SyntaxError, <<ERR.rstrip) do
682
+ File to import not found or unreadable: nonexistent.sass.
683
+ Load path: #{Dir.pwd}
684
+ ERR
685
+ render("@import nonexistent.sass")
686
+ end
687
+ end
688
+
689
+ def test_nonexistent_extensionless_import
690
+ assert_raise_message(Sass::SyntaxError, <<ERR.rstrip) do
691
+ File to import not found or unreadable: nonexistent.
692
+ Load path: #{Dir.pwd}
693
+ ERR
694
+ render("@import nonexistent")
695
+ end
696
+ end
697
+
698
+ def test_no_cache
699
+ assert !File.exist?(sassc_path("importee"))
700
+ renders_correctly("import", {
701
+ :style => :compact, :cache => false,
702
+ :load_paths => [File.dirname(__FILE__) + "/templates"],
703
+ })
704
+ assert !File.exist?(sassc_path("importee"))
705
+ end
706
+
707
+ def test_import_in_rule
708
+ assert_equal(<<CSS, render(<<SASS, :load_paths => [File.dirname(__FILE__) + '/templates/']))
709
+ .foo #foo {
710
+ background-color: #baf; }
711
+
712
+ .bar {
713
+ a: b; }
714
+ .bar #foo {
715
+ background-color: #baf; }
716
+ CSS
717
+ .foo
718
+ @import partial
719
+
720
+ .bar
721
+ a: b
722
+ @import partial
723
+ SASS
724
+ end
725
+
726
+ def test_units
727
+ renders_correctly "units"
728
+ end
729
+
730
+ def test_default_function
731
+ assert_equal(<<CSS, render(<<SASS))
732
+ foo {
733
+ bar: url("foo.png"); }
734
+ CSS
735
+ foo
736
+ bar: url("foo.png")
737
+ SASS
738
+ assert_equal("foo {\n bar: url(); }\n", render("foo\n bar: url()\n"));
739
+ end
740
+
741
+ def test_string_minus
742
+ assert_equal("foo {\n bar: baz-boom-bat; }\n", render(%Q{foo\n bar: baz-boom-bat}))
743
+ assert_equal("foo {\n bar: -baz-boom; }\n", render(%Q{foo\n bar: -baz-boom}))
744
+ end
745
+
746
+ def test_string_div
747
+ assert_equal("foo {\n bar: baz/boom/bat; }\n", render(%Q{foo\n bar: baz/boom/bat}))
748
+ assert_equal("foo {\n bar: /baz/boom; }\n", render(%Q{foo\n bar: /baz/boom}))
749
+ end
750
+
751
+ def test_basic_multiline_selector
752
+ assert_equal("#foo #bar,\n#baz #boom {\n foo: bar; }\n",
753
+ render("#foo #bar,\n#baz #boom\n :foo bar"))
754
+ assert_equal("#foo #bar,\n#foo #baz {\n foo: bar; }\n",
755
+ render("#foo\n #bar,\n #baz\n :foo bar"))
756
+ assert_equal("#foo,\n#bar {\n foo: bar; }\n #foo #baz,\n #bar #baz {\n foo: bar; }\n",
757
+ render("#foo,\n#bar\n :foo bar\n #baz\n :foo bar"))
758
+ assert_equal("#foo #bar, #baz #boom { foo: bar; }\n",
759
+ render("#foo #bar,\n#baz #boom\n :foo bar", :style => :compact))
760
+
761
+ assert_equal("#foo #bar,#baz #boom{foo:bar}\n",
762
+ render("#foo #bar,\n#baz #boom\n :foo bar", :style => :compressed))
763
+
764
+ assert_equal("#foo #bar,\n#baz #boom {\n foo: bar; }\n",
765
+ render("#foo #bar,,\n,#baz #boom,\n :foo bar"))
766
+
767
+ assert_equal("#bip #bop {\n foo: bar; }\n",
768
+ render("#bip #bop,, ,\n :foo bar"))
769
+ end
770
+
771
+ def test_complex_multiline_selector
772
+ renders_correctly "multiline"
773
+ end
774
+
775
+ def test_colon_only
776
+ begin
777
+ render("a\n b: c", :property_syntax => :old)
778
+ rescue Sass::SyntaxError => e
779
+ assert_equal("Illegal property syntax: can't use new syntax when :property_syntax => :old is set.",
780
+ e.message)
781
+ assert_equal(2, e.sass_line)
782
+ else
783
+ assert(false, "SyntaxError not raised for :property_syntax => :old")
784
+ end
785
+
786
+ begin
787
+ render("a\n :b c", :property_syntax => :new)
788
+ assert_equal(2, e.sass_line)
789
+ rescue Sass::SyntaxError => e
790
+ assert_equal("Illegal property syntax: can't use old syntax when :property_syntax => :new is set.",
791
+ e.message)
792
+ else
793
+ assert(false, "SyntaxError not raised for :property_syntax => :new")
794
+ end
795
+ end
796
+
797
+ def test_pseudo_elements
798
+ assert_equal(<<CSS, render(<<SASS))
799
+ ::first-line {
800
+ size: 10em; }
801
+ CSS
802
+ ::first-line
803
+ size: 10em
804
+ SASS
805
+ end
806
+
807
+ def test_directive
808
+ assert_equal("@a b;\n", render("@a b"))
809
+
810
+ assert_equal("@a {\n b: c; }\n", render("@a\n :b c"))
811
+ assert_equal("@a { b: c; }\n", render("@a\n :b c", :style => :compact))
812
+ assert_equal("@a {\n b: c;\n}\n", render("@a\n :b c", :style => :expanded))
813
+ assert_equal("@a{b:c}\n", render("@a\n :b c", :style => :compressed))
814
+
815
+ assert_equal("@a {\n b: c;\n d: e; }\n",
816
+ render("@a\n :b c\n :d e"))
817
+ assert_equal("@a { b: c; d: e; }\n",
818
+ render("@a\n :b c\n :d e", :style => :compact))
819
+ assert_equal("@a {\n b: c;\n d: e;\n}\n",
820
+ render("@a\n :b c\n :d e", :style => :expanded))
821
+ assert_equal("@a{b:c;d:e}\n",
822
+ render("@a\n :b c\n :d e", :style => :compressed))
823
+
824
+ assert_equal("@a {\n #b {\n c: d; } }\n",
825
+ render("@a\n #b\n :c d"))
826
+ assert_equal("@a { #b { c: d; } }\n",
827
+ render("@a\n #b\n :c d", :style => :compact))
828
+ assert_equal("@a {\n #b {\n c: d;\n }\n}\n",
829
+ render("@a\n #b\n :c d", :style => :expanded))
830
+ assert_equal("@a{#b{c:d}}\n",
831
+ render("@a\n #b\n :c d", :style => :compressed))
832
+
833
+ assert_equal("@a {\n #b {\n a: b; }\n #b #c {\n d: e; } }\n",
834
+ render("@a\n #b\n :a b\n #c\n :d e"))
835
+ assert_equal("@a { #b { a: b; }\n #b #c { d: e; } }\n",
836
+ render("@a\n #b\n :a b\n #c\n :d e", :style => :compact))
837
+ assert_equal("@a {\n #b {\n a: b;\n }\n #b #c {\n d: e;\n }\n}\n",
838
+ render("@a\n #b\n :a b\n #c\n :d e", :style => :expanded))
839
+ assert_equal("@a{#b{a:b}#b #c{d:e}}\n",
840
+ render("@a\n #b\n :a b\n #c\n :d e", :style => :compressed))
841
+
842
+ assert_equal("@a {\n #foo,\n #bar {\n b: c; } }\n",
843
+ render("@a\n #foo, \n #bar\n :b c"))
844
+ assert_equal("@a { #foo, #bar { b: c; } }\n",
845
+ render("@a\n #foo, \n #bar\n :b c", :style => :compact))
846
+ assert_equal("@a {\n #foo,\n #bar {\n b: c;\n }\n}\n",
847
+ render("@a\n #foo, \n #bar\n :b c", :style => :expanded))
848
+ assert_equal("@a{#foo,#bar{b:c}}\n",
849
+ render("@a\n #foo, \n #bar\n :b c", :style => :compressed))
850
+
851
+ to_render = <<END
852
+ @a
853
+ :b c
854
+ #d
855
+ :e f
856
+ :g h
857
+ END
858
+ rendered = <<END
859
+ @a { b: c;
860
+ #d { e: f; }
861
+ g: h; }
862
+ END
863
+ assert_equal(rendered, render(to_render, :style => :compact))
864
+
865
+ assert_equal("@a{b:c;#d{e:f}g:h}\n", render(to_render, :style => :compressed))
866
+ end
867
+
868
+ def test_property_hacks
869
+ assert_equal(<<CSS, render(<<SASS))
870
+ foo {
871
+ _name: val;
872
+ *name: val;
873
+ #name: val;
874
+ .name: val;
875
+ name/**/: val;
876
+ name/*\\**/: val;
877
+ name: val; }
878
+ CSS
879
+ foo
880
+ _name: val
881
+ *name: val
882
+ #name: val
883
+ .name: val
884
+ name/**/: val
885
+ name/*\\**/: val
886
+ name: val
887
+ SASS
888
+ end
889
+
890
+ def test_properties_with_space_after_colon
891
+ assert_equal <<CSS, render(<<SASS)
892
+ foo {
893
+ bar: baz;
894
+ bizz: bap; }
895
+ CSS
896
+ foo
897
+ bar : baz
898
+ bizz : bap
899
+ SASS
900
+ end
901
+
902
+ def test_line_annotations
903
+ assert_equal(<<CSS, render(<<SASS, :line_comments => true, :style => :compact))
904
+ /* line 2, test_line_annotations_inline.sass */
905
+ foo bar { foo: bar; }
906
+ /* line 5, test_line_annotations_inline.sass */
907
+ foo baz { blip: blop; }
908
+
909
+ /* line 9, test_line_annotations_inline.sass */
910
+ floodle { flop: blop; }
911
+
912
+ /* line 18, test_line_annotations_inline.sass */
913
+ bup { mix: on; }
914
+ /* line 15, test_line_annotations_inline.sass */
915
+ bup mixin { moop: mup; }
916
+
917
+ /* line 22, test_line_annotations_inline.sass */
918
+ bip hop, skip hop { a: b; }
919
+ CSS
920
+ foo
921
+ bar
922
+ foo: bar
923
+
924
+ baz
925
+ blip: blop
926
+
927
+
928
+ floodle
929
+
930
+ flop: blop
931
+
932
+ =mxn
933
+ mix: on
934
+ mixin
935
+ moop: mup
936
+
937
+ bup
938
+ +mxn
939
+
940
+ bip, skip
941
+ hop
942
+ a: b
943
+ SASS
944
+ end
945
+
946
+ def test_line_annotations_with_filename
947
+ renders_correctly "line_numbers", :line_comments => true, :load_paths => [File.dirname(__FILE__) + "/templates"]
948
+ end
949
+
950
+ def test_debug_info
951
+ esc_file_name = Sass::SCSS::RX.escape_ident(Sass::Util.scope("test_debug_info_inline.sass"))
952
+
953
+ assert_equal(<<CSS, render(<<SASS, :debug_info => true, :style => :compact))
954
+ @media -sass-debug-info{filename{font-family:file\\:\\/\\/#{esc_file_name}}line{font-family:\\000032}}
955
+ foo bar { foo: bar; }
956
+ @media -sass-debug-info{filename{font-family:file\\:\\/\\/#{esc_file_name}}line{font-family:\\000035}}
957
+ foo baz { blip: blop; }
958
+
959
+ @media -sass-debug-info{filename{font-family:file\\:\\/\\/#{esc_file_name}}line{font-family:\\000039}}
960
+ floodle { flop: blop; }
961
+
962
+ @media -sass-debug-info{filename{font-family:file\\:\\/\\/#{esc_file_name}}line{font-family:\\0000318}}
963
+ bup { mix: on; }
964
+ @media -sass-debug-info{filename{font-family:file\\:\\/\\/#{esc_file_name}}line{font-family:\\0000315}}
965
+ bup mixin { moop: mup; }
966
+
967
+ @media -sass-debug-info{filename{font-family:file\\:\\/\\/#{esc_file_name}}line{font-family:\\0000322}}
968
+ bip hop, skip hop { a: b; }
969
+ CSS
970
+ foo
971
+ bar
972
+ foo: bar
973
+
974
+ baz
975
+ blip: blop
976
+
977
+
978
+ floodle
979
+
980
+ flop: blop
981
+
982
+ =mxn
983
+ mix: on
984
+ mixin
985
+ moop: mup
986
+
987
+ bup
988
+ +mxn
989
+
990
+ bip, skip
991
+ hop
992
+ a: b
993
+ SASS
994
+ end
995
+
996
+ def test_debug_info_without_filename
997
+ assert_equal(<<CSS, Sass::Engine.new(<<SASS, :debug_info => true).render)
998
+ @media -sass-debug-info{filename{}line{font-family:\\000031}}
999
+ foo {
1000
+ a: b; }
1001
+ CSS
1002
+ foo
1003
+ a: b
1004
+ SASS
1005
+ end
1006
+
1007
+ def test_debug_info_with_compressed
1008
+ assert_equal(<<CSS, render(<<SASS, :debug_info => true, :style => :compressed))
1009
+ foo{a:b}
1010
+ CSS
1011
+ foo
1012
+ a: b
1013
+ SASS
1014
+ end
1015
+
1016
+ def test_debug_info_with_line_annotations
1017
+ esc_file_name = Sass::SCSS::RX.escape_ident(Sass::Util.scope("test_debug_info_with_line_annotations_inline.sass"))
1018
+
1019
+ assert_equal(<<CSS, render(<<SASS, :debug_info => true, :line_comments => true))
1020
+ @media -sass-debug-info{filename{font-family:file\\:\\/\\/#{esc_file_name}}line{font-family:\\000031}}
1021
+ foo {
1022
+ a: b; }
1023
+ CSS
1024
+ foo
1025
+ a: b
1026
+ SASS
1027
+ end
1028
+
1029
+ def test_debug_info_in_keyframes
1030
+ assert_equal(<<CSS, render(<<SASS, :debug_info => true))
1031
+ @-webkit-keyframes warm {
1032
+ from {
1033
+ color: black; }
1034
+ to {
1035
+ color: red; } }
1036
+ CSS
1037
+ @-webkit-keyframes warm
1038
+ from
1039
+ color: black
1040
+ to
1041
+ color: red
1042
+ SASS
1043
+ end
1044
+
1045
+ def test_empty_first_line
1046
+ assert_equal("#a {\n b: c; }\n", render("#a\n\n b: c"))
1047
+ end
1048
+
1049
+ def test_escaped_rule
1050
+ assert_equal(":focus {\n a: b; }\n", render("\\:focus\n a: b"))
1051
+ assert_equal("a {\n b: c; }\n a :focus {\n d: e; }\n", render("\\a\n b: c\n \\:focus\n d: e"))
1052
+ end
1053
+
1054
+ def test_cr_newline
1055
+ 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"))
1056
+ end
1057
+
1058
+ def test_property_with_content_and_nested_props
1059
+ assert_equal(<<CSS, render(<<SASS))
1060
+ foo {
1061
+ a: b;
1062
+ a-c: d;
1063
+ a-c-e: f; }
1064
+ CSS
1065
+ foo
1066
+ a: b
1067
+ c: d
1068
+ e: f
1069
+ SASS
1070
+
1071
+ assert_equal(<<CSS, render(<<SASS))
1072
+ foo {
1073
+ a: b;
1074
+ a-c-e: f; }
1075
+ CSS
1076
+ foo
1077
+ a: b
1078
+ c:
1079
+ e: f
1080
+ SASS
1081
+ end
1082
+
1083
+ def test_guarded_assign
1084
+ assert_equal("foo {\n a: b; }\n", render(%Q{$foo: b\n$foo: c !default\nfoo\n a: $foo}))
1085
+ assert_equal("foo {\n a: b; }\n", render(%Q{$foo: b !default\nfoo\n a: $foo}))
1086
+ assert_equal("foo {\n a: b; }\n", render(%Q{$foo: null\n$foo: b !default\nfoo\n a: $foo}))
1087
+ end
1088
+
1089
+ def test_mixins
1090
+ renders_correctly "mixins", { :style => :expanded }
1091
+ end
1092
+
1093
+ def test_directive_style_mixins
1094
+ assert_equal <<CSS, render(<<SASS)
1095
+ bar {
1096
+ prop: baz; }
1097
+ CSS
1098
+ @mixin foo($arg)
1099
+ prop: $arg
1100
+
1101
+ bar
1102
+ @include foo(baz)
1103
+ SASS
1104
+ end
1105
+
1106
+ def test_mixins_dont_interfere_with_sibling_combinator
1107
+ assert_equal("foo + bar {\n a: b; }\nfoo + baz {\n c: d; }\n",
1108
+ render("foo\n +\n bar\n a: b\n baz\n c: d"))
1109
+ end
1110
+
1111
+ def test_mixin_args
1112
+ assert_equal("blat {\n baz: hi; }\n", render(<<SASS))
1113
+ =foo($bar)
1114
+ baz: $bar
1115
+ blat
1116
+ +foo(hi)
1117
+ SASS
1118
+ assert_equal("blat {\n baz: 3; }\n", render(<<SASS))
1119
+ =foo($a, $b)
1120
+ baz: $a + $b
1121
+ blat
1122
+ +foo(1, 2)
1123
+ SASS
1124
+ assert_equal("blat {\n baz: 4;\n baz: 3;\n baz: 5;\n bang: 3; }\n", render(<<SASS))
1125
+ =foo($c: (6 + 4) / 2)
1126
+ baz: $c
1127
+ $c: 3
1128
+ blat
1129
+ +foo($c + 1)
1130
+ +foo(($c + 3)/2)
1131
+ +foo
1132
+ bang: $c
1133
+ SASS
1134
+ end
1135
+
1136
+ def test_default_values_for_mixin_arguments
1137
+ assert_equal(<<CSS, render(<<SASS))
1138
+ white {
1139
+ color: #FFF; }
1140
+
1141
+ black {
1142
+ color: #000; }
1143
+ CSS
1144
+ =foo($a: #FFF)
1145
+ :color $a
1146
+ white
1147
+ +foo
1148
+ black
1149
+ +foo(#000)
1150
+ SASS
1151
+ assert_equal(<<CSS, render(<<SASS))
1152
+ one {
1153
+ color: #fff;
1154
+ padding: 1px;
1155
+ margin: 4px; }
1156
+
1157
+ two {
1158
+ color: #fff;
1159
+ padding: 2px;
1160
+ margin: 5px; }
1161
+
1162
+ three {
1163
+ color: #fff;
1164
+ padding: 2px;
1165
+ margin: 3px; }
1166
+ CSS
1167
+ $a: 5px
1168
+ =foo($a, $b: 1px, $c: 3px + $b)
1169
+ :color $a
1170
+ :padding $b
1171
+ :margin $c
1172
+ one
1173
+ +foo(#fff)
1174
+ two
1175
+ +foo(#fff, 2px)
1176
+ three
1177
+ +foo(#fff, 2px, 3px)
1178
+ SASS
1179
+ assert_equal(<<CSS, render(<<SASS))
1180
+ one {
1181
+ color: #fff;
1182
+ padding: 1px;
1183
+ margin: 4px; }
1184
+
1185
+ two {
1186
+ color: #fff;
1187
+ padding: 2px;
1188
+ margin: 5px; }
1189
+
1190
+ three {
1191
+ color: #fff;
1192
+ padding: 2px;
1193
+ margin: 3px; }
1194
+ CSS
1195
+ $a: 5px
1196
+ =foo($a, $b: 1px, $c: null)
1197
+ $c: 3px + $b !default
1198
+ color: $a
1199
+ padding: $b
1200
+ margin: $c
1201
+ one
1202
+ +foo(#fff)
1203
+ two
1204
+ +foo(#fff, 2px)
1205
+ three
1206
+ +foo(#fff, 2px, 3px)
1207
+ SASS
1208
+ end
1209
+
1210
+ def test_hyphen_underscore_insensitive_mixins
1211
+ assert_equal(<<CSS, render(<<SASS))
1212
+ a {
1213
+ b: 12;
1214
+ c: foo; }
1215
+ CSS
1216
+ =mixin-hyphen
1217
+ b: 12
1218
+
1219
+ =mixin_under
1220
+ c: foo
1221
+
1222
+ a
1223
+ +mixin_hyphen
1224
+ +mixin-under
1225
+ SASS
1226
+ end
1227
+
1228
+ def test_css_identifier_mixin
1229
+ assert_equal(<<CSS, render(<<SASS))
1230
+ a {
1231
+ foo: 12; }
1232
+ CSS
1233
+ =\\{foo\\(12\\)($a)
1234
+ foo: $a
1235
+
1236
+ a
1237
+ +\\{foo\\(12\\)(12)
1238
+ SASS
1239
+ end
1240
+
1241
+ def test_basic_function
1242
+ assert_equal(<<CSS, render(<<SASS))
1243
+ bar {
1244
+ a: 3; }
1245
+ CSS
1246
+ @function foo()
1247
+ @return 1 + 2
1248
+
1249
+ bar
1250
+ a: foo()
1251
+ SASS
1252
+ end
1253
+
1254
+ def test_function_args
1255
+ assert_equal(<<CSS, render(<<SASS))
1256
+ bar {
1257
+ a: 3; }
1258
+ CSS
1259
+ @function plus($var1, $var2)
1260
+ @return $var1 + $var2
1261
+
1262
+ bar
1263
+ a: plus(1, 2)
1264
+ SASS
1265
+ end
1266
+
1267
+ def test_function_arg_default
1268
+ assert_equal(<<CSS, render(<<SASS))
1269
+ bar {
1270
+ a: 3; }
1271
+ CSS
1272
+ @function plus($var1, $var2: 2)
1273
+ @return $var1 + $var2
1274
+
1275
+ bar
1276
+ a: plus(1)
1277
+ SASS
1278
+ end
1279
+
1280
+ def test_function_arg_keyword
1281
+ assert_equal(<<CSS, render(<<SASS))
1282
+ bar {
1283
+ a: 1bar; }
1284
+ CSS
1285
+ @function plus($var1: 1, $var2: 2)
1286
+ @return $var1 + $var2
1287
+
1288
+ bar
1289
+ a: plus($var2: bar)
1290
+ SASS
1291
+ end
1292
+
1293
+ def test_function_with_missing_argument
1294
+ render(<<SASS)
1295
+ @function plus($var1, $var2)
1296
+ @return $var1 + $var2
1297
+
1298
+ bar
1299
+ a: plus($var2: bar)
1300
+ SASS
1301
+ flunk("Expected exception")
1302
+ rescue Sass::SyntaxError => e
1303
+ assert_equal("Function plus is missing argument $var1.", e.message)
1304
+ end
1305
+
1306
+ def test_function_with_extra_argument
1307
+ render(<<SASS)
1308
+ @function plus($var1, $var2)
1309
+ @return $var1 + $var2
1310
+
1311
+ bar
1312
+ a: plus($var1: foo, $var2: bar, $var3: baz)
1313
+ SASS
1314
+ flunk("Expected exception")
1315
+ rescue Sass::SyntaxError => e
1316
+ assert_equal("Function plus doesn't have an argument named $var3.", e.message)
1317
+ end
1318
+
1319
+ def test_function_with_positional_and_keyword_argument
1320
+ render(<<SASS)
1321
+ @function plus($var1, $var2)
1322
+ @return $var1 + $var2
1323
+
1324
+ bar
1325
+ a: plus(foo, bar, $var2: baz)
1326
+ SASS
1327
+ flunk("Expected exception")
1328
+ rescue Sass::SyntaxError => e
1329
+ assert_equal("Function plus was passed argument $var2 both by position and by name.", e.message)
1330
+ end
1331
+
1332
+ def test_function_with_keyword_before_positional_argument
1333
+ render(<<SASS)
1334
+ @function plus($var1, $var2)
1335
+ @return $var1 + $var2
1336
+
1337
+ bar
1338
+ a: plus($var2: foo, bar)
1339
+ SASS
1340
+ flunk("Expected exception")
1341
+ rescue Sass::SyntaxError => e
1342
+ assert_equal("Positional arguments must come before keyword arguments.", e.message)
1343
+ end
1344
+
1345
+ def test_function_with_if
1346
+ assert_equal(<<CSS, render(<<SASS))
1347
+ bar {
1348
+ a: foo;
1349
+ b: bar; }
1350
+ CSS
1351
+ @function my-if($cond, $val1, $val2)
1352
+ @if $cond
1353
+ @return $val1
1354
+ @else
1355
+ @return $val2
1356
+
1357
+ bar
1358
+ a: my-if(true, foo, bar)
1359
+ b: my-if(false, foo, bar)
1360
+ SASS
1361
+ end
1362
+
1363
+ def test_function_with_var
1364
+ assert_equal(<<CSS, render(<<SASS))
1365
+ bar {
1366
+ a: 1; }
1367
+ CSS
1368
+ @function foo($val1, $val2)
1369
+ $intermediate: $val1 + $val2
1370
+ @return $intermediate/3
1371
+
1372
+ bar
1373
+ a: foo(1, 2)
1374
+ SASS
1375
+ end
1376
+
1377
+ def test_user_defined_function_variable_scope
1378
+ render(<<SASS)
1379
+ bar
1380
+ -no-op: set-a-variable(variable, 5)
1381
+ a: $variable
1382
+ SASS
1383
+ flunk("Exception not raised for test_user_defined_function_variable_scope")
1384
+ rescue Sass::SyntaxError => e
1385
+ assert_equal('Undefined variable: "$variable".', e.message)
1386
+ end
1387
+
1388
+ def test_user_defined_function_can_change_global_variable
1389
+ assert_equal(<<CSS, render(<<SASS))
1390
+ bar {
1391
+ a: 5; }
1392
+ CSS
1393
+ $variable: 0
1394
+ bar
1395
+ $local: 10
1396
+ -no-op: set-a-global-variable(variable, 5)
1397
+ a: $variable
1398
+ SASS
1399
+ end
1400
+
1401
+ def test_user_defined_function_cannot_read_local_variable
1402
+ assert_equal(<<CSS, render(<<SASS))
1403
+ bar {
1404
+ global: 0;
1405
+ local: undefined; }
1406
+ CSS
1407
+ $global: 0
1408
+ bar
1409
+ $local: 10
1410
+ global: get-a-variable(global)
1411
+ local: get-a-variable(local)
1412
+ SASS
1413
+ end
1414
+
1415
+ def test_control_directive_in_nested_property
1416
+ assert_equal(<<CSS, render(<<SASS))
1417
+ foo {
1418
+ a-b: c; }
1419
+ CSS
1420
+ foo
1421
+ a:
1422
+ @if true
1423
+ b: c
1424
+ SASS
1425
+ end
1426
+
1427
+ def test_interpolation
1428
+ assert_equal("a-1 {\n b-2-3: c-3; }\n", render(<<SASS))
1429
+ $a: 1
1430
+ $b: 2
1431
+ $c: 3
1432
+ a-\#{$a}
1433
+ b-\#{$b}-\#{$c}: c-\#{$a + $b}
1434
+ SASS
1435
+ end
1436
+
1437
+ def test_complex_property_interpolation
1438
+ assert_equal(<<CSS, render(<<SASS))
1439
+ a-1 {
1440
+ b-2 3-fizzap18: c-3; }
1441
+ CSS
1442
+ $a: 1
1443
+ $b: 2
1444
+ $c: 3
1445
+ a-\#{$a}
1446
+ b-\#{$b $c}-\#{fizzap + ($c + 15)}: c-\#{$a + $b}
1447
+ SASS
1448
+ end
1449
+
1450
+ def test_if_directive
1451
+ assert_equal("a {\n b: 1; }\n", render(<<SASS))
1452
+ $var: true
1453
+ a
1454
+ @if $var
1455
+ b: 1
1456
+ @if not $var
1457
+ b: 2
1458
+ SASS
1459
+
1460
+ assert_equal("a {\n b: 2; }\n", render(<<SASS))
1461
+ $var: null
1462
+ a
1463
+ @if $var
1464
+ b: 1
1465
+ @if not $var
1466
+ b: 2
1467
+ SASS
1468
+ end
1469
+
1470
+ def test_for
1471
+ assert_equal(<<CSS, render(<<SASS))
1472
+ a-0 {
1473
+ two-i: 0; }
1474
+
1475
+ a-1 {
1476
+ two-i: 2; }
1477
+
1478
+ a-2 {
1479
+ two-i: 4; }
1480
+
1481
+ a-3 {
1482
+ two-i: 6; }
1483
+
1484
+ b-1 {
1485
+ j-1: 0; }
1486
+
1487
+ b-2 {
1488
+ j-1: 1; }
1489
+
1490
+ b-3 {
1491
+ j-1: 2; }
1492
+
1493
+ b-4 {
1494
+ j-1: 3; }
1495
+ CSS
1496
+ $a: 3
1497
+ @for $i from 0 to $a + 1
1498
+ a-\#{$i}
1499
+ two-i: 2 * $i
1500
+
1501
+ @for $j from 1 through 4
1502
+ b-\#{$j}
1503
+ j-1: $j - 1
1504
+ SASS
1505
+ end
1506
+
1507
+ def test_while
1508
+ assert_equal(<<CSS, render(<<SASS))
1509
+ a-5 {
1510
+ blooble: gloop; }
1511
+
1512
+ a-4 {
1513
+ blooble: gloop; }
1514
+
1515
+ a-3 {
1516
+ blooble: gloop; }
1517
+
1518
+ a-2 {
1519
+ blooble: gloop; }
1520
+
1521
+ a-1 {
1522
+ blooble: gloop; }
1523
+ CSS
1524
+ $a: 5
1525
+ @while $a != 0
1526
+ a-\#{$a}
1527
+ blooble: gloop
1528
+ $a: $a - 1 !global
1529
+ SASS
1530
+ end
1531
+
1532
+ def test_else
1533
+ assert_equal(<<CSS, render(<<SASS))
1534
+ a {
1535
+ t1: t;
1536
+ t2: t;
1537
+ t3: t;
1538
+ t4: t; }
1539
+ CSS
1540
+ a
1541
+ @if true
1542
+ t1: t
1543
+ @else
1544
+ f1: f
1545
+
1546
+ @if false
1547
+ f2: f
1548
+ @else
1549
+ t2: t
1550
+
1551
+ @if false
1552
+ f3: f1
1553
+ @else if 1 + 1 == 3
1554
+ f3: f2
1555
+ @else
1556
+ t3: t
1557
+
1558
+ @if false
1559
+ f4: f1
1560
+ @else if 1 + 1 == 2
1561
+ t4: t
1562
+ @else
1563
+ f4: f2
1564
+
1565
+ @if false
1566
+ f5: f1
1567
+ @else if false
1568
+ f5: f2
1569
+ SASS
1570
+ end
1571
+
1572
+ def test_each
1573
+ assert_equal(<<CSS, render(<<SASS))
1574
+ a {
1575
+ b: 1px;
1576
+ b: 2px;
1577
+ b: 3px;
1578
+ b: 4px;
1579
+ c: foo;
1580
+ c: bar;
1581
+ c: baz;
1582
+ c: bang;
1583
+ d: blue; }
1584
+ CSS
1585
+ a
1586
+ @each $number in 1px 2px 3px 4px
1587
+ b: $number
1588
+ @each $str in foo, bar, baz, bang
1589
+ c: $str
1590
+ @each $single in blue
1591
+ d: $single
1592
+ SASS
1593
+ end
1594
+
1595
+ def test_destructuring_each
1596
+ assert_equal <<CSS, render(<<SCSS)
1597
+ a {
1598
+ foo: 1px;
1599
+ bar: 2px;
1600
+ baz: 3px; }
1601
+
1602
+ c {
1603
+ foo: "Value is bar";
1604
+ bar: "Value is baz";
1605
+ bang: "Value is "; }
1606
+ CSS
1607
+ a
1608
+ @each $name, $number in (foo: 1px, bar: 2px, baz: 3px)
1609
+ \#{$name}: $number
1610
+ c
1611
+ @each $key, $value in (foo bar) (bar, baz) bang
1612
+ \#{$key}: "Value is \#{$value}"
1613
+ SCSS
1614
+ end
1615
+
1616
+ def test_variable_reassignment
1617
+ assert_equal(<<CSS, render(<<SASS))
1618
+ a {
1619
+ b: 1;
1620
+ c: 2; }
1621
+ CSS
1622
+ a
1623
+ $a: 1
1624
+ b: $a
1625
+ $a: 2
1626
+ c: $a
1627
+ SASS
1628
+ end
1629
+
1630
+ def test_hyphen_underscore_insensitive_variables
1631
+ assert_equal(<<CSS, render(<<SASS))
1632
+ d {
1633
+ e: 13;
1634
+ f: foobar; }
1635
+ CSS
1636
+ $var-hyphen: 12
1637
+ $var_under: foo
1638
+
1639
+ $var_hyphen: 1 + $var_hyphen
1640
+ $var-under: $var-under + bar
1641
+
1642
+ d
1643
+ e: $var-hyphen
1644
+ f: $var_under
1645
+ SASS
1646
+ end
1647
+
1648
+ def test_css_identifier_variable
1649
+ assert_equal(<<CSS, render(<<SASS))
1650
+ a {
1651
+ b: 12; }
1652
+ CSS
1653
+ $\\{foo\\(12\\): 12
1654
+
1655
+ a
1656
+ b: $\\{foo\\(12\\)
1657
+ SASS
1658
+ end
1659
+
1660
+ def test_important
1661
+ assert_equal(<<CSS, render(<<SASS))
1662
+ a {
1663
+ b: 12px !important; }
1664
+ CSS
1665
+ $foo: 12px
1666
+ a
1667
+ b: $foo !important
1668
+ SASS
1669
+ end
1670
+
1671
+ def test_argument_error
1672
+ assert_raises(Sass::SyntaxError) { render("a\n b: hsl(1)") }
1673
+ end
1674
+
1675
+ def test_comments_at_the_top_of_a_document
1676
+ render(<<SASS)
1677
+ //
1678
+ This is a comment that
1679
+ continues to the second line.
1680
+ foo
1681
+ bar: baz
1682
+ SASS
1683
+ end
1684
+
1685
+ def test_loud_comments_containing_a_comment_close
1686
+ actual_css = render(<<SASS)
1687
+ /*
1688
+ This is a comment that
1689
+ continues to the second line. */
1690
+ foo
1691
+ bar: baz
1692
+ SASS
1693
+ assert_equal(<<CSS, actual_css)
1694
+ /* This is a comment that
1695
+ * continues to the second line. */
1696
+ foo {
1697
+ bar: baz; }
1698
+ CSS
1699
+ end
1700
+
1701
+ def test_loud_comments_with_starred_lines
1702
+ assert_equal(<<CSS, render(<<SASS))
1703
+ /* This is a comment that
1704
+ * continues to the second line.
1705
+ * And even to the third! */
1706
+ CSS
1707
+ /* This is a comment that
1708
+ * continues to the second line.
1709
+ * And even to the third!
1710
+ SASS
1711
+ end
1712
+
1713
+ def test_loud_comments_with_no_space_after_starred_lines
1714
+ assert_equal(<<CSS, render(<<SASS))
1715
+ /*bip bop
1716
+ *beep boop
1717
+ *bap blimp */
1718
+ CSS
1719
+ /*bip bop
1720
+ *beep boop
1721
+ *bap blimp
1722
+ SASS
1723
+ end
1724
+
1725
+ def test_comment_indentation_at_beginning_of_doc
1726
+ assert_equal <<CSS, render(<<SASS)
1727
+ /* foo
1728
+ * bar
1729
+ * baz */
1730
+ foo {
1731
+ a: b; }
1732
+ CSS
1733
+ /* foo
1734
+ bar
1735
+ baz
1736
+ foo
1737
+ a: b
1738
+ SASS
1739
+ end
1740
+
1741
+ def test_unusual_comment_indentation
1742
+ assert_equal <<CSS, render(<<SASS)
1743
+ foo {
1744
+ /* foo
1745
+ * bar
1746
+ * baz */ }
1747
+ CSS
1748
+ foo
1749
+ /* foo
1750
+ bar
1751
+ baz
1752
+ SASS
1753
+ end
1754
+
1755
+ def test_loud_comment_with_close
1756
+ assert_equal <<CSS, render(<<SASS)
1757
+ foo {
1758
+ /* foo
1759
+ * bar */ }
1760
+ CSS
1761
+ foo
1762
+ /* foo
1763
+ bar */
1764
+ SASS
1765
+ end
1766
+
1767
+ def test_loud_comment_with_separate_line_close
1768
+ assert_equal <<CSS, render(<<SASS)
1769
+ foo {
1770
+ /* foo
1771
+ * bar
1772
+ */ }
1773
+ CSS
1774
+ foo
1775
+ /* foo
1776
+ * bar
1777
+ */
1778
+ SASS
1779
+ end
1780
+
1781
+ def test_loud_comment_in_compressed_mode
1782
+ assert_equal <<CSS, render(<<SASS, :style => :compressed)
1783
+ foo{color:blue;/*! foo
1784
+ * bar
1785
+ */}
1786
+ CSS
1787
+ foo
1788
+ color: blue
1789
+ /*! foo
1790
+ * bar
1791
+ */
1792
+ SASS
1793
+ end
1794
+
1795
+ def test_loud_comment_is_evaluated
1796
+ assert_equal <<CSS, render(<<SASS)
1797
+ /*! Hue: 327.21649deg */
1798
+ CSS
1799
+ /*! Hue: \#{hue(#f836a0)}
1800
+ SASS
1801
+ end
1802
+
1803
+ def test_attribute_selector_with_spaces
1804
+ assert_equal(<<CSS, render(<<SASS))
1805
+ a b[foo=bar] {
1806
+ c: d; }
1807
+ CSS
1808
+ a
1809
+ b[foo = bar]
1810
+ c: d
1811
+ SASS
1812
+ end
1813
+
1814
+ def test_quoted_colon
1815
+ assert_equal(<<CSS, render(<<SASS))
1816
+ a b[foo="bar: baz"] {
1817
+ c: d; }
1818
+ CSS
1819
+ a
1820
+ b[foo="bar: baz"]
1821
+ c: d
1822
+ SASS
1823
+ end
1824
+
1825
+ def test_quoted_comma
1826
+ assert_equal(<<CSS, render(<<SASS))
1827
+ a b[foo="bar, baz"] {
1828
+ c: d; }
1829
+ CSS
1830
+ a
1831
+ b[foo="bar, baz"]
1832
+ c: d
1833
+ SASS
1834
+ end
1835
+
1836
+ def test_quoted_ampersand
1837
+ assert_equal(<<CSS, render(<<SASS))
1838
+ a b[foo="bar & baz"] {
1839
+ c: d; }
1840
+ CSS
1841
+ a
1842
+ b[foo="bar & baz"]
1843
+ c: d
1844
+ SASS
1845
+ end
1846
+
1847
+ def test_empty_selector_warning
1848
+ assert_warning(<<END) {render("foo bar")}
1849
+ WARNING on line 1 of test_empty_selector_warning_inline.sass:
1850
+ This selector doesn't have any properties and will not be rendered.
1851
+ END
1852
+ end
1853
+
1854
+ def test_nonprinting_empty_property
1855
+ assert_equal(<<CSS, render(<<SASS))
1856
+ a {
1857
+ c: "";
1858
+ e: f; }
1859
+ CSS
1860
+ $null-value: null
1861
+ $empty-string: ''
1862
+ $empty-list: (null)
1863
+ a
1864
+ b: $null-value
1865
+ c: $empty-string
1866
+ d: $empty-list
1867
+ e: f
1868
+
1869
+ g
1870
+ h: null
1871
+ SASS
1872
+ end
1873
+
1874
+ def test_root_level_pseudo_class_with_new_properties
1875
+ assert_equal(<<CSS, render(<<SASS, :property_syntax => :new))
1876
+ :focus {
1877
+ outline: 0; }
1878
+ CSS
1879
+ :focus
1880
+ outline: 0
1881
+ SASS
1882
+ end
1883
+
1884
+ def test_pseudo_class_with_new_properties
1885
+ assert_equal(<<CSS, render(<<SASS, :property_syntax => :new))
1886
+ p :focus {
1887
+ outline: 0; }
1888
+ CSS
1889
+ p
1890
+ :focus
1891
+ outline: 0
1892
+ SASS
1893
+ end
1894
+
1895
+ def test_nil_option
1896
+ assert_equal(<<CSS, render(<<SASS, :format => nil))
1897
+ foo {
1898
+ a: b; }
1899
+ CSS
1900
+ foo
1901
+ a: b
1902
+ SASS
1903
+ end
1904
+
1905
+ def test_interpolation_in_raw_functions
1906
+ assert_equal(<<CSS, render(<<SASS))
1907
+ foo {
1908
+ filter: progid:Microsoft.foo.bar.Baz(flip=foobar, bang=#00ff00cc); }
1909
+ CSS
1910
+ foo
1911
+ filter: progid:Microsoft.foo.bar.Baz(flip=\#{foo + bar}, bang=#00ff00cc)
1912
+ SASS
1913
+ end
1914
+
1915
+ # SassScript string behavior
1916
+
1917
+ def test_plus_preserves_quotedness
1918
+ assert_equal(<<CSS, render(<<SASS))
1919
+ foo {
1920
+ a: "foo1";
1921
+ b: "1foo";
1922
+ c: foo1;
1923
+ d: 1foo;
1924
+ e: "foobar";
1925
+ f: foobar; }
1926
+ CSS
1927
+ foo
1928
+ a: "foo" + 1
1929
+ b: 1 + "foo"
1930
+ c: foo + 1
1931
+ d: 1 + foo
1932
+ e: "foo" + bar
1933
+ f: foo + "bar"
1934
+ SASS
1935
+ end
1936
+
1937
+ def test_colon_properties_preserve_quotedness
1938
+ assert_equal(<<CSS, render(<<SASS))
1939
+ foo {
1940
+ a: "foo";
1941
+ b: bar;
1942
+ c: "foo" bar;
1943
+ d: foo, "bar"; }
1944
+ CSS
1945
+ foo
1946
+ a: "foo"
1947
+ b: bar
1948
+ c: "foo" bar
1949
+ d: foo, "bar"
1950
+ SASS
1951
+ end
1952
+
1953
+ def test_colon_variables_preserve_quotedness
1954
+ assert_equal(<<CSS, render(<<SASS))
1955
+ foo {
1956
+ a: "foo";
1957
+ b: bar; }
1958
+ CSS
1959
+ $a: "foo"
1960
+ $b: bar
1961
+
1962
+ foo
1963
+ a: $a
1964
+ b: $b
1965
+ SASS
1966
+ end
1967
+
1968
+ def test_colon_args_preserve_quotedness
1969
+ assert_equal(<<CSS, render(<<SASS))
1970
+ foo {
1971
+ a: "foo";
1972
+ b: bar;
1973
+ c: "foo" bar;
1974
+ d: foo, "bar"; }
1975
+ CSS
1976
+ =foo($a: "foo", $b: bar, $c: "foo" bar, $d: (foo, "bar"))
1977
+ foo
1978
+ a: $a
1979
+ b: $b
1980
+ c: $c
1981
+ d: $d
1982
+
1983
+ +foo
1984
+ SASS
1985
+ end
1986
+
1987
+ def test_interpolation_unquotes_strings
1988
+ assert_equal(<<CSS, render(<<SASS))
1989
+ .foo-bar {
1990
+ a: b; }
1991
+ CSS
1992
+ .foo-\#{"bar"}
1993
+ a: b
1994
+ SASS
1995
+
1996
+ assert_equal(<<CSS, render(<<SASS))
1997
+ .foo {
1998
+ a: b c; }
1999
+ CSS
2000
+ .foo
2001
+ a: b \#{"c"}
2002
+ SASS
2003
+ end
2004
+
2005
+ def test_interpolation_unquotes_strings_in_vars
2006
+ assert_equal(<<CSS, render(<<SASS))
2007
+ .foo-bar {
2008
+ a: b; }
2009
+ CSS
2010
+ $var: "bar"
2011
+
2012
+ .foo-\#{$var}
2013
+ a: b
2014
+ SASS
2015
+ end
2016
+
2017
+ def test_interpolation_deep_unquotes_strings
2018
+ assert_equal(<<CSS, render(<<SASS))
2019
+ .foo {
2020
+ a: bar baz; }
2021
+ CSS
2022
+ .foo
2023
+ a: \#{"bar" "baz"}
2024
+ SASS
2025
+ end
2026
+
2027
+ def test_warn_directive
2028
+ expected_warning = <<EXPECTATION
2029
+ WARNING: this is a warning
2030
+ on line 4 of test_warn_directive_inline.sass
2031
+
2032
+ WARNING: this is a mixin warning
2033
+ on line 2 of test_warn_directive_inline.sass, in `foo'
2034
+ from line 7 of test_warn_directive_inline.sass
2035
+ EXPECTATION
2036
+ assert_warning expected_warning do
2037
+ assert_equal <<CSS, render(<<SASS)
2038
+ bar {
2039
+ c: d; }
2040
+ CSS
2041
+ =foo
2042
+ @warn "this is a mixin warning"
2043
+
2044
+ @warn "this is a warning"
2045
+ bar
2046
+ c: d
2047
+ +foo
2048
+ SASS
2049
+ end
2050
+ end
2051
+
2052
+ def test_warn_directive_when_quiet
2053
+ assert_warning "" do
2054
+ assert_equal <<CSS, render(<<SASS, :quiet => true)
2055
+ CSS
2056
+ @warn "this is a warning"
2057
+ SASS
2058
+ end
2059
+ end
2060
+
2061
+ def test_warn_with_imports
2062
+ expected_warning = <<WARN
2063
+ WARNING: In the main file
2064
+ on line 1 of #{File.dirname(__FILE__)}/templates/warn.sass
2065
+
2066
+ WARNING: Imported
2067
+ on line 1 of #{File.dirname(__FILE__)}/templates/warn_imported.sass
2068
+ from line 2 of #{File.dirname(__FILE__)}/templates/warn.sass
2069
+
2070
+ WARNING: In an imported mixin
2071
+ on line 4 of #{File.dirname(__FILE__)}/templates/warn_imported.sass, in `emits-a-warning'
2072
+ from line 3 of #{File.dirname(__FILE__)}/templates/warn.sass
2073
+ WARN
2074
+ assert_warning expected_warning do
2075
+ renders_correctly "warn", :style => :compact, :load_paths => [File.dirname(__FILE__) + "/templates"]
2076
+ end
2077
+ end
2078
+
2079
+ def test_media_bubbling
2080
+ assert_equal <<CSS, render(<<SASS)
2081
+ .foo {
2082
+ a: b; }
2083
+ @media bar {
2084
+ .foo {
2085
+ c: d; } }
2086
+ .foo .baz {
2087
+ e: f; }
2088
+ @media bip {
2089
+ .foo .baz {
2090
+ g: h; } }
2091
+
2092
+ .other {
2093
+ i: j; }
2094
+ CSS
2095
+ .foo
2096
+ a: b
2097
+ @media bar
2098
+ c: d
2099
+ .baz
2100
+ e: f
2101
+ @media bip
2102
+ g: h
2103
+
2104
+ .other
2105
+ i: j
2106
+ SASS
2107
+
2108
+ assert_equal <<CSS, render(<<SASS, :style => :compact)
2109
+ .foo { a: b; }
2110
+ @media bar { .foo { c: d; } }
2111
+ .foo .baz { e: f; }
2112
+ @media bip { .foo .baz { g: h; } }
2113
+
2114
+ .other { i: j; }
2115
+ CSS
2116
+ .foo
2117
+ a: b
2118
+ @media bar
2119
+ c: d
2120
+ .baz
2121
+ e: f
2122
+ @media bip
2123
+ g: h
2124
+
2125
+ .other
2126
+ i: j
2127
+ SASS
2128
+
2129
+ assert_equal <<CSS, render(<<SASS, :style => :expanded)
2130
+ .foo {
2131
+ a: b;
2132
+ }
2133
+ @media bar {
2134
+ .foo {
2135
+ c: d;
2136
+ }
2137
+ }
2138
+ .foo .baz {
2139
+ e: f;
2140
+ }
2141
+ @media bip {
2142
+ .foo .baz {
2143
+ g: h;
2144
+ }
2145
+ }
2146
+
2147
+ .other {
2148
+ i: j;
2149
+ }
2150
+ CSS
2151
+ .foo
2152
+ a: b
2153
+ @media bar
2154
+ c: d
2155
+ .baz
2156
+ e: f
2157
+ @media bip
2158
+ g: h
2159
+
2160
+ .other
2161
+ i: j
2162
+ SASS
2163
+ end
2164
+
2165
+ def test_double_media_bubbling
2166
+ assert_equal <<CSS, render(<<SASS)
2167
+ @media bar and (a: b) {
2168
+ .foo {
2169
+ c: d; } }
2170
+ CSS
2171
+ @media bar
2172
+ @media (a: b)
2173
+ .foo
2174
+ c: d
2175
+ SASS
2176
+
2177
+ assert_equal <<CSS, render(<<SASS)
2178
+ @media bar {
2179
+ .foo {
2180
+ a: b; } }
2181
+ @media bar and (a: b) {
2182
+ .foo {
2183
+ c: d; } }
2184
+ CSS
2185
+ .foo
2186
+ @media bar
2187
+ a: b
2188
+ @media (a: b)
2189
+ c: d
2190
+ SASS
2191
+ end
2192
+
2193
+ def test_double_media_bubbling_with_commas
2194
+ assert_equal <<CSS, render(<<SASS)
2195
+ @media (a: b) and (e: f), (c: d) and (e: f), (a: b) and (g: h), (c: d) and (g: h) {
2196
+ .foo {
2197
+ c: d; } }
2198
+ CSS
2199
+ @media (a: b), (c: d)
2200
+ @media (e: f), (g: h)
2201
+ .foo
2202
+ c: d
2203
+ SASS
2204
+ end
2205
+
2206
+ def test_double_media_bubbling_with_surrounding_rules
2207
+ assert_equal <<CSS, render(<<SASS)
2208
+ @media (min-width: 0) {
2209
+ a {
2210
+ a: a; }
2211
+
2212
+ b {
2213
+ before: b;
2214
+ after: b; } }
2215
+ @media (min-width: 0) and (max-width: 5000px) {
2216
+ b {
2217
+ x: x; } }
2218
+
2219
+ @media (min-width: 0) {
2220
+ c {
2221
+ c: c; } }
2222
+ CSS
2223
+ @media (min-width: 0)
2224
+ a
2225
+ a: a
2226
+ b
2227
+ before: b
2228
+ @media (max-width: 5000px)
2229
+ x: x
2230
+ after: b
2231
+ c
2232
+ c: c
2233
+ SASS
2234
+ end
2235
+
2236
+ def test_rule_media_rule_bubbling
2237
+ assert_equal <<CSS, render(<<SASS)
2238
+ @media bar {
2239
+ .foo {
2240
+ a: b;
2241
+ e: f; }
2242
+ .foo .baz {
2243
+ c: d; } }
2244
+ CSS
2245
+ .foo
2246
+ @media bar
2247
+ a: b
2248
+ .baz
2249
+ c: d
2250
+ e: f
2251
+ SASS
2252
+ end
2253
+
2254
+ def test_nested_media_around_properties
2255
+ assert_equal <<CSS, render(<<SASS)
2256
+ .outside {
2257
+ color: red;
2258
+ background: blue; }
2259
+ @media print {
2260
+ .outside {
2261
+ color: black; } }
2262
+ @media print and (a: b) {
2263
+ .outside .inside {
2264
+ border: 1px solid black; } }
2265
+
2266
+ .outside .middle {
2267
+ display: block; }
2268
+ CSS
2269
+ .outside
2270
+ color: red
2271
+ @media print
2272
+ color: black
2273
+ .inside
2274
+ @media (a: b)
2275
+ border: 1px solid black
2276
+ background: blue
2277
+ .middle
2278
+ display: block
2279
+ SASS
2280
+ end
2281
+
2282
+ def test_media_with_parent_references
2283
+ sass_str = <<SASS
2284
+ .outside
2285
+ @media print
2286
+ &.inside
2287
+ border: 1px solid black
2288
+ SASS
2289
+ css_str = <<CSS
2290
+ @media print {
2291
+ .outside.inside {
2292
+ border: 1px solid black; } }
2293
+ CSS
2294
+ assert_equal css_str, render(sass_str)
2295
+ end
2296
+
2297
+ def test_eliminated_media_bubbling
2298
+ assert_equal <<CSS, render(<<SASS)
2299
+ @media screen {
2300
+ a: b; }
2301
+ CSS
2302
+ @media screen
2303
+ a: b
2304
+ @media print
2305
+ c: d
2306
+ SASS
2307
+
2308
+ assert_equal <<CSS, render(<<SASS)
2309
+ @media not print {
2310
+ a: b; }
2311
+ CSS
2312
+ @media not print
2313
+ a: b
2314
+ @media print
2315
+ c: d
2316
+ SASS
2317
+
2318
+ assert_equal <<CSS, render(<<SASS)
2319
+ @media not print {
2320
+ a: b; }
2321
+ CSS
2322
+ @media not print
2323
+ a: b
2324
+ @media not screen
2325
+ c: d
2326
+ SASS
2327
+ end
2328
+
2329
+ def test_non_eliminated_media_bubbling
2330
+ assert_equal <<CSS, render(<<SASS)
2331
+ @media screen {
2332
+ a: b; }
2333
+ @media screen and (a: b) {
2334
+ c: d; }
2335
+ CSS
2336
+ @media screen
2337
+ a: b
2338
+ @media screen and (a: b)
2339
+ c: d
2340
+ SASS
2341
+
2342
+ assert_equal <<CSS, render(<<SASS)
2343
+ @media not print {
2344
+ a: b; }
2345
+ @media screen {
2346
+ c: d; }
2347
+ CSS
2348
+ @media not print
2349
+ a: b
2350
+ @media screen
2351
+ c: d
2352
+ SASS
2353
+
2354
+ assert_equal <<CSS, render(<<SASS)
2355
+ @media only screen {
2356
+ a: b; }
2357
+ @media only screen and (a: b) {
2358
+ c: d; }
2359
+ CSS
2360
+ @media only screen
2361
+ a: b
2362
+ @media screen and (a: b)
2363
+ c: d
2364
+ SASS
2365
+ end
2366
+
2367
+ def test_directive_interpolation
2368
+ assert_equal <<CSS, render(<<SASS)
2369
+ @foo bar12 qux {
2370
+ a: b; }
2371
+ CSS
2372
+ $baz: 12
2373
+ @foo bar\#{$baz} qux
2374
+ a: b
2375
+ SASS
2376
+ end
2377
+
2378
+ def test_media_interpolation
2379
+ assert_equal <<CSS, render(<<SASS)
2380
+ @media bar12 {
2381
+ a: b; }
2382
+ CSS
2383
+ $baz: 12
2384
+ @media bar\#{$baz}
2385
+ a: b
2386
+ SASS
2387
+ end
2388
+
2389
+ def test_variables_in_media
2390
+ assert_equal <<CSS, render(<<SASS)
2391
+ @media screen and (-webkit-min-device-pixel-ratio-foo: 25), only print {
2392
+ a: b; }
2393
+ CSS
2394
+ $media1: screen
2395
+ $media2: print
2396
+ $var: -webkit-min-device-pixel-ratio
2397
+ $val: 20
2398
+ @media \#{$media1} and ($var + "-foo": $val + 5), only \#{$media2}
2399
+ a: b
2400
+ SASS
2401
+ end
2402
+
2403
+ def test_at_root
2404
+ assert_equal <<CSS, render(<<SASS)
2405
+ .bar {
2406
+ a: b; }
2407
+ CSS
2408
+ .foo
2409
+ @at-root
2410
+ .bar
2411
+ a: b
2412
+ SASS
2413
+ end
2414
+
2415
+ def test_at_root_with_selector
2416
+ assert_equal <<CSS, render(<<SASS)
2417
+ .bar {
2418
+ a: b; }
2419
+ CSS
2420
+ .foo
2421
+ @at-root .bar
2422
+ a: b
2423
+ SASS
2424
+ end
2425
+
2426
+ def test_at_root_with_query
2427
+ assert_equal <<CSS, render(<<SASS)
2428
+ .foo .bar {
2429
+ a: b; }
2430
+ CSS
2431
+ .foo
2432
+ @media screen
2433
+ @at-root (without: media)
2434
+ .bar
2435
+ a: b
2436
+ SASS
2437
+ end
2438
+
2439
+ def test_variable_assignment_with_global
2440
+ assert_no_warning {assert_equal(<<CSS, render(<<SASS))}
2441
+ .foo {
2442
+ a: x; }
2443
+
2444
+ .bar {
2445
+ b: x; }
2446
+ CSS
2447
+ $var: 1
2448
+
2449
+ .foo
2450
+ $var: x !global
2451
+ a: $var
2452
+
2453
+ .bar
2454
+ b: $var
2455
+ SASS
2456
+ end
2457
+
2458
+ # Regression tests
2459
+
2460
+ def test_list_separator_with_arg_list
2461
+ assert_equal(<<CSS, render(<<SASS))
2462
+ .test {
2463
+ separator: comma; }
2464
+ CSS
2465
+ @mixin arglist-test($args...)
2466
+ separator: list-separator($args)
2467
+
2468
+ .test
2469
+ @include arglist-test(this, is, comma, separated)
2470
+ SASS
2471
+ end
2472
+
2473
+ def test_parent_mixin_in_content_nested
2474
+ assert_equal(<<CSS, render(<<SASS))
2475
+ a {
2476
+ b: c; }
2477
+ CSS
2478
+ =foo
2479
+ @content
2480
+
2481
+ =bar
2482
+ +foo
2483
+ +foo
2484
+ a
2485
+ b: c
2486
+
2487
+ +bar
2488
+ SASS
2489
+ end
2490
+
2491
+ def test_supports_bubbles
2492
+ assert_equal <<CSS, render(<<SASS)
2493
+ parent {
2494
+ background: orange; }
2495
+ @supports (perspective: 10px) or (-moz-perspective: 10px) {
2496
+ parent child {
2497
+ background: blue; } }
2498
+ CSS
2499
+ parent
2500
+ background: orange
2501
+ @supports (perspective: 10px) or (-moz-perspective: 10px)
2502
+ child
2503
+ background: blue
2504
+ SASS
2505
+ end
2506
+
2507
+ def test_line_numbers_with_dos_line_endings
2508
+ assert_equal <<CSS, render(<<SASS, :line_comments => true)
2509
+ /* line 5, test_line_numbers_with_dos_line_endings_inline.sass */
2510
+ .foo {
2511
+ a: b; }
2512
+ CSS
2513
+ \r
2514
+ \r
2515
+ \r
2516
+ \r
2517
+ .foo
2518
+ a: b
2519
+ SASS
2520
+ end
2521
+
2522
+ def test_variable_in_media_in_mixin
2523
+ assert_equal <<CSS, render(<<SASS)
2524
+ @media screen and (min-width: 10px) {
2525
+ body {
2526
+ background: red; } }
2527
+ @media screen and (min-width: 20px) {
2528
+ body {
2529
+ background: blue; } }
2530
+ CSS
2531
+ @mixin respond-to($width)
2532
+ @media screen and (min-width: $width)
2533
+ @content
2534
+
2535
+ body
2536
+ @include respond-to(10px)
2537
+ background: red
2538
+ @include respond-to(20px)
2539
+ background: blue
2540
+ SASS
2541
+ end
2542
+
2543
+ def test_interpolated_comment_in_mixin
2544
+ assert_equal <<CSS, render(<<SASS)
2545
+ /*! color: red */
2546
+ .foo {
2547
+ color: red; }
2548
+
2549
+ /*! color: blue */
2550
+ .foo {
2551
+ color: blue; }
2552
+
2553
+ /*! color: green */
2554
+ .foo {
2555
+ color: green; }
2556
+ CSS
2557
+ =foo($var)
2558
+ /*! color: \#{$var}
2559
+ .foo
2560
+ color: $var
2561
+
2562
+ +foo(red)
2563
+ +foo(blue)
2564
+ +foo(green)
2565
+ SASS
2566
+ end
2567
+
2568
+ def test_parens_in_mixins
2569
+ assert_equal(<<CSS, render(<<SASS))
2570
+ .foo {
2571
+ color: #01ff7f;
2572
+ background-color: #000102; }
2573
+ CSS
2574
+ =foo($c1, $c2: rgb(0, 1, 2))
2575
+ color: $c1
2576
+ background-color: $c2
2577
+
2578
+ .foo
2579
+ +foo(rgb(1,255,127))
2580
+ SASS
2581
+ end
2582
+
2583
+ def test_comment_beneath_prop
2584
+ assert_equal(<<RESULT, render(<<SOURCE))
2585
+ .box {
2586
+ border-style: solid; }
2587
+ RESULT
2588
+ .box
2589
+ :border
2590
+ //:color black
2591
+ :style solid
2592
+ SOURCE
2593
+
2594
+ assert_equal(<<RESULT, render(<<SOURCE))
2595
+ .box {
2596
+ /* :color black */
2597
+ border-style: solid; }
2598
+ RESULT
2599
+ .box
2600
+ :border
2601
+ /* :color black
2602
+ :style solid
2603
+ SOURCE
2604
+
2605
+ assert_equal(<<RESULT, render(<<SOURCE, :style => :compressed))
2606
+ .box{border-style:solid}
2607
+ RESULT
2608
+ .box
2609
+ :border
2610
+ /*:color black
2611
+ :style solid
2612
+ SOURCE
2613
+ end
2614
+
2615
+ def test_compressed_comment_beneath_directive
2616
+ assert_equal(<<RESULT, render(<<SOURCE, :style => :compressed))
2617
+ @foo{a:b}
2618
+ RESULT
2619
+ @foo
2620
+ a: b
2621
+ /*b: c
2622
+ SOURCE
2623
+ end
2624
+
2625
+ def test_comment_with_crazy_indentation
2626
+ assert_equal(<<CSS, render(<<SASS))
2627
+ /* This is a loud comment:
2628
+ * Where the indentation is wonky. */
2629
+ .comment {
2630
+ width: 1px; }
2631
+ CSS
2632
+ /*
2633
+ This is a loud comment:
2634
+ Where the indentation is wonky.
2635
+ //
2636
+ This is a silent comment:
2637
+ Where the indentation is wonky.
2638
+ .comment
2639
+ width: 1px
2640
+ SASS
2641
+ end
2642
+
2643
+ def test_plus_with_space
2644
+ assert_equal(<<CSS, render(<<SASS))
2645
+ a + b {
2646
+ color: green; }
2647
+ CSS
2648
+ a
2649
+ + b
2650
+ color: green
2651
+ SASS
2652
+ end
2653
+
2654
+ def test_empty_line_comment
2655
+ assert_equal(<<CSS, render(<<SASS))
2656
+ /* Foo
2657
+ *
2658
+ * Bar */
2659
+ CSS
2660
+ /*
2661
+ Foo
2662
+
2663
+ Bar
2664
+ SASS
2665
+ end
2666
+
2667
+ def test_empty_comment
2668
+ assert_equal(<<CSS, render(<<SASS))
2669
+ /* */
2670
+ a {
2671
+ /* */
2672
+ b: c; }
2673
+ CSS
2674
+ /*
2675
+ a
2676
+ /*
2677
+ b: c
2678
+ SASS
2679
+ end
2680
+
2681
+ def test_options_available_in_environment
2682
+ assert_equal(<<CSS, render(<<SASS))
2683
+ a {
2684
+ b: nested; }
2685
+ CSS
2686
+ a
2687
+ b: option("style")
2688
+ SASS
2689
+ end
2690
+
2691
+ def test_mixin_no_arg_error
2692
+ assert_raise_message(Sass::SyntaxError, 'Invalid CSS after "($bar,": expected variable (e.g. $foo), was ")"') do
2693
+ render(<<SASS)
2694
+ =foo($bar,)
2695
+ bip: bap
2696
+ SASS
2697
+ end
2698
+ end
2699
+
2700
+ def test_import_with_commas_in_url
2701
+ assert_equal <<CSS, render(<<SASS)
2702
+ @import url(foo.css?bar,baz);
2703
+ CSS
2704
+ @import url(foo.css?bar,baz)
2705
+ SASS
2706
+ end
2707
+
2708
+ def test_silent_comment_in_prop_val_after_important
2709
+ assert_equal(<<CSS, render(<<SASS))
2710
+ .advanced {
2711
+ display: none !important; }
2712
+ CSS
2713
+ .advanced
2714
+ display: none !important // yeah, yeah. it's not really a style anyway.
2715
+ SASS
2716
+ end
2717
+
2718
+ def test_mixin_with_keyword_args
2719
+ assert_equal <<CSS, render(<<SASS)
2720
+ .mixed {
2721
+ required: foo;
2722
+ arg1: default-val1;
2723
+ arg2: non-default-val2; }
2724
+ CSS
2725
+ =a-mixin($required, $arg1: default-val1, $arg2: default-val2)
2726
+ required: $required
2727
+ arg1: $arg1
2728
+ arg2: $arg2
2729
+ .mixed
2730
+ +a-mixin(foo, $arg2: non-default-val2)
2731
+ SASS
2732
+ end
2733
+
2734
+ def test_mixin_with_keyword_arg_variable_value
2735
+ assert_equal <<CSS, render(<<SASS)
2736
+ .mixed {
2737
+ required: foo;
2738
+ arg1: default-val1;
2739
+ arg2: a-value; }
2740
+ CSS
2741
+ =a-mixin($required, $arg1: default-val1, $arg2: default-val2)
2742
+ required: $required
2743
+ arg1: $arg1
2744
+ arg2: $arg2
2745
+ .mixed
2746
+ $a-value: a-value
2747
+ +a-mixin(foo, $arg2: $a-value)
2748
+ SASS
2749
+ end
2750
+
2751
+ def test_mixin_keyword_args_handle_variable_underscore_dash_equivalence
2752
+ assert_equal <<CSS, render(<<SASS)
2753
+ .mixed {
2754
+ required: foo;
2755
+ arg1: non-default-val1;
2756
+ arg2: non-default-val2; }
2757
+ CSS
2758
+ =a-mixin($required, $arg-1: default-val1, $arg_2: default-val2)
2759
+ required: $required
2760
+ arg1: $arg_1
2761
+ arg2: $arg-2
2762
+ .mixed
2763
+ +a-mixin(foo, $arg-2: non-default-val2, $arg_1: non-default-val1)
2764
+ SASS
2765
+ end
2766
+
2767
+ def test_passing_required_args_as_a_keyword_arg
2768
+ assert_equal <<CSS, render(<<SASS)
2769
+ .mixed {
2770
+ required: foo;
2771
+ arg1: default-val1;
2772
+ arg2: default-val2; }
2773
+ CSS
2774
+ =a-mixin($required, $arg1: default-val1, $arg2: default-val2)
2775
+ required: $required
2776
+ arg1: $arg1
2777
+ arg2: $arg2
2778
+ .mixed
2779
+ +a-mixin($required: foo)
2780
+ SASS
2781
+ end
2782
+
2783
+ def test_passing_all_as_keyword_args_in_opposite_order
2784
+ assert_equal <<CSS, render(<<SASS)
2785
+ .mixed {
2786
+ required: foo;
2787
+ arg1: non-default-val1;
2788
+ arg2: non-default-val2; }
2789
+ CSS
2790
+ =a-mixin($required, $arg1: default-val1, $arg2: default-val2)
2791
+ required: $required
2792
+ arg1: $arg1
2793
+ arg2: $arg2
2794
+ .mixed
2795
+ +a-mixin($arg2: non-default-val2, $arg1: non-default-val1, $required: foo)
2796
+ SASS
2797
+ end
2798
+
2799
+ def test_function_output_with_comma
2800
+ assert_equal <<CSS, render(<<SASS)
2801
+ foo {
2802
+ a: b(c), d(e); }
2803
+ CSS
2804
+ foo
2805
+ a: b(c), d(e)
2806
+ SASS
2807
+ end
2808
+
2809
+ def test_interpolation_with_comma
2810
+ assert_equal <<CSS, render(<<SASS)
2811
+ foo {
2812
+ a: foo, bar; }
2813
+ CSS
2814
+ $foo: foo
2815
+ foo
2816
+ a: \#{$foo}, bar
2817
+ SASS
2818
+ end
2819
+
2820
+ def test_string_interpolation_with_comma
2821
+ assert_equal <<CSS, render(<<SASS)
2822
+ foo {
2823
+ a: "bip foo bap", bar; }
2824
+ CSS
2825
+ $foo: foo
2826
+ foo
2827
+ a: "bip \#{$foo} bap", bar
2828
+ SASS
2829
+ end
2830
+
2831
+ def test_unknown_directive
2832
+ assert_equal <<CSS, render(<<SASS)
2833
+ @baz {
2834
+ c: d; }
2835
+ CSS
2836
+ @baz
2837
+ c: d
2838
+ SASS
2839
+ end
2840
+
2841
+ def test_loud_comment_interpolations_can_be_escaped
2842
+ assert_equal <<CSS, render(<<SASS)
2843
+ /* \#{foo} */
2844
+ CSS
2845
+ /* \\\#{foo}
2846
+ SASS
2847
+ assert_equal <<CSS, render(<<SASS)
2848
+ /*! \#{foo} */
2849
+ CSS
2850
+ /*! \\\#{foo}
2851
+ SASS
2852
+ end
2853
+
2854
+ def test_selector_compression
2855
+ assert_equal <<CSS, render(<<SASS, :style => :compressed)
2856
+ a>b,c+d,:-moz-any(e,f,g){h:i}
2857
+ CSS
2858
+ a > b, c + d, :-moz-any(e, f, g)
2859
+ h: i
2860
+ SASS
2861
+ end
2862
+
2863
+ def test_comment_like_selector
2864
+ assert_raise_message(Sass::SyntaxError, 'Invalid CSS after "/": expected identifier, was " foo"') {render(<<SASS)}
2865
+ / foo
2866
+ a: b
2867
+ SASS
2868
+ end
2869
+
2870
+ def test_nested_empty_directive
2871
+ assert_equal <<CSS, render(<<SASS)
2872
+ @media screen {
2873
+ .foo {
2874
+ a: b; }
2875
+
2876
+ @unknown-directive; }
2877
+ CSS
2878
+ @media screen
2879
+ .foo
2880
+ a: b
2881
+
2882
+ @unknown-directive
2883
+ SASS
2884
+ end
2885
+
2886
+ def test_original_filename_set
2887
+ importer = MockImporter.new
2888
+ importer.add_import("imported", "div{color:red}")
2889
+
2890
+ original_filename = filename_for_test
2891
+ engine = Sass::Engine.new('@import "imported"; div{color:blue}',
2892
+ :filename => original_filename, :load_paths => [importer], :syntax => :scss, :importer => importer)
2893
+ engine.render
2894
+
2895
+ assert_equal original_filename, engine.options[:original_filename]
2896
+ assert_equal original_filename, importer.engine("imported").options[:original_filename]
2897
+ end
2898
+
2899
+ def test_changing_precision
2900
+ old_precision = Sass::Script::Value::Number.precision
2901
+ begin
2902
+ Sass::Script::Value::Number.precision = 8
2903
+ assert_equal <<CSS, render(<<SASS)
2904
+ div {
2905
+ maximum: 1.00000001;
2906
+ too-much: 1.0; }
2907
+ CSS
2908
+ div
2909
+ maximum : 1.00000001
2910
+ too-much: 1.000000001
2911
+ SASS
2912
+ ensure
2913
+ Sass::Script::Value::Number.precision = old_precision
2914
+ end
2915
+ end
2916
+
2917
+ def test_content
2918
+ assert_equal <<CSS, render(<<SASS)
2919
+ .children {
2920
+ background-color: red;
2921
+ color: blue;
2922
+ border-color: red; }
2923
+ CSS
2924
+ $color: blue
2925
+ =context($class, $color: red)
2926
+ .\#{$class}
2927
+ background-color: $color
2928
+ @content
2929
+ border-color: $color
2930
+ +context(children)
2931
+ color: $color
2932
+ SASS
2933
+ end
2934
+
2935
+ def test_selector_in_content
2936
+ assert_equal <<CSS, render(<<SASS)
2937
+ .parent {
2938
+ background-color: red;
2939
+ border-color: red; }
2940
+ .parent .children {
2941
+ color: blue; }
2942
+ CSS
2943
+ $color: blue
2944
+ =context($class, $color: red)
2945
+ .\#{$class}
2946
+ background-color: $color
2947
+ @content
2948
+ border-color: $color
2949
+ +context(parent)
2950
+ .children
2951
+ color: $color
2952
+ SASS
2953
+ end
2954
+
2955
+ def test_using_parent_mixin_in_content
2956
+ assert_equal <<CSS, render(<<SASS)
2957
+ .parent {
2958
+ before-color: red;
2959
+ after-color: red; }
2960
+ .parent .sibling {
2961
+ before-color: yellow;
2962
+ after-color: yellow; }
2963
+ .parent .sibling .child {
2964
+ before-color: green;
2965
+ color: blue;
2966
+ after-color: green; }
2967
+ CSS
2968
+ $color: blue
2969
+ =context($class, $color: red)
2970
+ .\#{$class}
2971
+ before-color: $color
2972
+ @content
2973
+ after-color: $color
2974
+ +context(parent)
2975
+ +context(sibling, $color: yellow)
2976
+ +context(child, $color: green)
2977
+ color: $color
2978
+ SASS
2979
+ end
2980
+
2981
+ def test_content_more_than_once
2982
+ assert_equal <<CSS, render(<<SASS)
2983
+ .once {
2984
+ color: blue; }
2985
+
2986
+ .twice {
2987
+ color: blue; }
2988
+ CSS
2989
+ $color: blue
2990
+ =context($class, $color: red)
2991
+ .once
2992
+ @content
2993
+ .twice
2994
+ @content
2995
+ +context(parent)
2996
+ color: $color
2997
+ SASS
2998
+ end
2999
+
3000
+ def test_content_with_variable
3001
+ assert_equal <<CSS, render(<<SASS)
3002
+ .foo {
3003
+ a: 1px; }
3004
+ CSS
3005
+ =foo
3006
+ .foo
3007
+ @content
3008
+ +foo
3009
+ $a: 1px
3010
+ a: $a
3011
+ SASS
3012
+ end
3013
+
3014
+ def test_nested_content_blocks
3015
+ assert_equal <<CSS, render(<<SASS)
3016
+ .foo {
3017
+ a: foo; }
3018
+ .foo .bar {
3019
+ a: bar; }
3020
+ .foo .bar .baz {
3021
+ a: baz; }
3022
+ .foo .bar .baz .outside {
3023
+ a: outside;
3024
+ color: red; }
3025
+ CSS
3026
+ $a: outside
3027
+ =baz($a: baz)
3028
+ .baz
3029
+ a: $a
3030
+ @content
3031
+ =bar($a: bar)
3032
+ .bar
3033
+ a: $a
3034
+ +baz
3035
+ @content
3036
+ =foo($a: foo)
3037
+ .foo
3038
+ a: $a
3039
+ +bar
3040
+ @content
3041
+ +foo
3042
+ .outside
3043
+ a: $a
3044
+ color: red
3045
+ SASS
3046
+ end
3047
+
3048
+ def test_content_not_seen_through_mixin
3049
+ assert_equal <<CSS, render(<<SASS)
3050
+ a foo {
3051
+ mixin: foo;
3052
+ a: b; }
3053
+ a foo bar {
3054
+ mixin: bar; }
3055
+ CSS
3056
+ =foo
3057
+ foo
3058
+ mixin: foo
3059
+ @content
3060
+ +bar
3061
+ =bar
3062
+ bar
3063
+ mixin: bar
3064
+ @content
3065
+ a
3066
+ +foo
3067
+ a: b
3068
+ SASS
3069
+ end
3070
+
3071
+ def test_content_backtrace_for_perform
3072
+ render(<<SASS)
3073
+ =foo
3074
+ @content
3075
+
3076
+ a
3077
+ +foo
3078
+ b: 1em + 2px
3079
+ SASS
3080
+ assert(false, "Expected exception")
3081
+ rescue Sass::SyntaxError => e
3082
+ assert_equal([
3083
+ {:mixin => '@content', :line => 6, :filename => 'test_content_backtrace_for_perform_inline.sass'},
3084
+ {:mixin => 'foo', :line => 2, :filename => 'test_content_backtrace_for_perform_inline.sass'},
3085
+ {:line => 5, :filename => 'test_content_backtrace_for_perform_inline.sass'},
3086
+ ], e.sass_backtrace)
3087
+ end
3088
+
3089
+ def test_content_backtrace_for_cssize
3090
+ render(<<SASS)
3091
+ =foo
3092
+ @content
3093
+
3094
+ a
3095
+ +foo
3096
+ @extend foo bar baz
3097
+ SASS
3098
+ assert(false, "Expected exception")
3099
+ rescue Sass::SyntaxError => e
3100
+ assert_equal([
3101
+ {:mixin => '@content', :line => 6, :filename => 'test_content_backtrace_for_cssize_inline.sass'},
3102
+ {:mixin => 'foo', :line => 2, :filename => 'test_content_backtrace_for_cssize_inline.sass'},
3103
+ {:line => 5, :filename => 'test_content_backtrace_for_cssize_inline.sass'},
3104
+ ], e.sass_backtrace)
3105
+ end
3106
+
3107
+ def test_mixin_with_args_and_varargs_passed_no_var_args
3108
+ assert_equal <<CSS, render(<<SASS, :syntax => :scss)
3109
+ .foo {
3110
+ a: 1;
3111
+ b: 2;
3112
+ c: 3; }
3113
+ CSS
3114
+ @mixin three-or-more-args($a, $b, $c, $rest...) {
3115
+ a: $a;
3116
+ b: $b;
3117
+ c: $c;
3118
+ }
3119
+
3120
+ .foo {
3121
+ @include three-or-more-args($a: 1, $b: 2, $c: 3);
3122
+ }
3123
+ SASS
3124
+
3125
+ end
3126
+
3127
+ def test_debug_inspects_sass_objects
3128
+ assert_warning(<<END) {render("@debug (a: 1, b: 2)")}
3129
+ test_debug_inspects_sass_objects_inline.sass:1 DEBUG: (a: 1, b: 2)
3130
+ END
3131
+ assert_warning(<<END) {render("$map: (a: 1, b: 2); @debug $map", :syntax => :scss)}
3132
+ test_debug_inspects_sass_objects_inline.scss:1 DEBUG: (a: 1, b: 2)
3133
+ END
3134
+ end
3135
+
3136
+ def test_error_throws_sass_objects
3137
+ assert_raise_message(Sass::SyntaxError, "(a: 1, b: 2)") {render("@error (a: 1, b: 2)")}
3138
+ assert_raise_message(Sass::SyntaxError, "(a: 1, b: 2)") do
3139
+ render("$map: (a: 1, b: 2); @error $map", :syntax => :scss)
3140
+ end
3141
+ end
3142
+
3143
+ def test_default_arg_before_splat
3144
+ assert_equal <<CSS, render(<<SASS, :syntax => :scss)
3145
+ .foo-positional {
3146
+ a: 1;
3147
+ b: 2;
3148
+ positional-arguments: 3, 4;
3149
+ keyword-arguments: (); }
3150
+
3151
+ .foo-keywords {
3152
+ a: true;
3153
+ positional-arguments: ();
3154
+ keyword-arguments: (c: c, d: d); }
3155
+ CSS
3156
+ @mixin foo($a: true, $b: null, $arguments...) {
3157
+ a: $a;
3158
+ b: $b;
3159
+ positional-arguments: inspect($arguments);
3160
+ keyword-arguments: inspect(keywords($arguments));
3161
+ }
3162
+ .foo-positional {
3163
+ @include foo(1, 2, 3, 4);
3164
+ }
3165
+ .foo-keywords {
3166
+ @include foo($c: c, $d: d);
3167
+ }
3168
+ SASS
3169
+ end
3170
+
3171
+ def test_keyframes
3172
+ assert_equal <<CSS, render(<<SASS)
3173
+ @keyframes identifier {
3174
+ 0% {
3175
+ top: 0;
3176
+ left: 0; }
3177
+ 30% {
3178
+ top: 50px; }
3179
+ 68%, 72% {
3180
+ left: 50px; }
3181
+ 100% {
3182
+ top: 100px;
3183
+ left: 100%; } }
3184
+ CSS
3185
+ @keyframes identifier
3186
+ 0%
3187
+ top: 0
3188
+ left: 0
3189
+ \#{"30%"}
3190
+ top: 50px
3191
+ 68%, 72%
3192
+ left: 50px
3193
+ 100%
3194
+ top: 100px
3195
+ left: 100%
3196
+ SASS
3197
+ end
3198
+
3199
+ def test_prefixed_keyframes
3200
+ assert_equal <<CSS, render(<<SASS)
3201
+ @-moz-keyframes identifier {
3202
+ 0% {
3203
+ top: 0;
3204
+ left: 0; }
3205
+ 30% {
3206
+ top: 50px; }
3207
+ 68%, 72% {
3208
+ left: 50px; }
3209
+ 100% {
3210
+ top: 100px;
3211
+ left: 100%; } }
3212
+ CSS
3213
+ @-moz-keyframes identifier
3214
+ 0%
3215
+ top: 0
3216
+ left: 0
3217
+ \#{"30%"}
3218
+ top: 50px
3219
+ 68%, 72%
3220
+ left: 50px
3221
+ 100%
3222
+ top: 100px
3223
+ left: 100%
3224
+ SASS
3225
+ end
3226
+
3227
+ def test_uppercase_keyframes
3228
+ assert_equal <<CSS, render(<<SASS)
3229
+ @KEYFRAMES identifier {
3230
+ 0% {
3231
+ top: 0;
3232
+ left: 0; }
3233
+ 30% {
3234
+ top: 50px; }
3235
+ 68%, 72% {
3236
+ left: 50px; }
3237
+ 100% {
3238
+ top: 100px;
3239
+ left: 100%; } }
3240
+ CSS
3241
+ @KEYFRAMES identifier
3242
+ 0%
3243
+ top: 0
3244
+ left: 0
3245
+ \#{"30%"}
3246
+ top: 50px
3247
+ 68%, 72%
3248
+ left: 50px
3249
+ 100%
3250
+ top: 100px
3251
+ left: 100%
3252
+ SASS
3253
+ end
3254
+
3255
+ private
3256
+
3257
+ def assert_hash_has(hash, expected)
3258
+ expected.each {|k, v| assert_equal(v, hash[k])}
3259
+ end
3260
+
3261
+ def assert_renders_encoded(css, sass)
3262
+ result = render(sass)
3263
+ assert_equal css.encoding, result.encoding
3264
+ assert_equal css, result
3265
+ end
3266
+
3267
+ def render(sass, options = {})
3268
+ munge_filename options
3269
+ options[:importer] ||= MockImporter.new
3270
+ Sass::Engine.new(sass, options).render
3271
+ end
3272
+
3273
+ def renders_correctly(name, options={})
3274
+ sass_file = load_file(name, "sass")
3275
+ css_file = load_file(name, "css")
3276
+ options[:filename] ||= filename(name, "sass")
3277
+ options[:syntax] ||= :sass
3278
+ options[:css_filename] ||= filename(name, "css")
3279
+ css_result = Sass::Engine.new(sass_file, options).render
3280
+ assert_equal css_file, css_result
3281
+ end
3282
+
3283
+ def load_file(name, type = "sass")
3284
+ @result = ''
3285
+ File.new(filename(name, type)).each_line { |l| @result += l }
3286
+ @result
3287
+ end
3288
+
3289
+ def filename(name, type)
3290
+ File.dirname(__FILE__) + "/#{type == 'sass' ? 'templates' : 'results'}/#{name}.#{type}"
3291
+ end
3292
+
3293
+ def sassc_path(template)
3294
+ sassc_path = File.join(File.dirname(__FILE__) + "/templates/#{template}.sass")
3295
+ engine = Sass::Engine.new("", :filename => sassc_path,
3296
+ :importer => Sass::Importers::Filesystem.new("."))
3297
+ key = engine.send(:sassc_key)
3298
+ File.join(engine.options[:cache_location], key)
3299
+ end
3300
+ end
3301
+