sass 3.1.0 → 3.3.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 (260) hide show
  1. checksums.yaml +7 -0
  2. data/CONTRIBUTING +1 -1
  3. data/MIT-LICENSE +2 -2
  4. data/README.md +29 -17
  5. data/Rakefile +43 -9
  6. data/VERSION +1 -1
  7. data/VERSION_DATE +1 -0
  8. data/VERSION_NAME +1 -1
  9. data/bin/sass +6 -1
  10. data/bin/sass-convert +6 -1
  11. data/bin/scss +6 -1
  12. data/ext/mkrf_conf.rb +27 -0
  13. data/lib/sass/cache_stores/base.rb +7 -3
  14. data/lib/sass/cache_stores/chain.rb +3 -2
  15. data/lib/sass/cache_stores/filesystem.rb +5 -7
  16. data/lib/sass/cache_stores/memory.rb +1 -1
  17. data/lib/sass/cache_stores/null.rb +2 -2
  18. data/lib/sass/callbacks.rb +2 -1
  19. data/lib/sass/css.rb +168 -53
  20. data/lib/sass/engine.rb +502 -174
  21. data/lib/sass/environment.rb +151 -111
  22. data/lib/sass/error.rb +7 -7
  23. data/lib/sass/exec.rb +176 -60
  24. data/lib/sass/features.rb +40 -0
  25. data/lib/sass/importers/base.rb +46 -7
  26. data/lib/sass/importers/deprecated_path.rb +51 -0
  27. data/lib/sass/importers/filesystem.rb +113 -30
  28. data/lib/sass/importers.rb +1 -0
  29. data/lib/sass/logger/base.rb +30 -0
  30. data/lib/sass/logger/log_level.rb +45 -0
  31. data/lib/sass/logger.rb +12 -0
  32. data/lib/sass/media.rb +213 -0
  33. data/lib/sass/plugin/compiler.rb +194 -104
  34. data/lib/sass/plugin/configuration.rb +18 -25
  35. data/lib/sass/plugin/merb.rb +1 -1
  36. data/lib/sass/plugin/staleness_checker.rb +37 -11
  37. data/lib/sass/plugin.rb +10 -13
  38. data/lib/sass/railtie.rb +2 -1
  39. data/lib/sass/repl.rb +5 -6
  40. data/lib/sass/script/css_lexer.rb +8 -4
  41. data/lib/sass/script/css_parser.rb +5 -2
  42. data/lib/sass/script/functions.rb +1547 -618
  43. data/lib/sass/script/lexer.rb +122 -72
  44. data/lib/sass/script/parser.rb +304 -135
  45. data/lib/sass/script/tree/funcall.rb +306 -0
  46. data/lib/sass/script/{interpolation.rb → tree/interpolation.rb} +43 -13
  47. data/lib/sass/script/tree/list_literal.rb +77 -0
  48. data/lib/sass/script/tree/literal.rb +45 -0
  49. data/lib/sass/script/tree/map_literal.rb +64 -0
  50. data/lib/sass/script/{node.rb → tree/node.rb} +30 -12
  51. data/lib/sass/script/{operation.rb → tree/operation.rb} +33 -21
  52. data/lib/sass/script/{string_interpolation.rb → tree/string_interpolation.rb} +14 -4
  53. data/lib/sass/script/{unary_operation.rb → tree/unary_operation.rb} +21 -9
  54. data/lib/sass/script/tree/variable.rb +57 -0
  55. data/lib/sass/script/tree.rb +15 -0
  56. data/lib/sass/script/value/arg_list.rb +36 -0
  57. data/lib/sass/script/value/base.rb +238 -0
  58. data/lib/sass/script/value/bool.rb +40 -0
  59. data/lib/sass/script/{color.rb → value/color.rb} +256 -74
  60. data/lib/sass/script/value/deprecated_false.rb +55 -0
  61. data/lib/sass/script/value/helpers.rb +155 -0
  62. data/lib/sass/script/value/list.rb +128 -0
  63. data/lib/sass/script/value/map.rb +70 -0
  64. data/lib/sass/script/value/null.rb +49 -0
  65. data/lib/sass/script/{number.rb → value/number.rb} +115 -62
  66. data/lib/sass/script/{string.rb → value/string.rb} +9 -11
  67. data/lib/sass/script/value.rb +12 -0
  68. data/lib/sass/script.rb +35 -9
  69. data/lib/sass/scss/css_parser.rb +2 -12
  70. data/lib/sass/scss/parser.rb +657 -230
  71. data/lib/sass/scss/rx.rb +17 -12
  72. data/lib/sass/scss/static_parser.rb +37 -6
  73. data/lib/sass/scss.rb +0 -1
  74. data/lib/sass/selector/abstract_sequence.rb +35 -3
  75. data/lib/sass/selector/comma_sequence.rb +29 -14
  76. data/lib/sass/selector/sequence.rb +371 -74
  77. data/lib/sass/selector/simple.rb +28 -13
  78. data/lib/sass/selector/simple_sequence.rb +163 -36
  79. data/lib/sass/selector.rb +138 -36
  80. data/lib/sass/shared.rb +3 -5
  81. data/lib/sass/source/map.rb +196 -0
  82. data/lib/sass/source/position.rb +39 -0
  83. data/lib/sass/source/range.rb +41 -0
  84. data/lib/sass/stack.rb +126 -0
  85. data/lib/sass/supports.rb +228 -0
  86. data/lib/sass/tree/at_root_node.rb +82 -0
  87. data/lib/sass/tree/comment_node.rb +34 -29
  88. data/lib/sass/tree/content_node.rb +9 -0
  89. data/lib/sass/tree/css_import_node.rb +60 -0
  90. data/lib/sass/tree/debug_node.rb +3 -3
  91. data/lib/sass/tree/directive_node.rb +33 -3
  92. data/lib/sass/tree/each_node.rb +9 -9
  93. data/lib/sass/tree/extend_node.rb +20 -6
  94. data/lib/sass/tree/for_node.rb +6 -6
  95. data/lib/sass/tree/function_node.rb +12 -4
  96. data/lib/sass/tree/if_node.rb +2 -15
  97. data/lib/sass/tree/import_node.rb +11 -5
  98. data/lib/sass/tree/media_node.rb +27 -11
  99. data/lib/sass/tree/mixin_def_node.rb +15 -4
  100. data/lib/sass/tree/mixin_node.rb +27 -7
  101. data/lib/sass/tree/node.rb +69 -35
  102. data/lib/sass/tree/prop_node.rb +47 -31
  103. data/lib/sass/tree/return_node.rb +4 -3
  104. data/lib/sass/tree/root_node.rb +20 -4
  105. data/lib/sass/tree/rule_node.rb +37 -26
  106. data/lib/sass/tree/supports_node.rb +38 -0
  107. data/lib/sass/tree/trace_node.rb +33 -0
  108. data/lib/sass/tree/variable_node.rb +10 -4
  109. data/lib/sass/tree/visitors/base.rb +5 -8
  110. data/lib/sass/tree/visitors/check_nesting.rb +67 -52
  111. data/lib/sass/tree/visitors/convert.rb +134 -53
  112. data/lib/sass/tree/visitors/cssize.rb +245 -51
  113. data/lib/sass/tree/visitors/deep_copy.rb +102 -0
  114. data/lib/sass/tree/visitors/extend.rb +68 -0
  115. data/lib/sass/tree/visitors/perform.rb +331 -105
  116. data/lib/sass/tree/visitors/set_options.rb +125 -0
  117. data/lib/sass/tree/visitors/to_css.rb +259 -95
  118. data/lib/sass/tree/warn_node.rb +3 -3
  119. data/lib/sass/tree/while_node.rb +3 -3
  120. data/lib/sass/util/cross_platform_random.rb +19 -0
  121. data/lib/sass/util/multibyte_string_scanner.rb +157 -0
  122. data/lib/sass/util/normalized_map.rb +130 -0
  123. data/lib/sass/util/ordered_hash.rb +192 -0
  124. data/lib/sass/util/subset_map.rb +11 -2
  125. data/lib/sass/util/test.rb +9 -0
  126. data/lib/sass/util.rb +565 -39
  127. data/lib/sass/version.rb +27 -15
  128. data/lib/sass.rb +39 -4
  129. data/test/sass/cache_test.rb +15 -0
  130. data/test/sass/compiler_test.rb +223 -0
  131. data/test/sass/conversion_test.rb +901 -107
  132. data/test/sass/css2sass_test.rb +94 -0
  133. data/test/sass/engine_test.rb +1059 -164
  134. data/test/sass/exec_test.rb +86 -0
  135. data/test/sass/extend_test.rb +933 -837
  136. data/test/sass/fixtures/test_staleness_check_across_importers.css +1 -0
  137. data/test/sass/fixtures/test_staleness_check_across_importers.scss +1 -0
  138. data/test/sass/functions_test.rb +995 -136
  139. data/test/sass/importer_test.rb +338 -18
  140. data/test/sass/logger_test.rb +58 -0
  141. data/test/sass/more_results/more_import.css +2 -2
  142. data/test/sass/plugin_test.rb +114 -30
  143. data/test/sass/results/cached_import_option.css +3 -0
  144. data/test/sass/results/filename_fn.css +3 -0
  145. data/test/sass/results/import.css +2 -2
  146. data/test/sass/results/import_charset.css +1 -0
  147. data/test/sass/results/import_charset_1_8.css +1 -0
  148. data/test/sass/results/import_charset_ibm866.css +1 -0
  149. data/test/sass/results/import_content.css +1 -0
  150. data/test/sass/results/script.css +1 -1
  151. data/test/sass/results/scss_import.css +2 -2
  152. data/test/sass/results/units.css +2 -2
  153. data/test/sass/script_conversion_test.rb +43 -1
  154. data/test/sass/script_test.rb +380 -36
  155. data/test/sass/scss/css_test.rb +257 -75
  156. data/test/sass/scss/scss_test.rb +2322 -110
  157. data/test/sass/source_map_test.rb +887 -0
  158. data/test/sass/templates/_cached_import_option_partial.scss +1 -0
  159. data/test/sass/templates/_double_import_loop2.sass +1 -0
  160. data/test/sass/templates/_filename_fn_import.scss +11 -0
  161. data/test/sass/templates/_imported_content.sass +3 -0
  162. data/test/sass/templates/_same_name_different_partiality.scss +1 -0
  163. data/test/sass/templates/bork5.sass +3 -0
  164. data/test/sass/templates/cached_import_option.scss +3 -0
  165. data/test/sass/templates/double_import_loop1.sass +1 -0
  166. data/test/sass/templates/filename_fn.scss +18 -0
  167. data/test/sass/templates/import_charset.sass +2 -0
  168. data/test/sass/templates/import_charset_1_8.sass +2 -0
  169. data/test/sass/templates/import_charset_ibm866.sass +2 -0
  170. data/test/sass/templates/import_content.sass +4 -0
  171. data/test/sass/templates/same_name_different_ext.sass +2 -0
  172. data/test/sass/templates/same_name_different_ext.scss +1 -0
  173. data/test/sass/templates/same_name_different_partiality.scss +1 -0
  174. data/test/sass/templates/single_import_loop.sass +1 -0
  175. data/test/sass/templates/subdir/import_up1.scss +1 -0
  176. data/test/sass/templates/subdir/import_up2.scss +1 -0
  177. data/test/sass/test_helper.rb +1 -1
  178. data/test/sass/util/multibyte_string_scanner_test.rb +147 -0
  179. data/test/sass/util/normalized_map_test.rb +51 -0
  180. data/test/sass/util_test.rb +183 -0
  181. data/test/sass/value_helpers_test.rb +181 -0
  182. data/test/test_helper.rb +45 -5
  183. data/vendor/listen/CHANGELOG.md +228 -0
  184. data/vendor/listen/CONTRIBUTING.md +38 -0
  185. data/vendor/listen/Gemfile +30 -0
  186. data/vendor/listen/Guardfile +8 -0
  187. data/vendor/{fssm → listen}/LICENSE +1 -1
  188. data/vendor/listen/README.md +315 -0
  189. data/vendor/listen/Rakefile +47 -0
  190. data/vendor/listen/Vagrantfile +96 -0
  191. data/vendor/listen/lib/listen/adapter.rb +214 -0
  192. data/vendor/listen/lib/listen/adapters/bsd.rb +112 -0
  193. data/vendor/listen/lib/listen/adapters/darwin.rb +85 -0
  194. data/vendor/listen/lib/listen/adapters/linux.rb +113 -0
  195. data/vendor/listen/lib/listen/adapters/polling.rb +67 -0
  196. data/vendor/listen/lib/listen/adapters/windows.rb +87 -0
  197. data/vendor/listen/lib/listen/dependency_manager.rb +126 -0
  198. data/vendor/listen/lib/listen/directory_record.rb +371 -0
  199. data/vendor/listen/lib/listen/listener.rb +225 -0
  200. data/vendor/listen/lib/listen/multi_listener.rb +143 -0
  201. data/vendor/listen/lib/listen/turnstile.rb +28 -0
  202. data/vendor/listen/lib/listen/version.rb +3 -0
  203. data/vendor/listen/lib/listen.rb +40 -0
  204. data/vendor/listen/listen.gemspec +22 -0
  205. data/vendor/listen/spec/listen/adapter_spec.rb +183 -0
  206. data/vendor/listen/spec/listen/adapters/bsd_spec.rb +36 -0
  207. data/vendor/listen/spec/listen/adapters/darwin_spec.rb +37 -0
  208. data/vendor/listen/spec/listen/adapters/linux_spec.rb +47 -0
  209. data/vendor/listen/spec/listen/adapters/polling_spec.rb +68 -0
  210. data/vendor/listen/spec/listen/adapters/windows_spec.rb +30 -0
  211. data/vendor/listen/spec/listen/dependency_manager_spec.rb +107 -0
  212. data/vendor/listen/spec/listen/directory_record_spec.rb +1225 -0
  213. data/vendor/listen/spec/listen/listener_spec.rb +169 -0
  214. data/vendor/listen/spec/listen/multi_listener_spec.rb +174 -0
  215. data/vendor/listen/spec/listen/turnstile_spec.rb +56 -0
  216. data/vendor/listen/spec/listen_spec.rb +73 -0
  217. data/vendor/listen/spec/spec_helper.rb +21 -0
  218. data/vendor/listen/spec/support/adapter_helper.rb +629 -0
  219. data/vendor/listen/spec/support/directory_record_helper.rb +55 -0
  220. data/vendor/listen/spec/support/fixtures_helper.rb +29 -0
  221. data/vendor/listen/spec/support/listeners_helper.rb +156 -0
  222. data/vendor/listen/spec/support/platform_helper.rb +15 -0
  223. metadata +344 -271
  224. data/lib/sass/less.rb +0 -382
  225. data/lib/sass/script/bool.rb +0 -18
  226. data/lib/sass/script/funcall.rb +0 -162
  227. data/lib/sass/script/list.rb +0 -76
  228. data/lib/sass/script/literal.rb +0 -245
  229. data/lib/sass/script/variable.rb +0 -54
  230. data/lib/sass/scss/sass_parser.rb +0 -11
  231. data/test/sass/less_conversion_test.rb +0 -653
  232. data/vendor/fssm/README.markdown +0 -55
  233. data/vendor/fssm/Rakefile +0 -59
  234. data/vendor/fssm/VERSION.yml +0 -5
  235. data/vendor/fssm/example.rb +0 -9
  236. data/vendor/fssm/fssm.gemspec +0 -77
  237. data/vendor/fssm/lib/fssm/backends/fsevents.rb +0 -36
  238. data/vendor/fssm/lib/fssm/backends/inotify.rb +0 -26
  239. data/vendor/fssm/lib/fssm/backends/polling.rb +0 -25
  240. data/vendor/fssm/lib/fssm/backends/rubycocoa/fsevents.rb +0 -131
  241. data/vendor/fssm/lib/fssm/monitor.rb +0 -26
  242. data/vendor/fssm/lib/fssm/path.rb +0 -91
  243. data/vendor/fssm/lib/fssm/pathname.rb +0 -502
  244. data/vendor/fssm/lib/fssm/state/directory.rb +0 -57
  245. data/vendor/fssm/lib/fssm/state/file.rb +0 -24
  246. data/vendor/fssm/lib/fssm/support.rb +0 -63
  247. data/vendor/fssm/lib/fssm/tree.rb +0 -176
  248. data/vendor/fssm/lib/fssm.rb +0 -33
  249. data/vendor/fssm/profile/prof-cache.rb +0 -40
  250. data/vendor/fssm/profile/prof-fssm-pathname.html +0 -1231
  251. data/vendor/fssm/profile/prof-pathname.rb +0 -68
  252. data/vendor/fssm/profile/prof-plain-pathname.html +0 -988
  253. data/vendor/fssm/profile/prof.html +0 -2379
  254. data/vendor/fssm/spec/path_spec.rb +0 -75
  255. data/vendor/fssm/spec/root/duck/quack.txt +0 -0
  256. data/vendor/fssm/spec/root/file.css +0 -0
  257. data/vendor/fssm/spec/root/file.rb +0 -0
  258. data/vendor/fssm/spec/root/file.yml +0 -0
  259. data/vendor/fssm/spec/root/moo/cow.txt +0 -0
  260. data/vendor/fssm/spec/spec_helper.rb +0 -14
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
  # -*- coding: utf-8 -*-
