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