sass 3.7.4 → 4.0.0.alpha.1

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