3
- require 'test_helper'
4
- require 'sass/test_helper'
3
+ require File.dirname(__FILE__) + '/../test_helper'
4
+ require File.dirname(__FILE__) + '/test_helper'
5
5
  require 'sass/engine'
6
6
  require 'stringio'
7
7
  require 'mock_importer'
@@ -9,10 +9,23 @@ require 'pathname'
9
9
 
10
10
  module Sass::Script::Functions::UserFunctions
11
11
  def option(name)
12
- Sass::Script::String.new(@options[name.value.to_sym].to_s)
12
+ Sass::Script::Value::String.new(@options[name.value.to_sym].to_s)
13
+ end
14
+
15
+ def set_a_variable(name, value)
16
+ environment.set_var(name.value, value)
17
+ return Sass::Script::Value::Null.new
18
+ end
19
+
20
+ def get_a_variable(name)
21
+ environment.var(name.value) || Sass::Script::Value::String.new("undefined")
13
22
  end
14
23
  end
15
24
 
25
+ module Sass::Script::Functions
26
+ include Sass::Script::Functions::UserFunctions
27
+ end
28
+
16
29
  class SassEngineTest < Test::Unit::TestCase
17
30
  FAKE_FILE_NAME = __FILE__.gsub(/rb$/,"sass")
18
31
  # A map of erroneous Sass documents to the error messages they should produce.
@@ -39,8 +52,11 @@ MSG
39
52
  "a\n b: c;" => 'Invalid CSS after "c": expected expression (e.g. 1px, bold), was ";"',
40
53
  ".foo ^bar\n a: b" => ['Invalid CSS after ".foo ": expected selector, was "^bar"', 1],
41
54
  "a\n @extend .foo ^bar" => 'Invalid CSS after ".foo ": expected selector, was "^bar"',
42
- "a: b" => 'Properties are only allowed within rules, directives, or other properties.',
43
- ":a b" => 'Properties are only allowed within rules, directives, or other properties.',
55
+ "a\n @extend .foo .bar" => "Can't extend .foo .bar: can't extend nested selectors",
56
+ "a\n @extend >" => "Can't extend >: invalid selector",
57
+ "a\n @extend &.foo" => "Can't extend &.foo: can't extend parent selectors",
58
+ "a: b" => 'Properties are only allowed within rules, directives, mixin includes, or other properties.',
59
+ ":a b" => 'Properties are only allowed within rules, directives, mixin includes, or other properties.',
44
60
  "$" => 'Invalid variable: "$".',
45
61
  "$a" => 'Invalid variable: "$a".',
46
62
  "$ a" => 'Invalid variable: "$ a".',
@@ -51,29 +67,26 @@ MSG
51
67
  "$a: 1b <= 2c" => "Incompatible units: 'c' and 'b'.",
52
68
  "$a: 1b >= 2c" => "Incompatible units: 'c' and 'b'.",
53
69
  "a\n b: 1b * 2c" => "2b*c isn't a valid CSS value.",
54
- "a\n b: 1b % 2c" => "Cannot modulo by a number with units: 2c.",
70
+ "a\n b: 1b % 2c" => "Incompatible units: 'c' and 'b'.",
55
71
  "$a: 2px + #ccc" => "Cannot add a number with units (2px) to a color (#cccccc).",
