sass 3.2.7 → 3.3.0.rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. data/MIT-LICENSE +2 -2
  2. data/README.md +14 -2
  3. data/Rakefile +25 -1
  4. data/VERSION +1 -1
  5. data/VERSION_DATE +1 -1
  6. data/VERSION_NAME +1 -1
  7. data/lib/sass/cache_stores/base.rb +4 -2
  8. data/lib/sass/cache_stores/chain.rb +2 -1
  9. data/lib/sass/cache_stores/filesystem.rb +2 -6
  10. data/lib/sass/cache_stores/memory.rb +1 -1
  11. data/lib/sass/cache_stores/null.rb +2 -2
  12. data/lib/sass/callbacks.rb +1 -0
  13. data/lib/sass/css.rb +10 -10
  14. data/lib/sass/engine.rb +403 -150
  15. data/lib/sass/environment.rb +136 -57
  16. data/lib/sass/error.rb +7 -7
  17. data/lib/sass/exec.rb +123 -39
  18. data/lib/sass/features.rb +41 -0
  19. data/lib/sass/importers/base.rb +33 -2
  20. data/lib/sass/importers/deprecated_path.rb +45 -0
  21. data/lib/sass/importers/filesystem.rb +25 -14
  22. data/lib/sass/importers.rb +1 -0
  23. data/lib/sass/logger/base.rb +3 -3
  24. data/lib/sass/logger/log_level.rb +4 -6
  25. data/lib/sass/media.rb +19 -19
  26. data/lib/sass/plugin/compiler.rb +141 -101
  27. data/lib/sass/plugin/configuration.rb +18 -22
  28. data/lib/sass/plugin/merb.rb +1 -1
  29. data/lib/sass/plugin/staleness_checker.rb +24 -8
  30. data/lib/sass/plugin.rb +4 -2
  31. data/lib/sass/repl.rb +3 -3
  32. data/lib/sass/script/css_lexer.rb +9 -4
  33. data/lib/sass/script/css_parser.rb +6 -2
  34. data/lib/sass/script/functions.rb +1343 -590
  35. data/lib/sass/script/lexer.rb +84 -52
  36. data/lib/sass/script/parser.rb +217 -97
  37. data/lib/sass/script/tree/funcall.rb +290 -0
  38. data/lib/sass/script/{interpolation.rb → tree/interpolation.rb} +34 -13
  39. data/lib/sass/script/tree/list_literal.rb +80 -0
  40. data/lib/sass/script/tree/literal.rb +47 -0
  41. data/lib/sass/script/tree/map_literal.rb +64 -0
  42. data/lib/sass/script/{node.rb → tree/node.rb} +22 -12
  43. data/lib/sass/script/{operation.rb → tree/operation.rb} +17 -25
  44. data/lib/sass/script/tree/selector.rb +30 -0
  45. data/lib/sass/script/{string_interpolation.rb → tree/string_interpolation.rb} +5 -4
  46. data/lib/sass/script/{unary_operation.rb → tree/unary_operation.rb} +14 -9
  47. data/lib/sass/script/tree/variable.rb +57 -0
  48. data/lib/sass/script/tree.rb +16 -0
  49. data/lib/sass/script/{arg_list.rb → value/arg_list.rb} +4 -24
  50. data/lib/sass/script/value/base.rb +248 -0
  51. data/lib/sass/script/value/bool.rb +36 -0
  52. data/lib/sass/script/{color.rb → value/color.rb} +239 -195
  53. data/lib/sass/script/value/helpers.rb +155 -0
  54. data/lib/sass/script/value/list.rb +119 -0
  55. data/lib/sass/script/value/map.rb +70 -0
  56. data/lib/sass/script/value/null.rb +45 -0
  57. data/lib/sass/script/{number.rb → value/number.rb} +91 -65
  58. data/lib/sass/script/{string.rb → value/string.rb} +9 -11
  59. data/lib/sass/script/value.rb +11 -0
  60. data/lib/sass/script.rb +35 -8
  61. data/lib/sass/scss/css_parser.rb +2 -1
  62. data/lib/sass/scss/parser.rb +338 -170
  63. data/lib/sass/scss/rx.rb +5 -6
  64. data/lib/sass/scss/script_lexer.rb +1 -0
  65. data/lib/sass/scss/script_parser.rb +1 -0
  66. data/lib/sass/scss/static_parser.rb +23 -6
  67. data/lib/sass/selector/abstract_sequence.rb +2 -2
  68. data/lib/sass/selector/comma_sequence.rb +21 -16
  69. data/lib/sass/selector/sequence.rb +60 -34
  70. data/lib/sass/selector/simple.rb +11 -12
  71. data/lib/sass/selector/simple_sequence.rb +55 -33
  72. data/lib/sass/selector.rb +52 -48
  73. data/lib/sass/source/map.rb +211 -0
  74. data/lib/sass/source/position.rb +39 -0
  75. data/lib/sass/source/range.rb +41 -0
  76. data/lib/sass/stack.rb +120 -0
  77. data/lib/sass/supports.rb +12 -13
  78. data/lib/sass/tree/at_root_node.rb +82 -0
  79. data/lib/sass/tree/comment_node.rb +3 -3
  80. data/lib/sass/tree/css_import_node.rb +11 -11
  81. data/lib/sass/tree/debug_node.rb +2 -2
  82. data/lib/sass/tree/directive_node.rb +13 -2
  83. data/lib/sass/tree/each_node.rb +8 -8
  84. data/lib/sass/tree/extend_node.rb +13 -6
  85. data/lib/sass/tree/for_node.rb +4 -4
  86. data/lib/sass/tree/function_node.rb +5 -4
  87. data/lib/sass/tree/if_node.rb +1 -1
  88. data/lib/sass/tree/import_node.rb +4 -5
  89. data/lib/sass/tree/media_node.rb +4 -14
  90. data/lib/sass/tree/mixin_def_node.rb +4 -4
  91. data/lib/sass/tree/mixin_node.rb +21 -8
  92. data/lib/sass/tree/node.rb +29 -12
  93. data/lib/sass/tree/prop_node.rb +38 -18
  94. data/lib/sass/tree/return_node.rb +3 -2
  95. data/lib/sass/tree/root_node.rb +19 -3
  96. data/lib/sass/tree/rule_node.rb +25 -17
  97. data/lib/sass/tree/supports_node.rb +0 -13
  98. data/lib/sass/tree/trace_node.rb +2 -1
  99. data/lib/sass/tree/variable_node.rb +9 -3
  100. data/lib/sass/tree/visitors/base.rb +6 -6
  101. data/lib/sass/tree/visitors/check_nesting.rb +12 -9
  102. data/lib/sass/tree/visitors/convert.rb +63 -38
  103. data/lib/sass/tree/visitors/cssize.rb +63 -23
  104. data/lib/sass/tree/visitors/deep_copy.rb +6 -5
  105. data/lib/sass/tree/visitors/extend.rb +7 -7
  106. data/lib/sass/tree/visitors/perform.rb +256 -151
  107. data/lib/sass/tree/visitors/set_options.rb +6 -6
  108. data/lib/sass/tree/visitors/to_css.rb +231 -81
  109. data/lib/sass/tree/warn_node.rb +2 -2
  110. data/lib/sass/tree/while_node.rb +2 -2
  111. data/lib/sass/util/multibyte_string_scanner.rb +2 -0
  112. data/lib/sass/util/normalized_map.rb +65 -0
  113. data/lib/sass/util/ordered_hash.rb +188 -0
  114. data/lib/sass/util/subset_map.rb +3 -2
  115. data/lib/sass/util/test.rb +9 -0
  116. data/lib/sass/util.rb +220 -34
  117. data/lib/sass/version.rb +9 -9
  118. data/lib/sass.rb +14 -7
  119. data/test/sass/compiler_test.rb +213 -0
  120. data/test/sass/conversion_test.rb +235 -9
  121. data/test/sass/engine_test.rb +230 -60
  122. data/test/sass/exec_test.rb +86 -0
  123. data/test/sass/extend_test.rb +215 -147
  124. data/test/sass/functions_test.rb +584 -99
  125. data/test/sass/importer_test.rb +165 -17
  126. data/test/sass/plugin_test.rb +19 -13
  127. data/test/sass/script_conversion_test.rb +40 -0
  128. data/test/sass/script_test.rb +231 -21
  129. data/test/sass/scss/css_test.rb +14 -5
  130. data/test/sass/scss/scss_test.rb +1266 -66
  131. data/test/sass/source_map_test.rb +879 -0
  132. data/test/sass/templates/bork5.sass +3 -0
  133. data/test/sass/util/normalized_map_test.rb +30 -0
  134. data/test/sass/util_test.rb +90 -0
  135. data/test/sass/value_helpers_test.rb +181 -0
  136. data/test/test_helper.rb +7 -2
  137. metadata +316 -291
  138. data/lib/sass/script/bool.rb +0 -18
  139. data/lib/sass/script/funcall.rb +0 -231
  140. data/lib/sass/script/list.rb +0 -84
  141. data/lib/sass/script/literal.rb +0 -239
  142. data/lib/sass/script/null.rb +0 -34
  143. data/lib/sass/script/variable.rb +0 -58
  144. data/test/Gemfile +0 -3
  145. data/vendor/listen/CHANGELOG.md +0 -221
  146. data/vendor/listen/CONTRIBUTING.md +0 -38
  147. data/vendor/listen/Gemfile +0 -30
  148. data/vendor/listen/Guardfile +0 -8
  149. data/vendor/listen/LICENSE +0 -20
  150. data/vendor/listen/README.md +0 -315
  151. data/vendor/listen/Rakefile +0 -47
  152. data/vendor/listen/Vagrantfile +0 -96
  153. data/vendor/listen/lib/listen/adapter.rb +0 -214
  154. data/vendor/listen/lib/listen/adapters/bsd.rb +0 -112
  155. data/vendor/listen/lib/listen/adapters/darwin.rb +0 -85
  156. data/vendor/listen/lib/listen/adapters/linux.rb +0 -113
  157. data/vendor/listen/lib/listen/adapters/polling.rb +0 -67
  158. data/vendor/listen/lib/listen/adapters/windows.rb +0 -87
  159. data/vendor/listen/lib/listen/dependency_manager.rb +0 -126
  160. data/vendor/listen/lib/listen/directory_record.rb +0 -371
  161. data/vendor/listen/lib/listen/listener.rb +0 -225
  162. data/vendor/listen/lib/listen/multi_listener.rb +0 -143
  163. data/vendor/listen/lib/listen/turnstile.rb +0 -28
  164. data/vendor/listen/lib/listen/version.rb +0 -3
  165. data/vendor/listen/lib/listen.rb +0 -40
  166. data/vendor/listen/listen.gemspec +0 -22
  167. data/vendor/listen/spec/listen/adapter_spec.rb +0 -183
  168. data/vendor/listen/spec/listen/adapters/bsd_spec.rb +0 -36
  169. data/vendor/listen/spec/listen/adapters/darwin_spec.rb +0 -37
  170. data/vendor/listen/spec/listen/adapters/linux_spec.rb +0 -47
  171. data/vendor/listen/spec/listen/adapters/polling_spec.rb +0 -68
  172. data/vendor/listen/spec/listen/adapters/windows_spec.rb +0 -30
  173. data/vendor/listen/spec/listen/dependency_manager_spec.rb +0 -107
  174. data/vendor/listen/spec/listen/directory_record_spec.rb +0 -1225
  175. data/vendor/listen/spec/listen/listener_spec.rb +0 -169
  176. data/vendor/listen/spec/listen/multi_listener_spec.rb +0 -174
  177. data/vendor/listen/spec/listen/turnstile_spec.rb +0 -56
  178. data/vendor/listen/spec/listen_spec.rb +0 -73
  179. data/vendor/listen/spec/spec_helper.rb +0 -21
  180. data/vendor/listen/spec/support/adapter_helper.rb +0 -629
  181. data/vendor/listen/spec/support/directory_record_helper.rb +0 -55
  182. data/vendor/listen/spec/support/fixtures_helper.rb +0 -29
  183. data/vendor/listen/spec/support/listeners_helper.rb +0 -156
  184. data/vendor/listen/spec/support/platform_helper.rb +0 -15
