aliddle-sass 1.0

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