56
72
  "$a: #ccc + 2px" => "Cannot add a number with units (2px) to a color (#cccccc).",
57
73
  "& a\n :b c" => ["Base-level rules cannot contain the parent-selector-referencing character '&'.", 1],
58
74
  "a\n :b\n c" => "Illegal nesting: Only properties may be nested beneath properties.",
59
75
  "$a: b\n :c d\n" => "Illegal nesting: Nothing may be nested beneath variable declarations.",
60
- "@import foo.sass" => "File to import not found or unreadable: foo.sass.",
61
76
  "$a: b\n :c d\n" => "Illegal nesting: Nothing may be nested beneath variable declarations.",
62
- "@import foo.sass" => <<MSG,
63
- File to import not found or unreadable: foo.sass.
64
- Load path: .
65
- MSG
66
77
  "@import templates/basic\n foo" => "Illegal nesting: Nothing may be nested beneath import directives.",
67
78
  "foo\n @import foo.css" => "CSS import directives may only be used at the root of a document.",
68
79
  "@if true\n @import foo" => "Import directives may not be used within control directives or mixins.",
80
+ "@if true\n .foo\n @import foo" => "Import directives may not be used within control directives or mixins.",
69
81
  "@mixin foo\n @import foo" => "Import directives may not be used within control directives or mixins.",
82
+ "@mixin foo\n .foo\n @import foo" => "Import directives may not be used within control directives or mixins.",
83
+ "@import foo;" => "Invalid @import: expected end of line, was \";\".",
70
84
  '$foo: "bar" "baz" !' => %Q{Invalid CSS after ""bar" "baz" ": expected expression (e.g. 1px, bold), was "!"},
71
- '$foo: "bar" "baz" $' => %Q{Invalid CSS after ""bar" "baz" ": expected expression (e.g. 1px, bold), was "$"},
85
+ '$foo: "bar" "baz" $' => %Q{Invalid CSS after ""bar" "baz" ": expected expression (e.g. 1px, bold), was "$"}, #'
72
86
  "=foo\n :color red\n.bar\n +bang" => "Undefined mixin 'bang'.",
73
87
  "=foo\n :color red\n.bar\n +bang_bop" => "Undefined mixin 'bang_bop'.",
74
88
  "=foo\n :color red\n.bar\n +bang-bop" => "Undefined mixin 'bang-bop'.",
75
- ".bar\n =foo\n :color red\n" => ["Mixins may only be defined at the root of a document.", 2],
76
- "=foo\n :color red\n.bar\n +foo\n :color red" => "Illegal nesting: Nothing may be nested beneath mixin directives.",
89
+ ".foo\n =foo\n :color red\n.bar\n +foo" => "Undefined mixin 'foo'.",
77
90
  " a\n b: c" => ["Indenting at the beginning of the document is illegal.", 1],
78
91
  " \n \n\t\n a\n b: c" => ["Indenting at the beginning of the document is illegal.", 4],
79
92
  "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],
@@ -89,12 +102,12 @@ MSG
89
102
  "=a(,)" => 'Invalid CSS after "(": expected variable (e.g. $foo), was ",)"',
90
103
  "=a($)" => 'Invalid CSS after "(": expected variable (e.g. $foo), was "$)"',
91
104
  "=a($foo bar)" => 'Invalid CSS after "($foo ": expected ")", was "bar)"',
92
- "=foo\n bar: baz\n+foo" => ["Properties are only allowed within rules, directives, or other properties.", 2],
105
+ "=foo\n bar: baz\n+foo" => ["Properties are only allowed within rules, directives, mixin includes, or other properties.", 2],
93
106
  "a-\#{$b\n c: d" => ['Invalid CSS after "a-#{$b": expected "}", was ""', 1],
94
107
  "=a($b: 1, $c)" => "Required argument $c must come before any optional arguments.",
95
108
  "=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 parameter $b.",
109
+ "=a($b: 1)\n a: $b\ndiv\n +a(1,$c: 3)" => "Mixin a doesn't have an argument named $c.",
110
+ "=a($b)\n a: $b\ndiv\n +a" => "Mixin a is missing argument $b.",
98
111
  "@function foo()\n 1 + 2" => "Functions can only contain variable declarations and control directives.",
99
112
  "@function foo()\n foo: bar" => "Functions can only contain variable declarations and control directives.",
100
113
  "@function foo()\n foo: bar\n @return 3" => ["Functions can only contain variable declarations and control directives.", 2],
@@ -105,9 +118,10 @@ MSG
105
118
  "@function foo($)\n @return 1" => ['Invalid CSS after "(": expected variable (e.g. $foo), was "$)"', 1],
106
119
  "@function foo()\n @return" => 'Invalid @return: expected expression.',
107
120
  "@function foo()\n @return 1\n $var: val" => 'Illegal nesting: Nothing may be nested beneath return directives.',
108
- "foo\n @function bar()\n @return 1" => ['Functions may only be defined at the root of a document.', 2],
109
- "@function foo($a)\n @return 1\na\n b: foo()" => 'Function foo is missing parameter $a.',
110
- "@function foo()\n @return 1\na\n b: foo(2)" => 'Wrong number of arguments (1 for 0) for `foo\'',
121
+ "@function foo($a)\n @return 1\na\n b: foo()" => 'Function foo is missing argument $a.',
122
+ "@function foo()\n @return 1\na\n b: foo(2)" => 'Function foo takes 0 arguments but 1 was passed.',
123
+ "@function foo()\n @return 1\na\n b: foo($a: 1)" => "Function foo doesn't have an argument named $a.",
124
+ "@function foo()\n @return 1\na\n b: foo($a: 1, $b: 2)" => "Function foo doesn't have the following arguments: $a, $b.",
111
125
  "@return 1" => '@return may only be used within a function.',
112
126
  "@if true\n @return 1" => '@return may only be used within a function.',
113
127
  "@mixin foo\n @return 1\n@include foo" => ['@return may only be used within a function.', 2],
@@ -121,7 +135,7 @@ MSG
121
135
  '@for $a from "foo" to 1' => '"foo" is not an integer.',
122
136
  '@for $a from 1 to "2"' => '"2" is not an integer.',
123
137
  '@for $a from 1 to "foo"' => '"foo" is not an integer.',
124
- '@for $a from 1 to 1.232323' => '1.232 is not an integer.',
138
+ '@for $a from 1 to 1.232323' => '1.23232 is not an integer.',
125
139
  '@for $a from 1px to 3em' => "Incompatible units: 'em' and 'px'.",
126
140
  '@if' => "Invalid if directive '@if': expected expression.",
127
141
  '@while' => "Invalid while directive '@while': expected expression.",
@@ -133,22 +147,30 @@ MSG
133
147
  '+foo(1 + 1: 2)' => 'Invalid CSS after "(1 + 1": expected comma, was ": 2)"',
134
148
  '+foo($var: )' => 'Invalid CSS after "($var: ": expected mixin argument, was ")"',
135
149
  '+foo($var: a, $var: b)' => 'Keyword argument "$var" passed more than once',
136
- '+foo($var-var: a, $var_var: b)' => 'Keyword argument "$var-var" passed more than once',
137
- '+foo($var_var: a, $var-var: b)' => 'Keyword argument "$var_var" passed more than once',
150
+ '+foo($var-var: a, $var_var: b)' => 'Keyword argument "$var_var" passed more than once',
151
+ '+foo($var_var: a, $var-var: b)' => 'Keyword argument "$var-var" passed more than once',
138
152
  "a\n b: foo(1 + 1: 2)" => 'Invalid CSS after "foo(1 + 1": expected comma, was ": 2)"',
139
153
  "a\n b: foo($var: )" => 'Invalid CSS after "foo($var: ": expected function argument, was ")"',
140
154
  "a\n b: foo($var: a, $var: b)" => 'Keyword argument "$var" passed more than once',
141
- "a\n b: foo($var-var: a, $var_var: b)" => 'Keyword argument "$var-var" passed more than once',
142
- "a\n b: foo($var_var: a, $var-var: b)" => 'Keyword argument "$var_var" passed more than once',
155
+ "a\n b: foo($var-var: a, $var_var: b)" => 'Keyword argument "$var_var" passed more than once',
156
+ "a\n b: foo($var_var: a, $var-var: b)" => 'Keyword argument "$var-var" passed more than once',
143
157
  "@if foo\n @extend .bar" => ["Extend directives may only be used within rules.", 2],
144
158
  "$var: true\n@while $var\n @extend .bar\n $var: false" => ["Extend directives may only be used within rules.", 3],
145
159
  "@for $i from 0 to 1\n @extend .bar" => ["Extend directives may only be used within rules.", 2],
146
160
  "@mixin foo\n @extend .bar\n@include foo" => ["Extend directives may only be used within rules.", 2],
161
+ "foo %\n a: b" => ['Invalid CSS after "foo %": expected placeholder name, was ""', 1],
162
+ "=foo\n @content error" => "Invalid content directive. Trailing characters found: \"error\".",
163
+ "=foo\n @content\n b: c" => "Illegal nesting: Nothing may be nested beneath @content directives.",
164
+ "@content" => '@content may only be used within a mixin.',
165
+ "=simple\n .simple\n color: red\n+simple\n color: blue" => ['Mixin "simple" does not accept a content block.', 4],
166
+ "@import \"foo\" // bar" => "Invalid CSS after \"\"foo\" \": expected media query list, was \"// bar\"",
167
+ "@at-root\n a: b" => "Properties are only allowed within rules, directives, mixin includes, or other properties.",
147
168
 
148
169
  # Regression tests
149
170
  "a\n b:\n c\n d" => ["Illegal nesting: Only properties may be nested beneath properties.", 3],
150
171
  "& foo\n bar: baz\n blat: bang" => ["Base-level rules cannot contain the parent-selector-referencing character '&'.", 1],
151
172
  "a\n b: c\n& foo\n bar: baz\n blat: bang" => ["Base-level rules cannot contain the parent-selector-referencing character '&'.", 3],
173
+ "@" => "Invalid directive: '@'.",
152
174
  }
153
175
 
154
176
  def teardown