@@ -8,29 +8,38 @@ class ImporterTest < Test::Unit::TestCase
8
8
 
9
9
  class FruitImporter < Sass::Importers::Base
10
10
  def find(name, context = nil)
11
- if name =~ %r{fruits/(\w+)(\.s[ac]ss)?}
12
- fruit = $1
13
- color = case $1
14
- when "apple"
15
- "red"
16
- when "orange"
17
- "orange"
18
- else
19
- "blue"
20
- end
21
- contents = %Q{
22
- $#{fruit}-color: #{color} !default;
23
- @mixin #{fruit} {
24
- color: $#{fruit}-color;
25
- }
26
- }
27
- Sass::Engine.new(contents, :filename => name, :syntax => :scss, :importer => self)
11
+ fruit = parse(name)
12
+ return unless fruit
13
+ color = case fruit
14
+ when "apple"
15
+ "red"
16
+ when "orange"
17
+ "orange"
18
+ else
19
+ "blue"
28
20
  end
21
+ contents = %Q{
22
+ $#{fruit}-color: #{color} !default;
23
+ @mixin #{fruit} {
24
+ color: $#{fruit}-color;
25
+ }
26
+ }
27
+ Sass::Engine.new(contents, :filename => name, :syntax => :scss, :importer => self)
29
28
  end