@@ -208,7 +230,35 @@ MSG
208
230
  assert_equal("p {\n a: b; }\n p q {\n c: d; }\n",
209
231
  render("p\n\ta: b\n\tq\n\t\tc: d\n"))
210
232
  end
211
-
233
+
234
+ def test_import_same_name_different_ext
235
+ assert_raise_message Sass::SyntaxError, <<ERROR do
236
+ It's not clear which file to import for '@import "same_name_different_ext"'.
237
+ Candidates:
238
+ same_name_different_ext.sass
239
+ same_name_different_ext.scss
240
+ Please delete or rename all but one of these files.
241
+ ERROR
242
+ options = {:load_paths => [File.dirname(__FILE__) + '/templates/']}
243
+ munge_filename options
244
+ Sass::Engine.new("@import 'same_name_different_ext'", options).render
245
+ end
246
+ end
247
+
248
+ def test_import_same_name_different_partiality
249
+ assert_raise_message Sass::SyntaxError, <<ERROR do
250
+ It's not clear which file to import for '@import "same_name_different_partiality"'.
251
+ Candidates:
252
+ _same_name_different_partiality.scss
253
+ same_name_different_partiality.scss
254
+ Please delete or rename all but one of these files.
255
+ ERROR
256
+ options = {:load_paths => [File.dirname(__FILE__) + '/templates/']}
257
+ munge_filename options
258
+ Sass::Engine.new("@import 'same_name_different_partiality'", options).render
259
+ end
260
+ end
261
+
212
262
  EXCEPTION_MAP.each do |key, value|
213
263
  define_method("test_exception (#{key.inspect})") do
214
264
  line = 10
@@ -310,6 +360,23 @@ SASS
310
360
  end
311
361
  end
312
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
+
313
380
  def test_mixin_exception
314
381
  render(<<SASS)
315
382
  =error-mixin($a)
@@ -400,61 +467,59 @@ SASS
400
467
  assert_hash_has(err.sass_backtrace[4], :filename => nil, :mixin => nil, :line => 1)
401
468
  end
402
469
 
403
- def test_basic_mixin_loop_exception
404
- render <<SASS
405
- @mixin foo
406
- @include foo
407
- @include foo
408
- SASS
409
- assert(false, "Exception not raised")
410
- rescue Sass::SyntaxError => err
411
- assert_equal("An @include loop has been found: foo includes itself", err.message)
412
- assert_hash_has(err.sass_backtrace[0], :mixin => "foo", :line => 2)
413
- end
470
+ def test_recursive_mixin
471
+ assert_equal <<CSS, render(<<SASS)
472
+ .foo .bar .baz {
473
+ color: blue; }
474
+ .foo .bar .qux {
475
+ color: red; }
476
+ .foo .zap {
477
+ color: green; }
478
+ CSS
479
+ @mixin map-to-rule($map-or-color)
480
+ @if type-of($map-or-color) == map
481
+ @each $key, $value in $map-or-color
482
+ .\#{$key}
483
+ @include map-to-rule($value)
484
+ @else
485
+ color: $map-or-color
414
486
 
415
- def test_double_mixin_loop_exception
416
- render <<SASS
417
- @mixin foo
418
- @include bar
419
- @mixin bar
420
- @include foo
421
- @include foo
487
+ @include map-to-rule((foo: (bar: (baz: blue, qux: red), zap: green)))
422
488
  SASS
423
- assert(false, "Exception not raised")
424
- rescue Sass::SyntaxError => err
425
- assert_equal(<<MESSAGE.rstrip, err.message)
426
- An @include loop has been found:
427
- foo includes bar
428
- bar includes foo
429
- MESSAGE
430
- assert_hash_has(err.sass_backtrace[0], :mixin => "bar", :line => 4)
431
- assert_hash_has(err.sass_backtrace[1], :mixin => "foo", :line => 2)
432
489
  end
433
490
 
434
- def test_deep_mixin_loop_exception
435
- render <<SASS
436
- @mixin foo
437
- @include bar
491
+ def test_double_import_loop_exception
492
+ importer = MockImporter.new
493
+ importer.add_import("foo", "@import 'bar'")
494
+ importer.add_import("bar", "@import 'foo'")
438
495
 
439
- @mixin bar
440
- @include baz
496
+ engine = Sass::Engine.new('@import "foo"', :filename => filename_for_test,
497
+ :load_paths => [importer], :importer => importer)
441
498
 
442
- @mixin baz
443
- @include foo
499
+ assert_raise_message(Sass::SyntaxError, <<ERR.rstrip) {engine.render}
500
+ An @import loop has been found:
501
+ #{filename_for_test} imports foo
502
+ foo imports bar
503
+ bar imports foo
504
+ ERR
505
+ end
444
506
 
445
- @include foo
446
- SASS
447
- assert(false, "Exception not raised")
448
- rescue Sass::SyntaxError => err
449
- assert_equal(<<MESSAGE.rstrip, err.message)
450
- An @include loop has been found:
451
- foo includes bar
452
- bar includes baz
453
- baz includes foo
454
- MESSAGE
455
- assert_hash_has(err.sass_backtrace[0], :mixin => "baz", :line => 8)
456
- assert_hash_has(err.sass_backtrace[1], :mixin => "bar", :line => 5)
457
- assert_hash_has(err.sass_backtrace[2], :mixin => "foo", :line => 2)
507
+ def test_deep_import_loop_exception
508
+ importer = MockImporter.new
509
+ importer.add_import("foo", "@import 'bar'")
510
+ importer.add_import("bar", "@import 'baz'")
511
+ importer.add_import("baz", "@import 'foo'")
512
+
513
+ engine = Sass::Engine.new('@import "foo"', :filename => filename_for_test,
514
+ :load_paths => [importer], :importer => importer)
515
+
516
+ assert_raise_message(Sass::SyntaxError, <<ERR.rstrip) {engine.render}
517
+ An @import loop has been found:
518
+ #{filename_for_test} imports foo
519
+ foo imports bar
520
+ bar imports baz
521
+ baz imports foo
522
+ ERR
458
523
  end
459
524
 
460
525
  def test_exception_css_with_offset
@@ -528,7 +593,7 @@ SASS
528
593
  rescue Sass::SyntaxError => e
529
594
  assert_equal(<<CSS, Sass::SyntaxError.exception_to_css(e, opts).split("\n")[0..11].join("\n"))
530
595
  /*
531
- Syntax error: Properties are only allowed within rules, directives, or other properties.
596
+ Syntax error: Properties are only allowed within rules, directives, mixin includes, or other properties.
532
597
  on line 4 of test_cssize_exception_css_inline.sass
533
598
 
534
599
  1: .filler
@@ -552,11 +617,29 @@ CSS
552
617
  render("@import \"http://fonts.googleapis.com/css?family=Droid+Sans\""))
553
618
  end
554
619
 
555
- def test_http_import_with_interpolation
556
- assert_equal("@import url(http://fonts.googleapis.com/css?family=Droid+Sans);\n",
557
- render("$family: unquote(\"Droid+Sans\")\n@import \"http://fonts.googleapis.com/css?family=\#{$family}\"\n"))
558
- assert_equal("@import url(\"http://fonts.googleapis.com/css?family=Droid+Sans\");\n",
559
- render("$family: unquote(\"Droid+Sans\")\n@import url(\"http://fonts.googleapis.com/css?family=\#{$family}\")\n"))
620
+ def test_protocol_relative_import
621
+ assert_equal("@import url(//fonts.googleapis.com/css?family=Droid+Sans);\n",
622
+ render("@import \"//fonts.googleapis.com/css?family=Droid+Sans\""))
623
+ end
624
+
625
+ def test_import_with_interpolation
626
+ assert_equal(<<CSS, render(<<SASS))
627
+ @import url("http://fonts.googleapis.com/css?family=Droid+Sans");
628
+ CSS
629
+ $family: unquote("Droid+Sans")
630
+ @import url("http://fonts.googleapis.com/css?family=\#{$family}")
631
+ SASS
632
+ end
633
+
634
+ def test_import_with_dynamic_media_query
635
+ assert_equal(<<CSS, render(<<SASS))
636
+ @import "foo" print and (-webkit-min-device-pixel-ratio-foo: 25);
637
+ CSS
638
+ $media: print
639
+ $key: -webkit-min-device-pixel-ratio
640
+ $value: 20
641
+ @import "foo" \#{$media} and ($key + "-foo": $value + 5)
642
+ SASS
560
643
  end
561
644
 
562
645
  def test_url_import
@@ -579,12 +662,31 @@ CSS
579
662
  assert File.exists?(sassc_file)
580
663
  end
581
664
 
665
+ def test_import_from_global_load_paths
666
+ importer = MockImporter.new
667
+ importer.add_import("imported", "div{color:red}")
668
+ Sass.load_paths << importer
669
+
670
+ assert_equal "div {\n color: red; }\n", Sass::Engine.new('@import "imported"', :importer => importer).render
671
+ ensure
672
+ Sass.load_paths.clear
673
+ end
674
+
675
+ def test_nonexistent_import
676
+ assert_raise_message(Sass::SyntaxError, <<ERR.rstrip) do
677
+ File to import not found or unreadable: nonexistent.sass.
678
+ Load path: #{Dir.pwd}
679
+ ERR
680
+ render("@import nonexistent.sass")
681
+ end
682
+ end
683
+
582
684
  def test_nonexistent_extensionless_import
583
685
  assert_raise_message(Sass::SyntaxError, <<ERR.rstrip) do
584
686
  File to import not found or unreadable: nonexistent.
585
- Load path: .
687
+ Load path: #{Dir.pwd}
586
688
  ERR
587
- assert_equal("@import url(nonexistent.css);\n", render("@import nonexistent"))
689
+ render("@import nonexistent")
588
690
  end
589
691
  end
590
692
 
@@ -616,24 +718,6 @@ CSS
616
718
  SASS
617
719
  end
618
720
 
619
- def test_nested_import_with_toplevel_constructs
620
- Sass::Engine.new(".foo\n @import importee", :load_paths => [File.dirname(__FILE__) + '/templates/']).render
621
- rescue Sass::SyntaxError => err
622
- assert_equal(3, err.sass_line)
623
- assert_match(/(\/|^)importee\.sass$/, err.sass_filename)
624
-
625
- assert_hash_has(err.sass_backtrace.first,
626
- :filename => err.sass_filename, :line => err.sass_line)
627
-
628
- assert_nil(err.sass_backtrace[1][:filename])
629
- assert_equal(2, err.sass_backtrace[1][:line])
630
-
631
- assert_match(/(\/|^)importee\.sass:3$/, err.backtrace.first)
632
- assert_equal("(sass):2", err.backtrace[1])
633
- else
634
- assert(false, "Exception not raised for importing mixins nested")
635
- end
636
-
637
721
  def test_units
638
722
  renders_correctly "units"
639
723
  end
@@ -906,7 +990,7 @@ SASS
906
990
 
907
991
  def test_debug_info_without_filename
908
992
  assert_equal(<<CSS, Sass::Engine.new(<<SASS, :debug_info => true).render)
909
- @media -sass-debug-info{filename{font-family:}line{font-family:\\000031}}
993
+ @media -sass-debug-info{filename{}line{font-family:\\000031}}
910
994
  foo {
911
995
  a: b; }
912
996
  CSS
@@ -937,6 +1021,23 @@ foo
937
1021
  SASS
938
1022
  end
939
1023
 
1024
+ def test_debug_info_in_keyframes
1025
+ assert_equal(<<CSS, render(<<SASS, :debug_info => true))
1026
+ @-webkit-keyframes warm {
1027
+ from {
1028
+ color: black; }
1029
+
1030
+ to {
1031
+ color: red; } }
1032
+ CSS
1033
+ @-webkit-keyframes warm
1034
+ from
1035
+ color: black
1036
+ to
1037
+ color: red
1038
+ SASS
1039
+ end
1040
+
940
1041
  def test_empty_first_line
941
1042
  assert_equal("#a {\n b: c; }\n", render("#a\n\n b: c"))
942
1043
  end
@@ -978,6 +1079,7 @@ SASS
978
1079
  def test_guarded_assign
979
1080
  assert_equal("foo {\n a: b; }\n", render(%Q{$foo: b\n$foo: c !default\nfoo\n a: $foo}))
980
1081
  assert_equal("foo {\n a: b; }\n", render(%Q{$foo: b !default\nfoo\n a: $foo}))
1082
+ assert_equal("foo {\n a: b; }\n", render(%Q{$foo: null\n$foo: b !default\nfoo\n a: $foo}))
981
1083
  end
982
1084
 
983
1085
  def test_mixins
@@ -1063,6 +1165,35 @@ two
1063
1165
  +foo(#fff, 2px)
1064
1166
  three
1065
1167
  +foo(#fff, 2px, 3px)
1168
+ SASS
1169
+ assert_equal(<<CSS, render(<<SASS))
1170
+ one {
1171
+ color: white;
1172
+ padding: 1px;
1173
+ margin: 4px; }
1174
+
1175
+ two {
1176
+ color: white;
1177
+ padding: 2px;
1178
+ margin: 5px; }
1179
+
1180
+ three {
1181
+ color: white;
1182
+ padding: 2px;
1183
+ margin: 3px; }
1184
+ CSS
1185
+ $a: 5px
1186
+ =foo($a, $b: 1px, $c: null)
1187
+ $c: 3px + $b !default
1188
+ color: $a
1189
+ padding: $b
1190
+ margin: $c
1191
+ one
1192
+ +foo(#fff)
1193
+ two
1194
+ +foo(#fff, 2px)
1195
+ three
1196
+ +foo(#fff, 2px, 3px)
1066
1197
  SASS
1067
1198
  end
1068
1199
 
@@ -1149,6 +1280,58 @@ bar
1149
1280
  SASS
1150
1281
  end
1151
1282
 
1283
+ def test_function_with_missing_argument
1284
+ render(<<SASS)
1285
+ @function plus($var1, $var2)
1286
+ @return $var1 + $var2
1287
+
1288
+ bar
1289
+ a: plus($var2: bar)
1290
+ SASS
1291
+ flunk("Expected exception")
1292
+ rescue Sass::SyntaxError => e
1293
+ assert_equal("Function plus is missing argument $var1.", e.message)
1294
+ end
1295
+
1296
+ def test_function_with_extra_argument
1297
+ render(<<SASS)
1298
+ @function plus($var1, $var2)
1299
+ @return $var1 + $var2
1300
+
1301
+ bar
1302
+ a: plus($var1: foo, $var2: bar, $var3: baz)
1303
+ SASS
1304
+ flunk("Expected exception")
1305
+ rescue Sass::SyntaxError => e
1306
+ assert_equal("Function plus doesn't have an argument named $var3.", e.message)
1307
+ end
1308
+
1309
+ def test_function_with_positional_and_keyword_argument
1310
+ render(<<SASS)
1311
+ @function plus($var1, $var2)
1312
+ @return $var1 + $var2
1313
+
1314
+ bar
1315
+ a: plus(foo, bar, $var2: baz)
1316
+ SASS
1317
+ flunk("Expected exception")
1318
+ rescue Sass::SyntaxError => e
1319
+ assert_equal("Function plus was passed argument $var2 both by position and by name.", e.message)
1320
+ end
1321
+
1322
+ def test_function_with_keyword_before_positional_argument
1323
+ render(<<SASS)
1324
+ @function plus($var1, $var2)
1325
+ @return $var1 + $var2
1326
+
1327
+ bar
1328
+ a: plus($var2: foo, bar)
1329
+ SASS
1330
+ flunk("Expected exception")
1331
+ rescue Sass::SyntaxError => e
1332
+ assert_equal("Positional arguments must come before keyword arguments.", e.message)
1333
+ end
1334
+
1152
1335
  def test_function_with_if
1153
1336
  assert_equal(<<CSS, render(<<SASS))
1154
1337
  bar {
@@ -1181,6 +1364,56 @@ bar
1181
1364
  SASS
1182
1365
  end
1183
1366
 
1367
+ def test_user_defined_function_variable_scope
1368
+ render(<<SASS)
1369
+ bar
1370
+ -no-op: set-a-variable(variable, 5)
1371
+ a: $variable
1372
+ SASS
1373
+ flunk("Exception not raised for test_user_defined_function_variable_scope")
1374
+ rescue Sass::SyntaxError => e
1375
+ assert_equal('Undefined variable: "$variable".', e.message)
1376
+ end
1377
+
1378
+ def test_user_defined_function_can_change_global_variable
1379
+ assert_equal(<<CSS, render(<<SASS))
1380
+ bar {
1381
+ a: 5; }
1382
+ CSS
1383
+ $variable: 0
1384
+ bar
1385
+ $local: 10
1386
+ -no-op: set-a-variable(variable, 5)
1387
+ a: $variable
1388
+ SASS
1389
+ end
1390
+
1391
+ def test_user_defined_function_cannot_read_local_variable
1392
+ assert_equal(<<CSS, render(<<SASS))
1393
+ bar {
1394
+ global: 0;
1395
+ local: undefined; }
1396
+ CSS
1397
+ $global: 0
1398
+ bar
1399
+ $local: 10
1400
+ global: get-a-variable(global)
1401
+ local: get-a-variable(local)
1402
+ SASS
1403
+ end
1404
+
1405
+ def test_control_directive_in_nested_property
1406
+ assert_equal(<<CSS, render(<<SASS))
1407
+ foo {
1408
+ a-b: c; }
1409
+ CSS
1410
+ foo
1411
+ a:
1412
+ @if true
1413
+ b: c
1414
+ SASS
1415
+ end
1416
+
1184
1417
  def test_interpolation
1185
1418
  assert_equal("a-1 {\n b-2-3: c-3; }\n", render(<<SASS))
1186
1419
  $a: 1
@@ -1207,6 +1440,15 @@ SASS
1207
1440
  def test_if_directive
1208
1441
  assert_equal("a {\n b: 1; }\n", render(<<SASS))
1209
1442
  $var: true
1443
+ a
1444
+ @if $var
1445
+ b: 1
1446
+ @if not $var
1447
+ b: 2
1448
+ SASS
1449
+
1450
+ assert_equal("a {\n b: 2; }\n", render(<<SASS))
1451
+ $var: null
1210
1452
  a
1211
1453
  @if $var
1212
1454
  b: 1
@@ -1273,7 +1515,7 @@ $a: 5
1273
1515
  @while $a != 0
1274
1516
  a-\#{$a}
1275
1517
  blooble: gloop
1276
- $a: $a - 1
1518
+ $a: $a - 1 !global
1277
1519
  SASS
1278
1520
  end
1279
1521
 
@@ -1340,14 +1582,35 @@ a
1340
1582
  SASS
1341
1583
  end
1342
1584
 
1585
+ def test_destructuring_each
1586
+ assert_equal <<CSS, render(<<SCSS)
1587
+ a {
1588
+ foo: 1px;
1589
+ bar: 2px;
1590
+ baz: 3px; }
1591
+
1592
+ c {
1593
+ foo: "Value is bar";
1594
+ bar: "Value is baz";
1595
+ bang: "Value is "; }
1596
+ CSS
1597
+ a
1598
+ @each $name, $number in (foo: 1px, bar: 2px, baz: 3px)
1599
+ \#{$name}: $number
1600
+ c
1601
+ @each $key, $value in (foo bar) (bar, baz) bang
1602
+ \#{$key}: "Value is \#{$value}"
1603
+ SCSS
1604
+ end
1605
+
1343
1606
  def test_variable_reassignment
1344
1607
  assert_equal(<<CSS, render(<<SASS))
1345
1608
  a {
1346
1609
  b: 1;
1347
1610
  c: 2; }
1348
1611
  CSS
1349
- $a: 1
1350
1612
  a
1613
+ $a: 1
1351
1614
  b: $a
1352
1615
  $a: 2
1353
1616
  c: $a
@@ -1355,7 +1618,7 @@ SASS
1355
1618
  end
1356
1619
 
1357
1620
  def test_variable_scope
1358
- assert_equal(<<CSS, render(<<SASS))
1621
+ silence_warnings {assert_equal(<<CSS, render(<<SASS))}
1359
1622
  a {
1360
1623
  b-1: c;
1361
1624
  b-2: c;
@@ -1381,9 +1644,6 @@ SASS
1381
1644
 
1382
1645
  def test_hyphen_underscore_insensitive_variables
1383
1646
  assert_equal(<<CSS, render(<<SASS))
1384
- a {
1385
- b: c; }
1386
-
1387
1647
  d {
1388
1648
  e: 13;
1389
1649
  f: foobar; }
@@ -1391,10 +1651,8 @@ CSS
1391
1651
  $var-hyphen: 12
1392
1652
  $var_under: foo
1393
1653
 
1394
- a
1395
- $var_hyphen: 1 + $var_hyphen
1396
- $var-under: $var-under + bar
1397
- b: c
1654
+ $var_hyphen: 1 + $var_hyphen
1655
+ $var-under: $var-under + bar
1398
1656
 
1399
1657
  d
1400
1658
  e: $var-hyphen
@@ -1537,7 +1795,7 @@ SASS
1537
1795
 
1538
1796
  def test_loud_comment_in_compressed_mode
1539
1797
  assert_equal <<CSS, render(<<SASS, :style => :compressed)
1540
- foo{color:blue;/* foo
1798
+ foo{color:blue;/*! foo
1541
1799
  * bar
1542
1800
  */}