30
29
 
31
30
  def key(name, context)
32
31
  [self.class.name, name]
33
32
  end
33
+
34
+ def public_url(name)
35
+ "http://#{parse(name)}.example.com/style.scss"
36
+ end
37
+
38
+ private
39
+
40
+ def parse(name)
41
+ name[%r{fruits/(\w+)(\.s[ac]ss)?}, 1]
42
+ end
34
43
  end
35
44
 
36
45
  # This class proves that you can override the extension scheme for importers
@@ -177,6 +186,145 @@ CSS
177
186
  )
178
187
  end
179
188
 
189
+ def test_source_map_with_only_css_uri_supports_public_url_imports
190
+ fruit_importer = FruitImporter.new
191
+
192
+ options = {
193
+ :filename => 'fruits/orange',
194
+ :importer => fruit_importer,
195
+ :syntax => :scss
196
+ }
197
+
198
+ engine = Sass::Engine.new(<<SCSS, options)
199
+ .orchard {
200
+ color: blue;
201
+ }
202
+ SCSS
203
+
204
+ _, sourcemap = engine.render_with_sourcemap('sourcemap_uri')
205
+ assert_equal <<JSON.strip, sourcemap.to_json(:css_uri => 'css_uri')
206
+ {
207
+ "version": "3",
208
+ "mappings": "AAAA,QAAS;EACP,KAAK,EAAE,IAAI",
209
+ "sources": ["http://orange.example.com/style.scss"],
210
+ "file": "css_uri"
211
+ }
212
+ JSON
213
+ end
214
+
215
+ def test_source_map_with_only_css_uri_doesnt_support_filesystem_importer
216
+ file_system_importer = Sass::Importers::Filesystem.new('.')
217
+ options = {
218
+ :filename => filename_for_test(:scss),
219
+ :importer => file_system_importer,
220
+ :syntax => :scss
221
+ }
222
+
223
+ engine = Sass::Engine.new(<<SCSS, options)
224
+ .foo {a: b}
225
+ SCSS
226
+
227
+ _, sourcemap = engine.render_with_sourcemap('http://1.example.com/style.map')
228
+
229
+ assert_warning(<<WARNING) {sourcemap.to_json(:css_uri => 'css_uri')}
230
+ WARNING: Couldn't determine public URL for "#{filename_for_test(:scss)}" while generating sourcemap.
231
+ Without a public URL, there's nothing for the source map to link to.
232
+ Custom importers should define the #public_url method.
233
+ WARNING
234
+ end
235
+
236
+ def test_source_map_with_css_uri_and_css_path_doesnt_support_filesystem_importer
237
+ file_system_importer = Sass::Importers::Filesystem.new('.')
238
+ options = {
239
+ :filename => filename_for_test(:scss),
240
+ :importer => file_system_importer,
241
+ :syntax => :scss
242
+ }
243
+
244
+ engine = Sass::Engine.new(<<SCSS, options)
245
+ .foo {a: b}
246
+ SCSS
247
+
248
+ _, sourcemap = engine.render_with_sourcemap('http://1.example.com/style.map')
249
+
250
+ assert_warning(<<WARNING) {sourcemap.to_json(:css_uri => 'css_uri', :css_path => 'css_path')}
251
+ WARNING: Couldn't determine public URL for "#{filename_for_test(:scss)}" while generating sourcemap.
252
+ Without a public URL, there's nothing for the source map to link to.
253
+ Custom importers should define the #public_url method.
254
+ WARNING
255
+ end
256
+
257
+ def test_source_map_with_css_uri_and_sourcemap_path_supports_filesystem_importer
258
+ file_system_importer = Sass::Importers::Filesystem.new('.')
259
+ options = {
260
+ :filename => 'sass/style.scss',
261
+ :importer => file_system_importer,
262
+ :syntax => :scss
263
+ }
264
+
265
+ engine = Sass::Engine.new(<<SCSS, options)
266
+ .foo {a: b}
267
+ SCSS
268
+
269
+ rendered, sourcemap = engine.render_with_sourcemap('http://1.example.com/style.map')
270
+
271
+
272
+ rendered, sourcemap = engine.render_with_sourcemap('http://map.example.com/map/style.map')
273
+ css_uri = 'css_uri'
274
+ sourcemap_path = 'map/style.map'
275
+ assert_equal <<JSON.strip, sourcemap.to_json(:css_uri => css_uri, :sourcemap_path => sourcemap_path)
276
+ {
277
+ "version": "3",
278
+ "mappings": "AAAA,IAAK;EAAC,CAAC,EAAE,CAAC",
279
+ "sources": ["../sass/style.scss"],
280
+ "file": "css_uri"
281
+ }
282
+ JSON
283
+ end
284
+
285
+ def test_source_map_with_css_path_and_sourcemap_path_supports_file_system_importer
286
+ file_system_importer = Sass::Importers::Filesystem.new('.')
287
+ options = {
288
+ :filename => 'sass/style.scss',
289
+ :importer => file_system_importer,
290
+ :syntax => :scss
291
+ }
292
+
293
+ engine = Sass::Engine.new(<<SCSS, options)
294
+ .foo {a: b}
295
+ SCSS
296
+
297
+ _, sourcemap = engine.render_with_sourcemap('http://map.example.com/map/style.map')
298
+ css_path = 'static/style.css'
299
+ sourcemap_path = 'map/style.map'
300
+ assert_equal <<JSON.strip, sourcemap.to_json(:css_path => css_path, :sourcemap_path => sourcemap_path)
301
+ {
302
+ "version": "3",
303
+ "mappings": "AAAA,IAAK;EAAC,CAAC,EAAE,CAAC",
304
+ "sources": ["../sass/style.scss"],
305
+ "file": "../static/style.css"
306
+ }
307
+ JSON
308
+ end
309
+
310
+ def test_render_with_sourcemap_requires_filename
311
+ file_system_importer = Sass::Importers::Filesystem.new('.')
312
+ engine = Sass::Engine.new(".foo {a: b}", :syntax => :scss, :importer => file_system_importer)
313
+ assert_raise_message(Sass::SyntaxError, <<MESSAGE) {engine.render_with_sourcemap('sourcemap_url')}
314
+ Error generating source map: couldn't determine public URL for the source stylesheet.
315
+ No filename is available so there's nothing for the source map to link to.
316
+ MESSAGE
317
+ end
318
+
319
+ def test_render_with_sourcemap_requires_importer_with_public_url
320
+ class_importer = ClassImporter.new({"pear" => "color: green;"}, {"pear" => Time.now})
321
+ assert_raise_message(Sass::SyntaxError, <<MESSAGE) {class_importer.find("pear", {}).render_with_sourcemap('sourcemap_url')}
322
+ Error generating source map: couldn't determine public URL for "pear".
323
+ Without a public URL, there's nothing for the source map to link to.
324
+ Custom importers should define the #public_url method.
325
+ MESSAGE
326
+ end
327
+
180
328
  def fixture_dir