1543
1801
  CSS
@@ -1548,31 +1806,12 @@ foo
1548
1806
  */
1549
1807
  SASS
1550
1808
  end
1551
- def test_loud_comment_in_silent_comment
1552
- assert_equal <<CSS, render(<<SASS, :style => :compressed)
1553
- foo{color:blue;/* foo */
1554
- /* bar */
1555
- /* */
1556
- /* bip */
1557
- /* baz */}
1558
- CSS
1559
- foo
1560
- color: blue
1561
- //! foo
1562
- //! bar
1563
- //!
1564
- bip
1565
- baz
1566
- SASS
1567
- end
1568
1809
 
1569
1810
  def test_loud_comment_is_evaluated
1570
1811
  assert_equal <<CSS, render(<<SASS)
1571
- /*
1572
- * Hue: 327.216deg */
1812
+ /*! Hue: 327.21649deg */
1573
1813
  CSS
1574
- /*!
1575
- Hue: \#{hue(#f836a0)}
1814
+ /*! Hue: \#{hue(#f836a0)}
1576
1815
  SASS
1577
1816
  end
1578
1817
 
@@ -1627,6 +1866,26 @@ This selector doesn't have any properties and will not be rendered.
1627
1866
  END
1628
1867
  end
1629
1868
 
1869
+ def test_nonprinting_empty_property
1870
+ assert_equal(<<CSS, render(<<SASS))
1871
+ a {
1872
+ c: "";
1873
+ e: f; }
1874
+ CSS
1875
+ $null-value: null
1876
+ $empty-string: ''
1877
+ $empty-list: (null)
1878
+ a
1879
+ b: $null-value
1880
+ c: $empty-string
1881
+ d: $empty-list
1882
+ e: f
1883
+
1884
+ g
1885
+ h: null
1886
+ SASS
1887
+ end
1888
+
1630
1889
  def test_root_level_pseudo_class_with_new_properties
1631
1890
  assert_equal(<<CSS, render(<<SASS, :property_syntax => :new))
1632
1891
  :focus {
@@ -1772,22 +2031,22 @@ SASS
1772
2031
 
1773
2032
  def test_interpolation_doesnt_deep_unquote_strings
1774
2033
  assert_equal(<<CSS, render(<<SASS))
1775
- .foo- "bar" "baz" {
1776
- a: b; }
2034
+ .foo {
2035
+ a: "bar" "baz"; }
1777
2036
  CSS
1778
- .foo-\#{"bar" "baz"}
1779
- a: b
2037
+ .foo
2038
+ a: \#{"bar" "baz"}
1780
2039
  SASS
1781
2040
  end
1782
2041
 
1783
2042
  def test_warn_directive
1784
2043
  expected_warning = <<EXPECTATION
1785
2044
  WARNING: this is a warning
1786
- on line 4 of test_warn_directive_inline.sass
2045
+ on line 4 of test_warn_directive_inline.sass
1787
2046
 
1788
2047
  WARNING: this is a mixin warning
1789
- on line 2 of test_warn_directive_inline.sass, in `foo'
1790
- from line 7 of test_warn_directive_inline.sass
2048
+ on line 2 of test_warn_directive_inline.sass, in `foo'
2049
+ from line 7 of test_warn_directive_inline.sass
1791
2050
  EXPECTATION
1792
2051
  assert_warning expected_warning do
1793
2052
  assert_equal <<CSS, render(<<SASS)
@@ -1817,15 +2076,15 @@ SASS
1817
2076
  def test_warn_with_imports
1818
2077
  expected_warning = <<WARN
1819
2078
  WARNING: In the main file
1820
- on line 1 of #{File.dirname(__FILE__)}/templates/warn.sass
2079
+ on line 1 of #{File.dirname(__FILE__)}/templates/warn.sass
1821
2080
 
1822
2081
  WARNING: Imported
1823
- on line 1 of #{File.dirname(__FILE__)}/templates/warn_imported.sass
1824
- from line 2 of #{File.dirname(__FILE__)}/templates/warn.sass
2082
+ on line 1 of #{File.dirname(__FILE__)}/templates/warn_imported.sass
2083
+ from line 2 of #{File.dirname(__FILE__)}/templates/warn.sass
1825
2084
 
1826
2085
  WARNING: In an imported mixin
1827
- on line 4 of #{File.dirname(__FILE__)}/templates/warn_imported.sass, in `emits-a-warning'
1828
- from line 3 of #{File.dirname(__FILE__)}/templates/warn.sass
2086
+ on line 4 of #{File.dirname(__FILE__)}/templates/warn_imported.sass, in `emits-a-warning'
2087
+ from line 3 of #{File.dirname(__FILE__)}/templates/warn.sass
1829
2088
  WARN
1830
2089
  assert_warning expected_warning do
1831
2090
  renders_correctly "warn", :style => :compact, :load_paths => [File.dirname(__FILE__) + "/templates"]
@@ -1920,12 +2179,12 @@ SASS
1920
2179
 
1921
2180
  def test_double_media_bubbling
1922
2181
  assert_equal <<CSS, render(<<SASS)
1923
- @media bar and baz {
2182
+ @media bar and (a: b) {
1924
2183
  .foo {
1925
2184
  c: d; } }
1926
2185
  CSS
1927
2186
  @media bar
1928
- @media baz
2187
+ @media (a: b)
1929
2188
  .foo
1930
2189
  c: d
1931
2190
  SASS
@@ -1934,18 +2193,61 @@ SASS
1934
2193
  @media bar {
1935
2194
  .foo {
1936
2195
  a: b; } }
1937
- @media bar and baz {
2196
+ @media bar and (a: b) {
1938
2197
  .foo {
1939
2198
  c: d; } }
1940
2199
  CSS
1941
2200
  .foo
1942
2201
  @media bar
1943
2202
  a: b
1944
- @media baz
2203
+ @media (a: b)
1945
2204
  c: d
1946
2205
  SASS
1947
2206
  end
1948
2207
 
2208
+ def test_double_media_bubbling_with_commas
2209
+ assert_equal <<CSS, render(<<SASS)
2210
+ @media (a: b) and (e: f), (c: d) and (e: f), (a: b) and (g: h), (c: d) and (g: h) {
2211
+ .foo {
2212
+ c: d; } }
2213
+ CSS
2214
+ @media (a: b), (c: d)
2215
+ @media (e: f), (g: h)
2216
+ .foo
2217
+ c: d
2218
+ SASS
2219
+ end
2220
+
2221
+ def test_double_media_bubbling_with_surrounding_rules
2222
+ assert_equal <<CSS, render(<<SASS)
2223
+ @media (min-width: 0) {
2224
+ a {
2225
+ a: a; }
2226
+
2227
+ b {
2228
+ before: b;
2229
+ after: b; } }
2230
+ @media (min-width: 0) and (max-width: 5000px) {
2231
+ b {
2232
+ x: x; } }
2233
+
2234
+ @media (min-width: 0) {
2235
+ c {
2236
+ c: c; } }
2237
+ CSS
2238
+ @media (min-width: 0)
2239
+ a
2240
+ a: a
2241
+ b
2242
+ before: b
2243
+ @media (max-width: 5000px)
2244
+ x: x
2245
+ after: b
2246
+ c
2247
+ c: c
2248
+ SASS
2249
+ end
2250
+
1949
2251
  def test_rule_media_rule_bubbling
1950
2252
  assert_equal <<CSS, render(<<SASS)
1951
2253
  @media bar {
@@ -1972,9 +2274,10 @@ SASS
1972
2274
  @media print {
1973
2275
  .outside {
1974
2276
  color: black; } }
1975
- @media print and nested {
1976
- .outside .inside {
1977
- border: 1px solid black; } }
2277
+ @media print and (a: b) {
2278
+ .outside .inside {
2279
+ border: 1px solid black; } }
2280
+
1978
2281
  .outside .middle {
1979
2282
  display: block; }
1980
2283
  CSS
@@ -1983,7 +2286,7 @@ CSS
1983
2286
  @media print
1984
2287
  color: black
1985
2288
  .inside
1986
- @media nested
2289
+ @media (a: b)
1987
2290
  border: 1px solid black
1988
2291
  background: blue
1989
2292
  .middle
@@ -2006,8 +2309,277 @@ CSS
2006
2309
  assert_equal css_str, render(sass_str)
2007
2310
  end
2008
2311
 
2312
+ def test_eliminated_media_bubbling
2313
+ assert_equal <<CSS, render(<<SASS)
2314
+ @media screen {
2315
+ a: b; }
2316
+ CSS
2317
+ @media screen
2318
+ a: b
2319
+ @media print
2320
+ c: d
2321
+ SASS
2322
+
2323
+ assert_equal <<CSS, render(<<SASS)
2324
+ @media not print {
2325
+ a: b; }
2326
+ CSS
2327
+ @media not print
2328
+ a: b
2329
+ @media print
2330
+ c: d
2331
+ SASS
2332
+
2333
+ assert_equal <<CSS, render(<<SASS)
2334
+ @media not print {
2335
+ a: b; }
2336
+ CSS
2337
+ @media not print
2338
+ a: b
2339
+ @media not screen
2340
+ c: d
2341
+ SASS
2342
+ end
2343
+
2344
+ def test_non_eliminated_media_bubbling
2345
+ assert_equal <<CSS, render(<<SASS)
2346
+ @media screen {
2347
+ a: b; }
2348
+ @media screen and (a: b) {
2349
+ c: d; }
2350
+ CSS
2351
+ @media screen
2352
+ a: b
2353
+ @media screen and (a: b)
2354
+ c: d
2355
+ SASS
2356
+
2357
+ assert_equal <<CSS, render(<<SASS)
2358
+ @media not print {
2359
+ a: b; }
2360
+ @media screen {
2361
+ c: d; }
2362
+ CSS
2363
+ @media not print
2364
+ a: b
2365
+ @media screen
2366
+ c: d
2367
+ SASS
2368
+
2369
+ assert_equal <<CSS, render(<<SASS)
2370
+ @media only screen {
2371
+ a: b; }
2372
+ @media only screen and (a: b) {
2373
+ c: d; }
2374
+ CSS
2375
+ @media only screen
2376
+ a: b
2377
+ @media screen and (a: b)
2378
+ c: d
2379
+ SASS
2380
+ end
2381
+
2382
+ def test_directive_interpolation
2383
+ assert_equal <<CSS, render(<<SASS)
2384
+ @foo bar12 qux {
2385
+ a: b; }
2386
+ CSS
2387
+ $baz: 12
2388
+ @foo bar\#{$baz} qux
2389
+ a: b
2390
+ SASS
2391
+ end
2392
+
2393
+ def test_media_interpolation
2394
+ assert_equal <<CSS, render(<<SASS)
2395
+ @media bar12 {
2396
+ a: b; }
2397
+ CSS
2398
+ $baz: 12
2399
+ @media bar\#{$baz}
2400
+ a: b
2401
+ SASS
2402
+ end
2403
+
2404
+ def test_variables_in_media
2405
+ assert_equal <<CSS, render(<<SASS)
2406
+ @media screen and (-webkit-min-device-pixel-ratio-foo: 25), only print {
2407
+ a: b; }
2408
+ CSS
2409
+ $media1: screen
2410
+ $media2: print
2411
+ $var: -webkit-min-device-pixel-ratio
2412
+ $val: 20
2413
+ @media \#{$media1} and ($var + "-foo": $val + 5), only \#{$media2}
2414
+ a: b
2415
+ SASS
2416
+ end
2417
+
2418
+ def test_at_root
2419
+ assert_equal <<CSS, render(<<SASS)
2420
+ .bar {
2421
+ a: b; }
2422
+ CSS
2423
+ .foo
2424
+ @at-root
2425
+ .bar
2426
+ a: b
2427
+ SASS
2428
+ end
2429
+
2430
+ def test_at_root_with_selector
2431
+ assert_equal <<CSS, render(<<SASS)
2432
+ .bar {
2433
+ a: b; }
2434
+ CSS
2435
+ .foo
2436
+ @at-root .bar
2437
+ a: b
2438
+ SASS
2439
+ end
2440
+
2441
+ def test_at_root_with_query
2442
+ assert_equal <<CSS, render(<<SASS)
2443
+ .foo .bar {
2444
+ a: b; }
2445
+ CSS
2446
+ .foo
2447
+ @media screen
2448
+ @at-root (without: media)
2449
+ .bar
2450
+ a: b
2451
+ SASS
2452
+ end
2453
+
2454
+ def test_variable_assignment_with_global
2455
+ assert_no_warning {assert_equal(<<CSS, render(<<SASS))}
2456
+ .foo {
2457
+ a: x; }
2458
+
2459
+ .bar {
2460
+ b: x; }
2461
+ CSS
2462
+ $var: 1
2463
+
2464
+ .foo
2465
+ $var: x !global
2466
+ a: $var
2467
+
2468
+ .bar
2469
+ b: $var
2470
+ SASS
2471
+ end
2472
+
2009
2473
  # Regression tests
2010
2474
 
2475
+ def test_list_separator_with_arg_list
2476
+ assert_equal(<<CSS, render(<<SASS))
2477
+ .test {
2478
+ separator: comma; }
2479
+ CSS
2480
+ @mixin arglist-test($args...)
2481
+ separator: list-separator($args)
2482
+
2483
+ .test
2484
+ @include arglist-test(this, is, comma, separated)
2485
+ SASS
2486
+ end
2487
+
2488
+ def test_parent_mixin_in_content_nested
2489
+ assert_equal(<<CSS, render(<<SASS))
2490
+ a {
2491
+ b: c; }
2492
+ CSS
2493
+ =foo
2494
+ @content
2495
+
2496
+ =bar
2497
+ +foo
2498
+ +foo
2499
+ a
2500
+ b: c
2501
+
2502
+ +bar
2503
+ SASS
2504
+ end
2505
+
2506
+ def test_supports_bubbles
2507
+ assert_equal <<CSS, render(<<SASS)
2508
+ parent {
2509
+ background: orange; }
2510
+ @supports (perspective: 10px) or (-moz-perspective: 10px) {
2511
+ parent child {
2512
+ background: blue; } }
2513
+ CSS
2514
+ parent
2515
+ background: orange
2516
+ @supports (perspective: 10px) or (-moz-perspective: 10px)
2517
+ child
2518
+ background: blue
2519
+ SASS
2520
+ end
2521
+
2522
+ def test_line_numbers_with_dos_line_endings
2523
+ assert_equal <<CSS, render(<<SASS, :line_comments => true)
2524
+ /* line 5, test_line_numbers_with_dos_line_endings_inline.sass */
2525
+ .foo {
2526
+ a: b; }
2527
+ CSS
2528
+ \r
2529
+ \r
2530
+ \r
2531
+ \r
2532
+ .foo
2533
+ a: b
2534
+ SASS
2535
+ end
2536
+
2537
+ def test_variable_in_media_in_mixin
2538
+ assert_equal <<CSS, render(<<SASS)
2539
+ @media screen and (min-width: 10px) {
2540
+ body {
2541
+ background: red; } }
2542
+ @media screen and (min-width: 20px) {
2543
+ body {
2544
+ background: blue; } }
2545
+ CSS
2546
+ @mixin respond-to($width)
2547
+ @media screen and (min-width: $width)
2548
+ @content
2549
+
2550
+ body
2551
+ @include respond-to(10px)
2552
+ background: red
2553
+ @include respond-to(20px)
2554
+ background: blue
2555
+ SASS
2556
+ end
2557
+
2558
+ def test_interpolated_comment_in_mixin
2559
+ assert_equal <<CSS, render(<<SASS)
2560
+ /*! color: red */
2561
+ .foo {
2562
+ color: red; }
2563
+
2564
+ /*! color: blue */
2565
+ .foo {
2566
+ color: blue; }
2567
+
2568
+ /*! color: green */
2569
+ .foo {
2570
+ color: green; }
2571
+ CSS
2572
+ =foo($var)
2573
+ /*! color: \#{$var}
2574
+ .foo
2575
+ color: $var
2576
+
2577
+ +foo(red)
2578
+ +foo(blue)
2579
+ +foo(green)
2580
+ SASS
2581
+ end
2582
+
2011
2583
  def test_parens_in_mixins
2012
2584
  assert_equal(<<CSS, render(<<SASS))
2013
2585
  .foo {
@@ -2148,6 +2720,16 @@ CSS
2148
2720
  SASS
2149
2721
  end
2150
2722
 
2723
+ def test_silent_comment_in_prop_val_after_important
2724
+ assert_equal(<<CSS, render(<<SASS))
2725
+ .advanced {
2726
+ display: none !important; }
2727
+ CSS
2728
+ .advanced
2729
+ display: none !important // yeah, yeah. it's not really a style anyway.
2730
+ SASS
2731
+ end
2732
+
2151
2733
  def test_mixin_with_keyword_args
2152
2734
  assert_equal <<CSS, render(<<SASS)
2153
2735
  .mixed {
@@ -2271,15 +2853,6 @@ CSS
2271
2853
  SASS
2272
2854
  end
2273
2855
 
2274
- def test_comment_interpolation_warning
2275
- assert_warning(<<END) {render("/* \#{foo}")}
2276
- WARNING:
2277
- On line 1 of 'test_comment_interpolation_warning_inline.sass'
2278
- Comments will evaluate the contents of interpolations (\#{ ... }) in Sass 3.2.
2279
- Please escape the interpolation by adding a backslash before the hash sign.
2280
- END
2281
- end
2282
-
2283
2856
  def test_loud_comment_interpolations_can_be_escaped
2284
2857
  assert_equal <<CSS, render(<<SASS)
2285
2858
  /* \#{foo} */