181
329
  File.join(File.dirname(__FILE__), "fixtures")
182
330
  end
@@ -7,13 +7,13 @@ require 'fileutils'
7
7
  module Sass::Script::Functions
8
8
  def filename
9
9
  filename = options[:filename].gsub(%r{.*((/[^/]+){4})}, '\1')
10
- Sass::Script::String.new(filename)
10
+ Sass::Script::Value::String.new(filename)
11
11
  end
12
12
 
13
13
  def whatever
14
14
  custom = options[:custom]
15
15
  whatever = custom && custom[:whatever]
16
- Sass::Script::String.new(whatever || "incorrect")
16
+ Sass::Script::Value::String.new(whatever || "incorrect")
17
17
  end
18
18
  end
19
19
 
@@ -123,6 +123,23 @@ CSS
123
123
  File.delete(tempfile_loc('bork1'))
124
124
  end
125
125
 
126
+ def test_full_exception_with_block_comment
127
+ File.delete(tempfile_loc('bork5'))
128
+ check_for_updates!
129
+ File.open(tempfile_loc('bork5')) do |file|
130
+ assert_equal(<<CSS.strip, file.read.split("\n")[0...7].join("\n"))
131
+ /*
132
+ Syntax error: Undefined variable: "$bork".
133
+ on line 3 of #{template_loc('bork5')}
134
+
135
+ 1: bork
136
+ 2: /* foo *\\/
137
+ 3: :bork $bork
138
+ CSS
139
+ end
140
+ File.delete(tempfile_loc('bork1'))
141
+ end
142
+
126
143
  def test_single_level_import_loop
127
144
  File.delete(tempfile_loc('single_import_loop'))
128
145
  check_for_updates!
@@ -230,17 +247,6 @@ WARNING
230
247
 
231
248
  # Callbacks
232
249
 
233
- def test_updating_stylesheets_callback
234
- # Should run even when there's nothing to update
235
- Sass::Plugin.options[:template_location] = nil
236
- assert_callback :updating_stylesheets, []
237
- end
238
-
239
- def test_updating_stylesheets_callback_with_never_update
240
- Sass::Plugin.options[:never_update] = true
241
- assert_no_callback :updating_stylesheets
242
- end
243
-
244
250
  def test_updated_stylesheet_callback_for_updated_template
245
251
  Sass::Plugin.options[:always_update] = false
246
252
  touch 'basic'
@@ -69,6 +69,46 @@ class SassScriptConversionTest < Test::Unit::TestCase
69
69
  assert_renders "$flaznicate"
70
70
  end
71
71
 
72
+ def test_null
73
+ assert_renders "null"
74
+ end
75
+
76
+ def test_empty_list
77
+ assert_renders "()"
78
+ end
79
+
80
+ def test_list_in_args
81
+ assert_renders "foo((a, b, c))"
82
+ assert_renders "foo($arg: (a, b, c))"
83
+ assert_renders "foo(a, b, (a, b, c)...)"
84
+ end
85
+
86
+ def test_singleton_list
87
+ assert_renders "(1,)"
88
+ assert_renders "(1 2 3,)"
89
+ assert_renders "((1, 2, 3),)"
90
+ end
91
+
92
+ def test_map
93
+ assert_renders "(foo: bar)"
94
+ assert_renders "(foo: bar, baz: bip)"
95
+ assert_renders "(foo: bar, baz: (bip: bap))"
96
+ end
97
+
98
+ def test_map_in_list
99
+ assert_renders "(foo: bar) baz"
100
+ assert_renders "(foo: bar), (baz: bip)"
101
+ end
102
+
103
+ def test_list_in_map
104
+ assert_renders "(foo: bar baz)"
105
+ assert_renders "(foo: (bar, baz), bip: bop)"
106
+ end
107
+
108
+ def test_selector
109
+ assert_renders "&"
110
+ end
111
+
72
112
  def self.test_precedence(outer, inner)
73
113
  op_outer = Sass::Script::Lexer::OPERATORS_REVERSE[outer]
74
114
  op_inner = Sass::Script::Lexer::OPERATORS_REVERSE[inner]
@@ -5,7 +5,7 @@ require 'sass/engine'
5
5
  module Sass::Script::Functions::UserFunctions
6
6
  def assert_options(val)
7
7
  val.options[:foo]
8
- Sass::Script::String.new("Options defined!")
8
+ Sass::Script::Value::String.new("Options defined!")
9
9
  end
10
10
 
11
11
  def arg_error
@@ -21,13 +21,13 @@ class SassScriptTest < Test::Unit::TestCase
21
21
  include Sass::Script
22
22
 
23
23
  def test_color_checks_input
24
- assert_raise_message(ArgumentError, "Blue value -1 must be between 0 and 255") {Color.new([1, 2, -1])}
25
- assert_raise_message(ArgumentError, "Red value 256 must be between 0 and 255") {Color.new([256, 2, 3])}
24
+ assert_raise_message(ArgumentError, "Blue value -1 must be between 0 and 255") {Sass::Script::Value::Color.new([1, 2, -1])}
25
+ assert_raise_message(ArgumentError, "Red value 256 must be between 0 and 255") {Sass::Script::Value::Color.new([256, 2, 3])}
26
26
  end
27
27
 
28
28
  def test_color_checks_rgba_input
29
- assert_raise_message(ArgumentError, "Alpha channel 1.1 must be between 0 and 1") {Color.new([1, 2, 3, 1.1])}
30
- assert_raise_message(ArgumentError, "Alpha channel -0.1 must be between 0 and 1") {Color.new([1, 2, 3, -0.1])}
29
+ assert_raise_message(ArgumentError, "Alpha channel 1.1 must be between 0 and 1") {Sass::Script::Value::Color.new([1, 2, 3, 1.1])}
30
+ assert_raise_message(ArgumentError, "Alpha channel -0.1 must be between 0 and 1") {Sass::Script::Value::Color.new([1, 2, 3, -0.1])}
31
31
  end
32
32
 
33
33
  def test_string_escapes
@@ -46,17 +46,19 @@ class SassScriptTest < Test::Unit::TestCase
46
46
  assert_equal "white", resolve("white")
47
47
  assert_equal "white", resolve("#ffffff")
48
48
  assert_equal "#fffffe", resolve("white - #000001")
49
+ assert_equal "transparent", resolve("transparent")
50
+ assert_equal "transparent", resolve("rgba(0, 0, 0, 0)")
49
51
  end
50
52
 
51
53
  def test_rgba_color_literals
52
- assert_equal Sass::Script::Color.new([1, 2, 3, 0.75]), eval("rgba(1, 2, 3, 0.75)")
54
+ assert_equal Sass::Script::Value::Color.new([1, 2, 3, 0.75]), eval("rgba(1, 2, 3, 0.75)")
53
55
  assert_equal "rgba(1, 2, 3, 0.75)", resolve("rgba(1, 2, 3, 0.75)")
54
56
 
55
- assert_equal Sass::Script::Color.new([1, 2, 3, 0]), eval("rgba(1, 2, 3, 0)")
57
+ assert_equal Sass::Script::Value::Color.new([1, 2, 3, 0]), eval("rgba(1, 2, 3, 0)")
56
58
  assert_equal "rgba(1, 2, 3, 0)", resolve("rgba(1, 2, 3, 0)")
57
59
 
58
- assert_equal Sass::Script::Color.new([1, 2, 3]), eval("rgba(1, 2, 3, 1)")
59
- assert_equal Sass::Script::Color.new([1, 2, 3, 1]), eval("rgba(1, 2, 3, 1)")
60
+ assert_equal Sass::Script::Value::Color.new([1, 2, 3]), eval("rgba(1, 2, 3, 1)")
61
+ assert_equal Sass::Script::Value::Color.new([1, 2, 3, 1]), eval("rgba(1, 2, 3, 1)")
60
62
  assert_equal "#010203", resolve("rgba(1, 2, 3, 1)")
61
63
  assert_equal "white", resolve("rgba(255, 255, 255, 1)")
62
64
  end
@@ -95,6 +97,7 @@ class SassScriptTest < Test::Unit::TestCase
95
97
  assert_equal "navy", resolve("#000080", :style => :compressed)
96
98
  assert_equal "navy #fff", resolve("#000080 white", :style => :compressed)
97
99
  assert_equal "This color is #fff", resolve('"This color is #{ white }"', :style => :compressed)
100
+ assert_equal "transparent", resolve("rgba(0, 0, 0, 0)", :style => :compressed)
98
101
  end
99
102
 
100
103
  def test_compressed_comma
@@ -104,8 +107,8 @@ class SassScriptTest < Test::Unit::TestCase
104
107
  end
105
108
 
106
109
  def test_implicit_strings
107
- assert_equal Sass::Script::String.new("foo"), eval("foo")
108
- assert_equal Sass::Script::String.new("foo/bar"), eval("foo/bar")
110
+ assert_equal Sass::Script::Value::String.new("foo"), eval("foo")
111
+ assert_equal Sass::Script::Value::String.new("foo/bar"), eval("foo/bar")
109
112
  end
110
113
 
111
114
  def test_basic_interpolation
@@ -134,6 +137,7 @@ class SassScriptTest < Test::Unit::TestCase
134
137
  assert_equal '3,7', resolve('#{1 + 2},#{3 + 4}')
135
138
  assert_equal '3, 7, 11', resolve('#{1 + 2}, #{3 + 4}, #{5 + 6}')
136
139
  assert_equal '3, 7, 11', resolve('3, #{3 + 4}, 11')
140
+ assert_equal '3, 7, 11', resolve('3, 7, #{5 + 6}')
137
141
 
138
142
  assert_equal '3 / 7', resolve('3 / #{3 + 4}')
139
143
  assert_equal '3 /7', resolve('3 /#{3 + 4}')
@@ -228,9 +232,9 @@ SASS
228
232
  end
229
233
 
230
234
  def test_dynamic_url
231
- assert_equal "url(foo-bar)", resolve("url($foo)", {}, env('foo' => Sass::Script::String.new("foo-bar")))
232
- assert_equal "url(foo-bar baz)", resolve("url($foo $bar)", {}, env('foo' => Sass::Script::String.new("foo-bar"), 'bar' => Sass::Script::String.new("baz")))
233
- assert_equal "url(foo baz)", resolve("url(foo $bar)", {}, env('bar' => Sass::Script::String.new("baz")))
235
+ assert_equal "url(foo-bar)", resolve("url($foo)", {}, env('foo' => Sass::Script::Value::String.new("foo-bar")))
236
+ assert_equal "url(foo-bar baz)", resolve("url($foo $bar)", {}, env('foo' => Sass::Script::Value::String.new("foo-bar"), 'bar' => Sass::Script::Value::String.new("baz")))
237
+ assert_equal "url(foo baz)", resolve("url(foo $bar)", {}, env('bar' => Sass::Script::Value::String.new("baz")))
234
238
  assert_equal "url(foo bar)", resolve("url(foo bar)")
235
239
  end
236
240
 
@@ -242,7 +246,7 @@ SASS
242
246
  end
243
247
 
244
248
  def test_hyphenated_variables
245
- assert_equal("a-b", resolve("$a-b", {}, env("a-b" => Sass::Script::String.new("a-b"))))
249
+ assert_equal("a-b", resolve("$a-b", {}, env("a-b" => Sass::Script::Value::String.new("a-b"))))
246
250
  end
247
251
 
248
252
  def test_ruby_equality
@@ -372,7 +376,7 @@ SASS
372
376
 
373
377
  def test_equals
374
378
  assert_equal("true", resolve('"foo" == $foo', {},
375
- env("foo" => Sass::Script::String.new("foo"))))
379
+ env("foo" => Sass::Script::Value::String.new("foo"))))
376
380
  assert_equal "true", resolve("1 == 1.0")
377
381
  assert_equal "true", resolve("false != true")
378
382
  assert_equal "false", resolve("1em == 1px")
@@ -488,6 +492,59 @@ SASS
488
492
  assert_equal "1 2 3", resolve("null 1 2 3")
489
493
  end
490
494
 
495
+ def test_map_can_have_trailing_comma
496
+ assert_equal("(foo: 1, bar: 2)", eval("(foo: 1, bar: 2,)").to_sass)
497
+ end
498
+
499
+ def test_list_can_have_trailing_comma
500
+ assert_equal("1, 2, 3", resolve("1, 2, 3,"))
501
+ end
502
+
503
+ def test_trailing_comma_defines_singleton_list
504
+ assert_equal("1 2 3", resolve("nth((1 2 3,), 1)"))
505
+ end
506
+
507
+ def test_map_cannot_have_duplicate_keys
508
+ assert_raise_message(Sass::SyntaxError, 'Duplicate key "foo" in map (foo: bar, foo: baz).') do
509
+ eval("(foo: bar, foo: baz)")
510
+ end
511
+ assert_raise_message(Sass::SyntaxError, 'Duplicate key "foo" in map (foo: bar, fo + o: baz).') do
512
+ eval("(foo: bar, fo + o: baz)")
513
+ end
514
+ assert_raise_message(Sass::SyntaxError, 'Duplicate key "foo" in map (foo: bar, "foo": baz).') do
515
+ eval("(foo: bar, 'foo': baz)")
516
+ end
517
+ assert_raise_message(Sass::SyntaxError, 'Duplicate key 2px in map (2px: bar, 1px + 1px: baz).') do
518
+ eval("(2px: bar, 1px + 1px: baz)")
519
+ end
520
+ assert_raise_message(Sass::SyntaxError, 'Duplicate key #0000ff in map (blue: bar, blue: baz).') do
521
+ eval("(blue: bar, #00f: baz)")
522
+ end
523
+ end
524
+
525
+ def test_non_duplicate_map_keys
526
+ # These shouldn't throw errors
527
+ eval("(foo: foo, bar: bar)")
528
+ eval("(2px: foo, 2: bar)")
529
+ eval("(2px: foo, 2em: bar)")
530
+ eval("('2px': foo, 2px: bar)")
531
+ end
532
+
533
+ def test_map_syntax_errors
534
+ assert_raise_message(Sass::SyntaxError, 'Invalid CSS after "(foo:": expected expression (e.g. 1px, bold), was ")"') do
535
+ eval("(foo:)")
536
+ end
537
+ assert_raise_message(Sass::SyntaxError, 'Invalid CSS after "(": expected ")", was ":bar)"') do
538
+ eval("(:bar)")
539
+ end
540
+ assert_raise_message(Sass::SyntaxError, 'Invalid CSS after "(foo, bar": expected ")", was ": baz)"') do
541
+ eval("(foo, bar: baz)")
542
+ end
543
+ assert_raise_message(Sass::SyntaxError, 'Invalid CSS after "(foo: bar, baz": expected ":", was ")"') do
544
+ eval("(foo: bar, baz)")
545
+ end
546
+ end
547
+
491
548
  def test_deep_argument_error_not_unwrapped
492
549
  # JRuby (as of 1.6.7.2) offers no way of distinguishing between
493
550
  # argument errors caused by programming errors in a function and
@@ -503,8 +560,128 @@ SASS
503
560
  end
504
561
 
505
562
  def test_boolean_ops_short_circuit
506
- assert_equal "false", resolve("$ie and $ie <= 7", {}, env('ie' => Sass::Script::Bool.new(false)))
507
- assert_equal "true", resolve("$ie or $undef", {}, env('ie' => Sass::Script::Bool.new(true)))
563
+ assert_equal "false", resolve("$ie and $ie <= 7", {}, env('ie' => Sass::Script::Value::Bool.new(false)))
564
+ assert_equal "true", resolve("$ie or $undef", {}, env('ie' => Sass::Script::Value::Bool.new(true)))
565
+ end
566
+
567
+ def test_selector
568
+ env = Sass::Environment.new
569
+ assert_equal "true", resolve("& == null", {}, env)
570
+
571
+ env.selector = selector('.foo.bar .baz.bang, .bip.bop')
572
+ assert_equal ".foo.bar .baz.bang, .bip.bop", resolve("&", {}, env)
573
+ assert_equal ".foo.bar .baz.bang", resolve("nth(&, 1)", {}, env)
574
+ assert_equal ".bip.bop", resolve("nth(&, 2)", {}, env)
575
+ assert_equal ".foo.bar", resolve("nth(nth(&, 1), 1)", {}, env)
576
+ assert_equal ".baz.bang", resolve("nth(nth(&, 1), 2)", {}, env)
577
+ assert_equal ".bip.bop", resolve("nth(nth(&, 2), 1)", {}, env)
578
+ assert_equal "string", resolve("type-of(nth(nth(&, 1), 1))", {}, env)
579
+
580
+ env.selector = selector('.foo > .bar')
581
+ assert_equal ".foo > .bar", resolve("&", {}, env)
582
+ assert_equal ".foo > .bar", resolve("nth(&, 1)", {}, env)
583
+ assert_equal ".foo", resolve("nth(nth(&, 1), 1)", {}, env)
584
+ assert_equal ">", resolve("nth(nth(&, 1), 2)", {}, env)
585
+ assert_equal ".bar", resolve("nth(nth(&, 1), 3)", {}, env)
586
+ end
587
+
588
+ def test_setting_global_variable_locally_warns
589
+ assert_warning(<<WARNING) {assert_equal(<<CSS, render(<<SCSS, :syntax => :scss))}
590
+ DEPRECATION WARNING on line 4 of test_setting_global_variable_locally_warns_inline.scss:
591
+ Assigning to global variable "$var" by default is deprecated.
592
+ In future versions of Sass, this will create a new local variable.
593
+ If you want to assign to the global variable, use "$var: x !global" instead.
594
+ WARNING
595
+ .foo {
596
+ a: x; }
597
+
598
+ .bar {
599
+ b: x; }
600
+ CSS
601
+ $var: 1;
602
+
603
+ .foo {
604
+ $var: x;
605
+ a: $var;
606
+ }
607
+
608
+ .bar {
609
+ b: $var;
610
+ }
611
+ SCSS
612
+ end
613
+
614
+ def test_setting_global_variable_globally
615
+ assert_no_warning {assert_equal(<<CSS, render(<<SCSS, :syntax => :scss))}
616
+ .foo {
617
+ a: 1; }
618
+
619
+ .bar {
620
+ b: 2; }
621
+ CSS
622
+ $var: 1;
623
+
624
+ .foo {
625
+ a: $var;
626
+ }
627
+
628
+ $var: 2;
629
+
630
+ .bar {
631
+ b: $var;
632
+ }
633
+ SCSS
634
+ end
635
+
636
+ def test_setting_global_variable_with_flag
637
+ assert_no_warning {assert_equal(<<CSS, render(<<SCSS, :syntax => :scss))}
638
+ .bar {
639
+ a: x;
640
+ b: y;
641
+ c: z; }
642
+ CSS
643
+ $var1: 1;
644
+ $var3: 3;
645
+
646
+ .foo {
647
+ $var1: x !global;
648
+ $var2: y !global;
649
+ @each $var3 in _ {
650
+ $var3: z !global;
651
+ }
652
+ }
653
+
654
+ .bar {
655
+ a: $var1;
656
+ b: $var2;
657
+ c: $var3;
658
+ }
659
+ SCSS
660
+ end
661
+
662
+ def test_setting_global_variable_with_flag_and_default
663
+ assert_equal(<<CSS, render(<<SCSS, :syntax => :scss))
664
+ .bar {
665
+ a: 1;
666
+ b: y;
667
+ c: z; }
668
+ CSS
669
+ $var1: 1;
670
+
671
+ .foo {
672
+ $var1: x !global !default;
673
+ $var2: y !global !default;
674
+ @each $var3 in _ {
675
+ $var3: z !global !default;
676
+ }
677
+ }
678
+
679
+ .bar {
680
+ a: $var1;
681
+ b: $var2;
682
+ c: $var3;
683
+ }
684
+ SCSS
508
685
  end
509
686
 
510
687
  # Regression Tests
@@ -537,25 +714,48 @@ SASS
537
714
  assert_equal "and-bang", resolve("and-bang")
538
715
  end
539
716
 
717
+ def test_number_initialization
718
+ assert_equal Sass::Script::Value::Number.new(10, ["px"]), Sass::Script::Value::Number.new(10, "px")
719
+ assert_equal Sass::Script::Value::Number.new(10, ["px"], ["em"]), Sass::Script::Value::Number.new(10, "px", "em")
720
+ end
721
+
722
+ def test_is_unit
723
+ assert Sass::Script::Value::Number.new(10, "px").is_unit?("px")
724
+ assert Sass::Script::Value::Number.new(10).is_unit?(nil)
725
+ assert !Sass::Script::Value::Number.new(10, "px", "em").is_unit?("px")
726
+ assert !Sass::Script::Value::Number.new(10, [], "em").is_unit?("em")
727
+ assert !Sass::Script::Value::Number.new(10, ["px", "em"]).is_unit?("px")
728
+ end
729
+
730
+ def test_rename_redirect
731
+ assert_no_warning do
732
+ assert_equal Sass::Script::Value::Base, Sass::Script::Literal
733
+ assert_equal Sass::Script::Tree::Node, Sass::Script::Node
734
+ assert_equal Sass::Script::Tree::Operation, Sass::Script::Operation
735
+ assert_equal Sass::Script::Value::String, Sass::Script::String
736
+ end
737
+ end
738
+
540
739
  private
541
740
 
542
741
  def resolve(str, opts = {}, environment = env)
543
742
  munge_filename opts
544
743
  val = eval(str, opts, environment)
545
- val.is_a?(Sass::Script::String) ? val.value : val.to_s
744
+ assert_kind_of Sass::Script::Value::Base, val
745
+ val.is_a?(Sass::Script::Value::String) ? val.value : val.to_s
546
746
  end
547
747
 
548
748
  def assert_unquoted(str, opts = {}, environment = env)
549
749
  munge_filename opts
550
750
  val = eval(str, opts, environment)
551
- assert_kind_of Sass::Script::String, val
751
+ assert_kind_of Sass::Script::Value::String, val
552
752
  assert_equal :identifier, val.type
553
753
  end
554
754
 
555
755
  def assert_quoted(str, opts = {}, environment = env)
556
756
  munge_filename opts
557
757
  val = eval(str, opts, environment)
558
- assert_kind_of Sass::Script::String, val
758
+ assert_kind_of Sass::Script::Value::String, val
559
759
  assert_equal :string, val.type
560
760
  end
561
761
 
@@ -577,6 +777,12 @@ SASS
577
777
  env
578
778
  end
579
779
 
780
+ def selector(str)
781
+ parser = Sass::SCSS::StaticParser.new(
782
+ str, filename_for_test, Sass::Importers::Filesystem.new('.'))
783
+ parser.parse_selector
784
+ end
785
+
580
786
  def test_number_printing
581
787
  assert_equal "1", eval("1")
582
788
  assert_equal "1", eval("1.0")
@@ -586,4 +792,8 @@ SASS
586
792
  assert_equal "-Infinity", eval("-1.0/0.0")
587
793
  assert_equal "NaN", eval("0.0/0.0")
588
794
  end
795
+
796
+ def test_null_is_a_singleton
797
+ assert_same Sass::Script::Value::Null.new, Sass::Script::Value::Null.new
798
+ end
589
799
  end