@@ -2287,12 +2860,44 @@ CSS
2287
2860
  /* \\\#{foo}
2288
2861
  SASS
2289
2862
  assert_equal <<CSS, render(<<SASS)
2290
- /* \#{foo} */
2863
+ /*! \#{foo} */
2291
2864
  CSS
2292
2865
  /*! \\\#{foo}
2293
2866
  SASS
2294
2867
  end
2295
2868
 
2869
+ def test_selector_compression
2870
+ assert_equal <<CSS, render(<<SASS, :style => :compressed)
2871
+ a>b,c+d,:-moz-any(e,f,g){h:i}
2872
+ CSS
2873
+ a > b, c + d, :-moz-any(e, f, g)
2874
+ h: i
2875
+ SASS
2876
+ end
2877
+
2878
+ def test_comment_like_selector
2879
+ assert_raise_message(Sass::SyntaxError, 'Invalid CSS after "/": expected identifier, was " foo"') {render(<<SASS)}
2880
+ / foo
2881
+ a: b
2882
+ SASS
2883
+ end
2884
+
2885
+ def test_nested_empty_directive
2886
+ assert_equal <<CSS, render(<<SASS)
2887
+ @media screen {
2888
+ .foo {
2889
+ a: b; }
2890
+
2891
+ @unknown-directive; }
2892
+ CSS
2893
+ @media screen
2894
+ .foo
2895
+ a: b
2896
+
2897
+ @unknown-directive
2898
+ SASS
2899
+ end
2900
+
2296
2901
  # Encodings
2297
2902
 
2298
2903
  unless Sass::Util.ruby1_8?
@@ -2405,6 +3010,31 @@ CSS
2405
3010
  a: b
2406
3011
  SASS
2407
3012
  end
3013
+
3014
+ # Encoding Regression Test
3015
+
3016
+ def test_multibyte_prop_name
3017
+ assert_equal(<<CSS, render(<<SASS))
3018
+ @charset "UTF-8";
3019
+ #bar {
3020
+ cölor: blue; }
3021
+ CSS
3022
+ #bar
3023
+ cölor: blue
3024
+ SASS
3025
+ end
3026
+
3027
+ def test_multibyte_and_interpolation
3028
+ assert_equal(<<CSS, render(<<SCSS, :syntax => :scss))
3029
+ #bar {
3030
+ background: a 0%; }
3031
+ CSS
3032
+ #bar {
3033
+ // 
3034
+ background: \#{a} 0%;
3035
+ }
3036
+ SCSS
3037
+ end
2408
3038
  end
2409
3039
 
2410
3040
  def test_original_filename_set
@@ -2413,13 +3043,277 @@ SASS
2413
3043
 
2414
3044
  original_filename = filename_for_test
2415
3045
  engine = Sass::Engine.new('@import "imported"; div{color:blue}',
2416
- :filename => original_filename, :load_paths => [importer], :syntax => :scss)
3046
+ :filename => original_filename, :load_paths => [importer], :syntax => :scss, :importer => importer)
2417
3047
  engine.render
2418
3048
 
2419
3049
  assert_equal original_filename, engine.options[:original_filename]
2420
3050
  assert_equal original_filename, importer.engine("imported").options[:original_filename]
2421
3051
  end
2422
3052
 
3053
+ def test_changing_precision
3054
+ old_precision = Sass::Script::Value::Number.precision
3055
+ begin
3056
+ Sass::Script::Value::Number.precision = 8
3057
+ assert_equal <<CSS, render(<<SASS)
3058
+ div {
3059
+ maximum: 1.00000001;
3060
+ too-much: 1.0; }
3061
+ CSS
3062
+ div
3063
+ maximum : 1.00000001
3064
+ too-much: 1.000000001
3065
+ SASS
3066
+ ensure
3067
+ Sass::Script::Value::Number.precision = old_precision
3068
+ end
3069
+ end
3070
+
3071
+ def test_content
3072
+ assert_equal <<CSS, render(<<SASS)
3073
+ .children {
3074
+ background-color: red;
3075
+ color: blue;
3076
+ border-color: red; }
3077
+ CSS
3078
+ $color: blue
3079
+ =context($class, $color: red)
3080
+ .\#{$class}
3081
+ background-color: $color
3082
+ @content
3083
+ border-color: $color
3084
+ +context(children)
3085
+ color: $color
3086
+ SASS
3087
+ end
3088
+
3089
+ def test_selector_in_content
3090
+ assert_equal <<CSS, render(<<SASS)
3091
+ .parent {
3092
+ background-color: red;
3093
+ border-color: red; }
3094
+ .parent .children {
3095
+ color: blue; }
3096
+ CSS
3097
+ $color: blue
3098
+ =context($class, $color: red)
3099
+ .\#{$class}
3100
+ background-color: $color
3101
+ @content
3102
+ border-color: $color
3103
+ +context(parent)
3104
+ .children
3105
+ color: $color
3106
+ SASS
3107
+ end
3108
+
3109
+ def test_using_parent_mixin_in_content
3110
+ assert_equal <<CSS, render(<<SASS)
3111
+ .parent {
3112
+ before-color: red;
3113
+ after-color: red; }
3114
+ .parent .sibling {
3115
+ before-color: yellow;
3116
+ after-color: yellow; }
3117
+ .parent .sibling .child {
3118
+ before-color: green;
3119
+ color: blue;
3120
+ after-color: green; }
3121
+ CSS
3122
+ $color: blue
3123
+ =context($class, $color: red)
3124
+ .\#{$class}
3125
+ before-color: $color
3126
+ @content
3127
+ after-color: $color
3128
+ +context(parent)
3129
+ +context(sibling, $color: yellow)
3130
+ +context(child, $color: green)
3131
+ color: $color
3132
+ SASS
3133
+ end
3134
+
3135
+ def test_content_more_than_once
3136
+ assert_equal <<CSS, render(<<SASS)
3137
+ .once {
3138
+ color: blue; }
3139
+
3140
+ .twice {
3141
+ color: blue; }
3142
+ CSS
3143
+ $color: blue
3144
+ =context($class, $color: red)
3145
+ .once
3146
+ @content
3147
+ .twice
3148
+ @content
3149
+ +context(parent)
3150
+ color: $color
3151
+ SASS
3152
+ end
3153
+
3154
+ def test_content_with_variable
3155
+ assert_equal <<CSS, render(<<SASS)
3156
+ .foo {
3157
+ a: 1px; }
3158
+ CSS
3159
+ =foo
3160
+ .foo
3161
+ @content
3162
+ +foo
3163
+ $a: 1px
3164
+ a: $a
3165
+ SASS
3166
+ end
3167
+
3168
+ def test_nested_content_blocks
3169
+ assert_equal <<CSS, render(<<SASS)
3170
+ .foo {
3171
+ a: foo; }
3172
+ .foo .bar {
3173
+ a: bar; }
3174
+ .foo .bar .baz {
3175
+ a: baz; }
3176
+ .foo .bar .baz .outside {
3177
+ a: outside;
3178
+ color: red; }
3179
+ CSS
3180
+ $a: outside
3181
+ =baz($a: baz)
3182
+ .baz
3183
+ a: $a
3184
+ @content
3185
+ =bar($a: bar)
3186
+ .bar
3187
+ a: $a
3188
+ +baz
3189
+ @content
3190
+ =foo($a: foo)
3191
+ .foo
3192
+ a: $a
3193
+ +bar
3194
+ @content
3195
+ +foo
3196
+ .outside
3197
+ a: $a
3198
+ color: red
3199
+ SASS
3200
+ end
3201
+
3202
+ def test_content_not_seen_through_mixin
3203
+ assert_equal <<CSS, render(<<SASS)
3204
+ a foo {
3205
+ mixin: foo;
3206
+ a: b; }
3207
+ a foo bar {
3208
+ mixin: bar; }
3209
+ CSS
3210
+ =foo
3211
+ foo
3212
+ mixin: foo
3213
+ @content
3214
+ +bar
3215
+ =bar
3216
+ bar
3217
+ mixin: bar
3218
+ @content
3219
+ a
3220
+ +foo
3221
+ a: b
3222
+ SASS
3223
+ end
3224
+
3225
+ def test_content_backtrace_for_perform
3226
+ render(<<SASS)
3227
+ =foo
3228
+ @content
3229
+
3230
+ a
3231
+ +foo
3232
+ b: 1em + 2px
3233
+ SASS
3234
+ assert(false, "Expected exception")
3235
+ rescue Sass::SyntaxError => e
3236
+ assert_equal([
3237
+ {:mixin => '@content', :line => 6, :filename => 'test_content_backtrace_for_perform_inline.sass'},
3238
+ {:mixin => 'foo', :line => 2, :filename => 'test_content_backtrace_for_perform_inline.sass'},
3239
+ {:line => 5, :filename => 'test_content_backtrace_for_perform_inline.sass'},
3240
+ ], e.sass_backtrace)
3241
+ end
3242
+
3243
+ def test_content_backtrace_for_cssize
3244
+ render(<<SASS)
3245
+ =foo
3246
+ @content
3247
+
3248
+ a
3249
+ +foo
3250
+ @extend foo bar baz
3251
+ SASS
3252
+ assert(false, "Expected exception")
3253
+ rescue Sass::SyntaxError => e
3254
+ assert_equal([
3255
+ {:mixin => '@content', :line => 6, :filename => 'test_content_backtrace_for_cssize_inline.sass'},
3256
+ {:mixin => 'foo', :line => 2, :filename => 'test_content_backtrace_for_cssize_inline.sass'},
3257
+ {:line => 5, :filename => 'test_content_backtrace_for_cssize_inline.sass'},
3258
+ ], e.sass_backtrace)
3259
+ end
3260
+
3261
+ def test_mixin_with_args_and_varargs_passed_no_var_args
3262
+ assert_equal <<CSS, render(<<SASS, :syntax => :scss)
3263
+ .foo {
3264
+ a: 1;
3265
+ b: 2;
3266
+ c: 3; }
3267
+ CSS
3268
+ @mixin three-or-more-args($a, $b, $c, $rest...) {
3269
+ a: $a;
3270
+ b: $b;
3271
+ c: $c;
3272
+ }
3273
+
3274
+ .foo {
3275
+ @include three-or-more-args($a: 1, $b: 2, $c: 3);
3276
+ }
3277
+ SASS
3278
+
3279
+ end
3280
+
3281
+ def test_debug_inspects_sass_objects
3282
+ assert_warning(<<END) {render("@debug (a: 1, b: 2)")}
3283
+ test_debug_inspects_sass_objects_inline.sass:1 DEBUG: (a: 1, b: 2)
3284
+ END
3285
+ assert_warning(<<END) {render("$map: (a: 1, b: 2); @debug $map", :syntax => :scss)}
3286
+ test_debug_inspects_sass_objects_inline.scss:1 DEBUG: (a: 1, b: 2)
3287
+ END
3288
+ end
3289
+
3290
+ def test_default_arg_before_splat
3291
+ assert_equal <<CSS, render(<<SASS, :syntax => :scss)
3292
+ .foo-positional {
3293
+ a: 1;
3294
+ b: 2;
3295
+ positional-arguments: 3, 4;
3296
+ keyword-arguments: (); }
3297
+
3298
+ .foo-keywords {
3299
+ a: true;
3300
+ positional-arguments: ();
3301
+ keyword-arguments: (c: c, d: d); }
3302
+ CSS
3303
+ @mixin foo($a: true, $b: null, $arguments...) {
3304
+ a: $a;
3305
+ b: $b;
3306
+ positional-arguments: inspect($arguments);
3307
+ keyword-arguments: inspect(keywords($arguments));
3308
+ }
3309
+ .foo-positional {
3310
+ @include foo(1, 2, 3, 4);
3311
+ }
3312
+ .foo-keywords {
3313
+ @include foo($c: c, $d: d);
3314
+ }
3315
+ SASS
3316
+ end
2423
3317
 
2424
3318
  private
2425
3319
 
@@ -2435,6 +3329,7 @@ SASS
2435
3329
 
2436
3330
  def render(sass, options = {})
2437
3331
  munge_filename options
3332
+ options[:importer] ||= MockImporter.new
2438
3333
  Sass::Engine.new(sass, options).render
2439
3334
  end
2440
3335