oreorenasass 3.4.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 (268) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +11 -0
  3. data/CONTRIBUTING +3 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.md +221 -0
  6. data/Rakefile +370 -0
  7. data/VERSION +1 -0
  8. data/VERSION_NAME +1 -0
  9. data/bin/sass +13 -0
  10. data/bin/sass-convert +12 -0
  11. data/bin/scss +13 -0
  12. data/extra/update_watch.rb +13 -0
  13. data/init.rb +18 -0
  14. data/lib/sass/cache_stores/base.rb +88 -0
  15. data/lib/sass/cache_stores/chain.rb +34 -0
  16. data/lib/sass/cache_stores/filesystem.rb +60 -0
  17. data/lib/sass/cache_stores/memory.rb +47 -0
  18. data/lib/sass/cache_stores/null.rb +25 -0
  19. data/lib/sass/cache_stores.rb +15 -0
  20. data/lib/sass/callbacks.rb +67 -0
  21. data/lib/sass/css.rb +407 -0
  22. data/lib/sass/engine.rb +1181 -0
  23. data/lib/sass/environment.rb +191 -0
  24. data/lib/sass/error.rb +198 -0
  25. data/lib/sass/exec/base.rb +187 -0
  26. data/lib/sass/exec/sass_convert.rb +264 -0
  27. data/lib/sass/exec/sass_scss.rb +424 -0
  28. data/lib/sass/exec.rb +9 -0
  29. data/lib/sass/features.rb +47 -0
  30. data/lib/sass/importers/base.rb +182 -0
  31. data/lib/sass/importers/filesystem.rb +211 -0
  32. data/lib/sass/importers.rb +22 -0
  33. data/lib/sass/logger/base.rb +30 -0
  34. data/lib/sass/logger/log_level.rb +45 -0
  35. data/lib/sass/logger.rb +12 -0
  36. data/lib/sass/media.rb +210 -0
  37. data/lib/sass/plugin/compiler.rb +565 -0
  38. data/lib/sass/plugin/configuration.rb +118 -0
  39. data/lib/sass/plugin/generic.rb +15 -0
  40. data/lib/sass/plugin/merb.rb +48 -0
  41. data/lib/sass/plugin/rack.rb +60 -0
  42. data/lib/sass/plugin/rails.rb +47 -0
  43. data/lib/sass/plugin/staleness_checker.rb +199 -0
  44. data/lib/sass/plugin.rb +133 -0
  45. data/lib/sass/railtie.rb +10 -0
  46. data/lib/sass/repl.rb +57 -0
  47. data/lib/sass/root.rb +7 -0
  48. data/lib/sass/script/css_lexer.rb +33 -0
  49. data/lib/sass/script/css_parser.rb +34 -0
  50. data/lib/sass/script/functions.rb +2626 -0
  51. data/lib/sass/script/lexer.rb +449 -0
  52. data/lib/sass/script/parser.rb +637 -0
  53. data/lib/sass/script/tree/funcall.rb +306 -0
  54. data/lib/sass/script/tree/interpolation.rb +118 -0
  55. data/lib/sass/script/tree/list_literal.rb +77 -0
  56. data/lib/sass/script/tree/literal.rb +45 -0
  57. data/lib/sass/script/tree/map_literal.rb +64 -0
  58. data/lib/sass/script/tree/node.rb +109 -0
  59. data/lib/sass/script/tree/operation.rb +103 -0
  60. data/lib/sass/script/tree/selector.rb +26 -0
  61. data/lib/sass/script/tree/string_interpolation.rb +104 -0
  62. data/lib/sass/script/tree/unary_operation.rb +69 -0
  63. data/lib/sass/script/tree/variable.rb +57 -0
  64. data/lib/sass/script/tree.rb +16 -0
  65. data/lib/sass/script/value/arg_list.rb +36 -0
  66. data/lib/sass/script/value/base.rb +240 -0
  67. data/lib/sass/script/value/bool.rb +35 -0
  68. data/lib/sass/script/value/color.rb +680 -0
  69. data/lib/sass/script/value/helpers.rb +262 -0
  70. data/lib/sass/script/value/list.rb +113 -0
  71. data/lib/sass/script/value/map.rb +70 -0
  72. data/lib/sass/script/value/null.rb +44 -0
  73. data/lib/sass/script/value/number.rb +530 -0
  74. data/lib/sass/script/value/string.rb +97 -0
  75. data/lib/sass/script/value.rb +11 -0
  76. data/lib/sass/script.rb +66 -0
  77. data/lib/sass/scss/css_parser.rb +42 -0
  78. data/lib/sass/scss/parser.rb +1209 -0
  79. data/lib/sass/scss/rx.rb +141 -0
  80. data/lib/sass/scss/script_lexer.rb +15 -0
  81. data/lib/sass/scss/script_parser.rb +25 -0
  82. data/lib/sass/scss/static_parser.rb +368 -0
  83. data/lib/sass/scss.rb +16 -0
  84. data/lib/sass/selector/abstract_sequence.rb +109 -0
  85. data/lib/sass/selector/comma_sequence.rb +175 -0
  86. data/lib/sass/selector/pseudo.rb +256 -0
  87. data/lib/sass/selector/sequence.rb +600 -0
  88. data/lib/sass/selector/simple.rb +117 -0
  89. data/lib/sass/selector/simple_sequence.rb +325 -0
  90. data/lib/sass/selector.rb +326 -0
  91. data/lib/sass/shared.rb +76 -0
  92. data/lib/sass/source/map.rb +210 -0
  93. data/lib/sass/source/position.rb +39 -0
  94. data/lib/sass/source/range.rb +41 -0
  95. data/lib/sass/stack.rb +120 -0
  96. data/lib/sass/supports.rb +227 -0
  97. data/lib/sass/tree/at_root_node.rb +83 -0
  98. data/lib/sass/tree/charset_node.rb +22 -0
  99. data/lib/sass/tree/comment_node.rb +82 -0
  100. data/lib/sass/tree/content_node.rb +9 -0
  101. data/lib/sass/tree/css_import_node.rb +60 -0
  102. data/lib/sass/tree/debug_node.rb +18 -0
  103. data/lib/sass/tree/directive_node.rb +59 -0
  104. data/lib/sass/tree/each_node.rb +24 -0
  105. data/lib/sass/tree/error_node.rb +18 -0
  106. data/lib/sass/tree/extend_node.rb +43 -0
  107. data/lib/sass/tree/for_node.rb +36 -0
  108. data/lib/sass/tree/function_node.rb +39 -0
  109. data/lib/sass/tree/if_node.rb +52 -0
  110. data/lib/sass/tree/import_node.rb +74 -0
  111. data/lib/sass/tree/keyframe_rule_node.rb +15 -0
  112. data/lib/sass/tree/media_node.rb +48 -0
  113. data/lib/sass/tree/mixin_def_node.rb +38 -0
  114. data/lib/sass/tree/mixin_node.rb +52 -0
  115. data/lib/sass/tree/node.rb +238 -0
  116. data/lib/sass/tree/prop_node.rb +171 -0
  117. data/lib/sass/tree/return_node.rb +19 -0
  118. data/lib/sass/tree/root_node.rb +44 -0
  119. data/lib/sass/tree/rule_node.rb +145 -0
  120. data/lib/sass/tree/supports_node.rb +38 -0
  121. data/lib/sass/tree/trace_node.rb +33 -0
  122. data/lib/sass/tree/variable_node.rb +36 -0
  123. data/lib/sass/tree/visitors/base.rb +72 -0
  124. data/lib/sass/tree/visitors/check_nesting.rb +177 -0
  125. data/lib/sass/tree/visitors/convert.rb +334 -0
  126. data/lib/sass/tree/visitors/cssize.rb +369 -0
  127. data/lib/sass/tree/visitors/deep_copy.rb +107 -0
  128. data/lib/sass/tree/visitors/extend.rb +68 -0
  129. data/lib/sass/tree/visitors/perform.rb +539 -0
  130. data/lib/sass/tree/visitors/set_options.rb +139 -0
  131. data/lib/sass/tree/visitors/to_css.rb +381 -0
  132. data/lib/sass/tree/warn_node.rb +18 -0
  133. data/lib/sass/tree/while_node.rb +18 -0
  134. data/lib/sass/util/cross_platform_random.rb +19 -0
  135. data/lib/sass/util/multibyte_string_scanner.rb +157 -0
  136. data/lib/sass/util/normalized_map.rb +130 -0
  137. data/lib/sass/util/ordered_hash.rb +192 -0
  138. data/lib/sass/util/subset_map.rb +110 -0
  139. data/lib/sass/util/test.rb +9 -0
  140. data/lib/sass/util.rb +1318 -0
  141. data/lib/sass/version.rb +124 -0
  142. data/lib/sass.rb +102 -0
  143. data/rails/init.rb +1 -0
  144. data/test/sass/cache_test.rb +131 -0
  145. data/test/sass/callbacks_test.rb +61 -0
  146. data/test/sass/compiler_test.rb +232 -0
  147. data/test/sass/conversion_test.rb +2054 -0
  148. data/test/sass/css2sass_test.rb +477 -0
  149. data/test/sass/data/hsl-rgb.txt +319 -0
  150. data/test/sass/encoding_test.rb +219 -0
  151. data/test/sass/engine_test.rb +3301 -0
  152. data/test/sass/exec_test.rb +86 -0
  153. data/test/sass/extend_test.rb +1661 -0
  154. data/test/sass/fixtures/test_staleness_check_across_importers.css +1 -0
  155. data/test/sass/fixtures/test_staleness_check_across_importers.scss +1 -0
  156. data/test/sass/functions_test.rb +1926 -0
  157. data/test/sass/importer_test.rb +412 -0
  158. data/test/sass/logger_test.rb +58 -0
  159. data/test/sass/mock_importer.rb +49 -0
  160. data/test/sass/more_results/more1.css +9 -0
  161. data/test/sass/more_results/more1_with_line_comments.css +26 -0
  162. data/test/sass/more_results/more_import.css +29 -0
  163. data/test/sass/more_templates/_more_partial.sass +2 -0
  164. data/test/sass/more_templates/more1.sass +23 -0
  165. data/test/sass/more_templates/more_import.sass +11 -0
  166. data/test/sass/plugin_test.rb +554 -0
  167. data/test/sass/results/alt.css +4 -0
  168. data/test/sass/results/basic.css +9 -0
  169. data/test/sass/results/cached_import_option.css +3 -0
  170. data/test/sass/results/compact.css +5 -0
  171. data/test/sass/results/complex.css +86 -0
  172. data/test/sass/results/compressed.css +1 -0
  173. data/test/sass/results/expanded.css +19 -0
  174. data/test/sass/results/filename_fn.css +3 -0
  175. data/test/sass/results/if.css +3 -0
  176. data/test/sass/results/import.css +31 -0
  177. data/test/sass/results/import_charset.css +5 -0
  178. data/test/sass/results/import_charset_1_8.css +5 -0
  179. data/test/sass/results/import_charset_ibm866.css +5 -0
  180. data/test/sass/results/import_content.css +1 -0
  181. data/test/sass/results/line_numbers.css +49 -0
  182. data/test/sass/results/mixins.css +95 -0
  183. data/test/sass/results/multiline.css +24 -0
  184. data/test/sass/results/nested.css +22 -0
  185. data/test/sass/results/options.css +1 -0
  186. data/test/sass/results/parent_ref.css +13 -0
  187. data/test/sass/results/script.css +16 -0
  188. data/test/sass/results/scss_import.css +31 -0
  189. data/test/sass/results/scss_importee.css +2 -0
  190. data/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
  191. data/test/sass/results/subdir/subdir.css +3 -0
  192. data/test/sass/results/units.css +11 -0
  193. data/test/sass/results/warn.css +0 -0
  194. data/test/sass/results/warn_imported.css +0 -0
  195. data/test/sass/script_conversion_test.rb +328 -0
  196. data/test/sass/script_test.rb +1054 -0
  197. data/test/sass/scss/css_test.rb +1215 -0
  198. data/test/sass/scss/rx_test.rb +156 -0
  199. data/test/sass/scss/scss_test.rb +3900 -0
  200. data/test/sass/scss/test_helper.rb +37 -0
  201. data/test/sass/source_map_test.rb +977 -0
  202. data/test/sass/superselector_test.rb +191 -0
  203. data/test/sass/templates/_cached_import_option_partial.scss +1 -0
  204. data/test/sass/templates/_double_import_loop2.sass +1 -0
  205. data/test/sass/templates/_filename_fn_import.scss +11 -0
  206. data/test/sass/templates/_imported_charset_ibm866.sass +4 -0
  207. data/test/sass/templates/_imported_charset_utf8.sass +4 -0
  208. data/test/sass/templates/_imported_content.sass +3 -0
  209. data/test/sass/templates/_partial.sass +2 -0
  210. data/test/sass/templates/_same_name_different_partiality.scss +1 -0
  211. data/test/sass/templates/alt.sass +16 -0
  212. data/test/sass/templates/basic.sass +23 -0
  213. data/test/sass/templates/bork1.sass +2 -0
  214. data/test/sass/templates/bork2.sass +2 -0
  215. data/test/sass/templates/bork3.sass +2 -0
  216. data/test/sass/templates/bork4.sass +2 -0
  217. data/test/sass/templates/bork5.sass +3 -0
  218. data/test/sass/templates/cached_import_option.scss +3 -0
  219. data/test/sass/templates/compact.sass +17 -0
  220. data/test/sass/templates/complex.sass +305 -0
  221. data/test/sass/templates/compressed.sass +15 -0
  222. data/test/sass/templates/double_import_loop1.sass +1 -0
  223. data/test/sass/templates/expanded.sass +17 -0
  224. data/test/sass/templates/filename_fn.scss +18 -0
  225. data/test/sass/templates/if.sass +11 -0
  226. data/test/sass/templates/import.sass +12 -0
  227. data/test/sass/templates/import_charset.sass +9 -0
  228. data/test/sass/templates/import_charset_1_8.sass +6 -0
  229. data/test/sass/templates/import_charset_ibm866.sass +11 -0
  230. data/test/sass/templates/import_content.sass +4 -0
  231. data/test/sass/templates/importee.less +2 -0
  232. data/test/sass/templates/importee.sass +19 -0
  233. data/test/sass/templates/line_numbers.sass +13 -0
  234. data/test/sass/templates/mixin_bork.sass +5 -0
  235. data/test/sass/templates/mixins.sass +76 -0
  236. data/test/sass/templates/multiline.sass +20 -0
  237. data/test/sass/templates/nested.sass +25 -0
  238. data/test/sass/templates/nested_bork1.sass +2 -0
  239. data/test/sass/templates/nested_bork2.sass +2 -0
  240. data/test/sass/templates/nested_bork3.sass +2 -0
  241. data/test/sass/templates/nested_bork4.sass +2 -0
  242. data/test/sass/templates/nested_import.sass +2 -0
  243. data/test/sass/templates/nested_mixin_bork.sass +6 -0
  244. data/test/sass/templates/options.sass +2 -0
  245. data/test/sass/templates/parent_ref.sass +25 -0
  246. data/test/sass/templates/same_name_different_ext.sass +2 -0
  247. data/test/sass/templates/same_name_different_ext.scss +1 -0
  248. data/test/sass/templates/same_name_different_partiality.scss +1 -0
  249. data/test/sass/templates/script.sass +101 -0
  250. data/test/sass/templates/scss_import.scss +12 -0
  251. data/test/sass/templates/scss_importee.scss +1 -0
  252. data/test/sass/templates/single_import_loop.sass +1 -0
  253. data/test/sass/templates/subdir/import_up1.scss +1 -0
  254. data/test/sass/templates/subdir/import_up2.scss +1 -0
  255. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
  256. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
  257. data/test/sass/templates/subdir/subdir.sass +6 -0
  258. data/test/sass/templates/units.sass +11 -0
  259. data/test/sass/templates/warn.sass +3 -0
  260. data/test/sass/templates/warn_imported.sass +4 -0
  261. data/test/sass/test_helper.rb +8 -0
  262. data/test/sass/util/multibyte_string_scanner_test.rb +147 -0
  263. data/test/sass/util/normalized_map_test.rb +51 -0
  264. data/test/sass/util/subset_map_test.rb +91 -0
  265. data/test/sass/util_test.rb +467 -0
  266. data/test/sass/value_helpers_test.rb +179 -0
  267. data/test/test_helper.rb +109 -0
  268. metadata +386 -0
@@ -0,0 +1 @@
1
+ .pear { color: green; }
@@ -0,0 +1 @@
1
+ @import "apple";
@@ -0,0 +1,1926 @@
1
+ #!/usr/bin/env ruby
2
+ require 'minitest/autorun'
3
+ require File.dirname(__FILE__) + '/../test_helper'
4
+ require File.dirname(__FILE__) + '/test_helper'
5
+ require 'sass/script'
6
+ require 'mock_importer'
7
+
8
+ module Sass::Script::Functions
9
+ def no_kw_args
10
+ Sass::Script::Value::String.new("no-kw-args")
11
+ end
12
+
13
+ def only_var_args(*args)
14
+ Sass::Script::Value::String.new("only-var-args("+args.map{|a| a.plus(Sass::Script::Value::Number.new(1)).to_s }.join(", ")+")")
15
+ end
16
+ declare :only_var_args, [], :var_args => true
17
+
18
+ def only_kw_args(kwargs)
19
+ Sass::Script::Value::String.new("only-kw-args(" + kwargs.keys.map {|a| a.to_s}.sort.join(", ") + ")")
20
+ end
21
+ declare :only_kw_args, [], :var_kwargs => true
22
+
23
+ def deprecated_arg_fn(arg1, arg2, arg3 = nil)
24
+ Sass::Script::Value::List.new([arg1, arg2, arg3 || Sass::Script::Value::Null.new], :space)
25
+ end
26
+ declare :deprecated_arg_fn, [:arg1, :arg2, :arg3], :deprecated => [:arg_1, :arg_2, :arg3]
27
+ declare :deprecated_arg_fn, [:arg1, :arg2], :deprecated => [:arg_1, :arg_2]
28
+ end
29
+
30
+ module Sass::Script::Functions::UserFunctions
31
+ def call_options_on_new_value
32
+ str = Sass::Script::Value::String.new("foo")
33
+ str.options[:foo]
34
+ str
35
+ end
36
+
37
+ def user_defined
38
+ Sass::Script::Value::String.new("I'm a user-defined string!")
39
+ end
40
+
41
+ def _preceding_underscore
42
+ Sass::Script::Value::String.new("I'm another user-defined string!")
43
+ end
44
+
45
+ def fetch_the_variable
46
+ environment.var('variable')
47
+ end
48
+ end
49
+
50
+ module Sass::Script::Functions
51
+ include Sass::Script::Functions::UserFunctions
52
+ end
53
+
54
+ class SassFunctionTest < MiniTest::Test
55
+ # Tests taken from:
56
+ # http://www.w3.org/Style/CSS/Test/CSS3/Color/20070927/html4/t040204-hsl-h-rotating-b.htm
57
+ # http://www.w3.org/Style/CSS/Test/CSS3/Color/20070927/html4/t040204-hsl-values-b.htm
58
+ File.read(File.dirname(__FILE__) + "/data/hsl-rgb.txt").split("\n\n").each do |chunk|
59
+ hsls, rgbs = chunk.strip.split("====")
60
+ hsls.strip.split("\n").zip(rgbs.strip.split("\n")) do |hsl, rgb|
61
+ hsl_method = "test_hsl: #{hsl} = #{rgb}"
62
+ unless method_defined?(hsl_method)
63
+ define_method(hsl_method) do
64
+ assert_equal(evaluate(rgb), evaluate(hsl))
65
+ end
66
+ end
67
+
68
+ rgb_to_hsl_method = "test_rgb_to_hsl: #{rgb} = #{hsl}"
69
+ unless method_defined?(rgb_to_hsl_method)
70
+ define_method(rgb_to_hsl_method) do
71
+ rgb_color = perform(rgb)
72
+ hsl_color = perform(hsl)
73
+
74
+ white = hsl_color.lightness == 100
75
+ black = hsl_color.lightness == 0
76
+ grayscale = white || black || hsl_color.saturation == 0
77
+
78
+ assert_in_delta(hsl_color.hue, rgb_color.hue, 0.0001,
79
+ "Hues should be equal") unless grayscale
80
+ assert_in_delta(hsl_color.saturation, rgb_color.saturation, 0.0001,
81
+ "Saturations should be equal") unless white || black
82
+ assert_in_delta(hsl_color.lightness, rgb_color.lightness, 0.0001,
83
+ "Lightnesses should be equal")
84
+ end
85
+ end
86
+ end
87
+ end
88
+
89
+ def test_hsl_kwargs
90
+ assert_equal "#33cccc", evaluate("hsl($hue: 180, $saturation: 60%, $lightness: 50%)")
91
+ end
92
+
93
+ def test_hsl_clamps_bounds
94
+ assert_equal("#1f1f1f", evaluate("hsl(10, -114, 12)"))
95
+ assert_equal("white", evaluate("hsl(10, 10, 256%)"))
96
+ end
97
+
98
+ def test_hsl_checks_types
99
+ assert_error_message("$hue: \"foo\" is not a number for `hsl'", "hsl(\"foo\", 10, 12)");
100
+ assert_error_message("$saturation: \"foo\" is not a number for `hsl'", "hsl(10, \"foo\", 12)");
101
+ assert_error_message("$lightness: \"foo\" is not a number for `hsl'", "hsl(10, 10, \"foo\")");
102
+ end
103
+
104
+ def test_hsla
105
+ assert_equal "rgba(51, 204, 204, 0.4)", evaluate("hsla(180, 60%, 50%, 0.4)")
106
+ assert_equal "#33cccc", evaluate("hsla(180, 60%, 50%, 1)")
107
+ assert_equal "rgba(51, 204, 204, 0)", evaluate("hsla(180, 60%, 50%, 0)")
108
+ assert_equal "rgba(51, 204, 204, 0.4)", evaluate("hsla($hue: 180, $saturation: 60%, $lightness: 50%, $alpha: 0.4)")
109
+ end
110
+
111
+ def test_hsla_clamps_bounds
112
+ assert_equal("#1f1f1f", evaluate("hsla(10, -114, 12, 1)"))
113
+ assert_equal("rgba(255, 255, 255, 0)", evaluate("hsla(10, 10, 256%, 0)"))
114
+ assert_equal("rgba(28, 24, 23, 0)", evaluate("hsla(10, 10, 10, -0.1)"))
115
+ assert_equal("#1c1817", evaluate("hsla(10, 10, 10, 1.1)"))
116
+ end
117
+
118
+ def test_hsla_checks_types
119
+ assert_error_message("$hue: \"foo\" is not a number for `hsla'", "hsla(\"foo\", 10, 12, 0.3)");
120
+ assert_error_message("$saturation: \"foo\" is not a number for `hsla'", "hsla(10, \"foo\", 12, 0)");
121
+ assert_error_message("$lightness: \"foo\" is not a number for `hsla'", "hsla(10, 10, \"foo\", 1)");
122
+ assert_error_message("$alpha: \"foo\" is not a number for `hsla'", "hsla(10, 10, 10, \"foo\")");
123
+ end
124
+
125
+ def test_percentage
126
+ assert_equal("50%", evaluate("percentage(.5)"))
127
+ assert_equal("100%", evaluate("percentage(1)"))
128
+ assert_equal("25%", evaluate("percentage(25px / 100px)"))
129
+ assert_equal("50%", evaluate("percentage($number: 0.5)"))
130
+ end
131
+
132
+ def test_percentage_checks_types
133
+ assert_error_message("$number: 25px is not a unitless number for `percentage'", "percentage(25px)")
134
+ assert_error_message("$number: #cccccc is not a unitless number for `percentage'", "percentage(#ccc)")
135
+ assert_error_message("$number: \"string\" is not a unitless number for `percentage'", %Q{percentage("string")})
136
+ end
137
+
138
+ def test_round
139
+ assert_equal("5", evaluate("round(4.8)"))
140
+ assert_equal("5px", evaluate("round(4.8px)"))
141
+ assert_equal("5px", evaluate("round(5.49px)"))
142
+ assert_equal("5px", evaluate("round($number: 5.49px)"))
143
+ end
144
+
145
+ def test_round_checks_types
146
+ assert_error_message("$value: #cccccc is not a number for `round'", "round(#ccc)")
147
+ end
148
+
149
+ def test_floor
150
+ assert_equal("4", evaluate("floor(4.8)"))
151
+ assert_equal("4px", evaluate("floor(4.8px)"))
152
+ assert_equal("4px", evaluate("floor($number: 4.8px)"))
153
+ end
154
+
155
+ def test_floor_checks_types
156
+ assert_error_message("$value: \"foo\" is not a number for `floor'", "floor(\"foo\")")
157
+ end
158
+
159
+ def test_ceil
160
+ assert_equal("5", evaluate("ceil(4.1)"))
161
+ assert_equal("5px", evaluate("ceil(4.8px)"))
162
+ assert_equal("5px", evaluate("ceil($number: 4.8px)"))
163
+ end
164
+
165
+ def test_ceil_checks_types
166
+ assert_error_message("$value: \"a\" is not a number for `ceil'", "ceil(\"a\")")
167
+ end
168
+
169
+ def test_abs
170
+ assert_equal("5", evaluate("abs(-5)"))
171
+ assert_equal("5px", evaluate("abs(-5px)"))
172
+ assert_equal("5", evaluate("abs(5)"))
173
+ assert_equal("5px", evaluate("abs(5px)"))
174
+ assert_equal("5px", evaluate("abs($number: 5px)"))
175
+ end
176
+
177
+ def test_abs_checks_types
178
+ assert_error_message("$value: #aaaaaa is not a number for `abs'", "abs(#aaa)")
179
+ end
180
+
181
+ def test_min
182
+ assert_equal("1", evaluate("min(1, 2, 3)"))
183
+ assert_equal("1", evaluate("min(3px, 2px, 1)"))
184
+ assert_equal("4em", evaluate("min(4em)"))
185
+ assert_equal("10cm", evaluate("min(10cm, 6in)"))
186
+
187
+ assert_error_message("#aaaaaa is not a number for `min'", "min(#aaa)")
188
+ assert_error_message("Incompatible units: 'px' and 'em'.", "min(3em, 4em, 1px)")
189
+ end
190
+
191
+ def test_max
192
+ assert_equal("3", evaluate("max(1, 2, 3)"))
193
+ assert_equal("3", evaluate("max(3, 2px, 1px)"))
194
+ assert_equal("4em", evaluate("max(4em)"))
195
+ assert_equal("6in", evaluate("max(10cm, 6in)"))
196
+
197
+ assert_error_message("#aaaaaa is not a number for `max'", "max(#aaa)")
198
+ assert_error_message("Incompatible units: 'px' and 'em'.", "max(3em, 4em, 1px)")
199
+ end
200
+
201
+ def test_rgb
202
+ assert_equal("#123456", evaluate("rgb(18, 52, 86)"))
203
+ assert_equal("#beaded", evaluate("rgb(190, 173, 237)"))
204
+ assert_equal("springgreen", evaluate("rgb(0, 255, 127)"))
205
+ assert_equal("springgreen", evaluate("rgb($red: 0, $green: 255, $blue: 127)"))
206
+ end
207
+
208
+ def test_rgb_percent
209
+ assert_equal("#123456", evaluate("rgb(7.1%, 20.4%, 34%)"))
210
+ assert_equal("#beaded", evaluate("rgb(74.7%, 173, 93%)"))
211
+ assert_equal("#beaded", evaluate("rgb(190, 68%, 237)"))
212
+ assert_equal("springgreen", evaluate("rgb(0%, 100%, 50%)"))
213
+ end
214
+
215
+ def test_rgb_clamps_bounds
216
+ assert_equal("#ff0101", evaluate("rgb(256, 1, 1)"))
217
+ assert_equal("#01ff01", evaluate("rgb(1, 256, 1)"))
218
+ assert_equal("#0101ff", evaluate("rgb(1, 1, 256)"))
219
+ assert_equal("#01ffff", evaluate("rgb(1, 256, 257)"))
220
+ assert_equal("#000101", evaluate("rgb(-1, 1, 1)"))
221
+ end
222
+
223
+ def test_rgb_clamps_percent_bounds
224
+ assert_equal("red", evaluate("rgb(100.1%, 0, 0)"))
225
+ assert_equal("black", evaluate("rgb(0, -0.1%, 0)"))
226
+ assert_equal("blue", evaluate("rgb(0, 0, 101%)"))
227
+ end
228
+
229
+ def test_rgb_tests_types
230
+ assert_error_message("$red: \"foo\" is not a number for `rgb'", "rgb(\"foo\", 10, 12)");
231
+ assert_error_message("$green: \"foo\" is not a number for `rgb'", "rgb(10, \"foo\", 12)");
232
+ assert_error_message("$blue: \"foo\" is not a number for `rgb'", "rgb(10, 10, \"foo\")");
233
+ end
234
+
235
+ def test_rgba
236
+ assert_equal("rgba(18, 52, 86, 0.5)", evaluate("rgba(18, 52, 86, 0.5)"))
237
+ assert_equal("#beaded", evaluate("rgba(190, 173, 237, 1)"))
238
+ assert_equal("rgba(0, 255, 127, 0)", evaluate("rgba(0, 255, 127, 0)"))
239
+ assert_equal("rgba(0, 255, 127, 0)", evaluate("rgba($red: 0, $green: 255, $blue: 127, $alpha: 0)"))
240
+ end
241
+
242
+ def test_rgba_clamps_bounds
243
+ assert_equal("rgba(255, 1, 1, 0.3)", evaluate("rgba(256, 1, 1, 0.3)"))
244
+ assert_equal("rgba(1, 255, 1, 0.3)", evaluate("rgba(1, 256, 1, 0.3)"))
245
+ assert_equal("rgba(1, 1, 255, 0.3)", evaluate("rgba(1, 1, 256, 0.3)"))
246
+ assert_equal("rgba(1, 255, 255, 0.3)", evaluate("rgba(1, 256, 257, 0.3)"))
247
+ assert_equal("rgba(0, 1, 1, 0.3)", evaluate("rgba(-1, 1, 1, 0.3)"))
248
+ assert_equal("rgba(1, 1, 1, 0)", evaluate("rgba(1, 1, 1, -0.2)"))
249
+ assert_equal("#010101", evaluate("rgba(1, 1, 1, 1.2)"))
250
+ end
251
+
252
+ def test_rgba_tests_types
253
+ assert_error_message("$red: \"foo\" is not a number for `rgba'", "rgba(\"foo\", 10, 12, 0.2)");
254
+ assert_error_message("$green: \"foo\" is not a number for `rgba'", "rgba(10, \"foo\", 12, 0.1)");
255
+ assert_error_message("$blue: \"foo\" is not a number for `rgba'", "rgba(10, 10, \"foo\", 0)");
256
+ assert_error_message("$alpha: \"foo\" is not a number for `rgba'", "rgba(10, 10, 10, \"foo\")");
257
+ end
258
+
259
+ def test_rgba_with_color
260
+ assert_equal "rgba(16, 32, 48, 0.5)", evaluate("rgba(#102030, 0.5)")
261
+ assert_equal "rgba(0, 0, 255, 0.5)", evaluate("rgba(blue, 0.5)")
262
+ assert_equal "rgba(0, 0, 255, 0.5)", evaluate("rgba($color: blue, $alpha: 0.5)")
263
+ end
264
+
265
+ def test_rgba_with_color_tests_types
266
+ assert_error_message("$color: \"foo\" is not a color for `rgba'", "rgba(\"foo\", 0.2)");
267
+ assert_error_message("$alpha: \"foo\" is not a number for `rgba'", "rgba(blue, \"foo\")");
268
+ end
269
+
270
+ def test_rgba_tests_num_args
271
+ assert_error_message("wrong number of arguments (0 for 4) for `rgba'", "rgba()");
272
+ assert_error_message("wrong number of arguments (1 for 4) for `rgba'", "rgba(blue)");
273
+ assert_error_message("wrong number of arguments (3 for 4) for `rgba'", "rgba(1, 2, 3)");
274
+ assert_error_message("wrong number of arguments (5 for 4) for `rgba'", "rgba(1, 2, 3, 0.4, 5)");
275
+ end
276
+
277
+ def test_red
278
+ assert_equal("18", evaluate("red(#123456)"))
279
+ assert_equal("18", evaluate("red($color: #123456)"))
280
+ end
281
+
282
+ def test_red_exception
283
+ assert_error_message("$color: 12 is not a color for `red'", "red(12)")
284
+ end
285
+
286
+ def test_green
287
+ assert_equal("52", evaluate("green(#123456)"))
288
+ assert_equal("52", evaluate("green($color: #123456)"))
289
+ end
290
+
291
+ def test_green_exception
292
+ assert_error_message("$color: 12 is not a color for `green'", "green(12)")
293
+ end
294
+
295
+ def test_blue
296
+ assert_equal("86", evaluate("blue(#123456)"))
297
+ assert_equal("86", evaluate("blue($color: #123456)"))
298
+ end
299
+
300
+ def test_blue_exception
301
+ assert_error_message("$color: 12 is not a color for `blue'", "blue(12)")
302
+ end
303
+
304
+ def test_hue
305
+ assert_equal("18deg", evaluate("hue(hsl(18, 50%, 20%))"))
306
+ assert_equal("18deg", evaluate("hue($color: hsl(18, 50%, 20%))"))
307
+ end
308
+
309
+ def test_hue_exception
310
+ assert_error_message("$color: 12 is not a color for `hue'", "hue(12)")
311
+ end
312
+
313
+ def test_saturation
314
+ assert_equal("52%", evaluate("saturation(hsl(20, 52%, 20%))"))
315
+ assert_equal("52%", evaluate("saturation(hsl(20, 52, 20%))"))
316
+ assert_equal("52%", evaluate("saturation($color: hsl(20, 52, 20%))"))
317
+ end
318
+
319
+ def test_saturation_exception
320
+ assert_error_message("$color: 12 is not a color for `saturation'", "saturation(12)")
321
+ end
322
+
323
+ def test_lightness
324
+ assert_equal("86%", evaluate("lightness(hsl(120, 50%, 86%))"))
325
+ assert_equal("86%", evaluate("lightness(hsl(120, 50%, 86))"))
326
+ assert_equal("86%", evaluate("lightness($color: hsl(120, 50%, 86))"))
327
+ end
328
+
329
+ def test_lightness_exception
330
+ assert_error_message("$color: 12 is not a color for `lightness'", "lightness(12)")
331
+ end
332
+
333
+ def test_alpha
334
+ assert_equal("1", evaluate("alpha(#123456)"))
335
+ assert_equal("0.34", evaluate("alpha(rgba(0, 1, 2, 0.34))"))
336
+ assert_equal("0", evaluate("alpha(hsla(0, 1, 2, 0))"))
337
+ assert_equal("0", evaluate("alpha($color: hsla(0, 1, 2, 0))"))
338
+ end
339
+
340
+ def test_alpha_exception
341
+ assert_error_message("$color: 12 is not a color for `alpha'", "alpha(12)")
342
+ end
343
+
344
+ def test_opacity
345
+ assert_equal("1", evaluate("opacity(#123456)"))
346
+ assert_equal("0.34", evaluate("opacity(rgba(0, 1, 2, 0.34))"))
347
+ assert_equal("0", evaluate("opacity(hsla(0, 1, 2, 0))"))
348
+ assert_equal("0", evaluate("opacity($color: hsla(0, 1, 2, 0))"))
349
+ assert_equal("opacity(20%)", evaluate("opacity(20%)"))
350
+ end
351
+
352
+ def test_opacity_exception
353
+ assert_error_message("$color: \"foo\" is not a color for `opacity'", "opacity(foo)")
354
+ end
355
+
356
+ def test_opacify
357
+ assert_equal("rgba(0, 0, 0, 0.75)", evaluate("opacify(rgba(0, 0, 0, 0.5), 0.25)"))
358
+ assert_equal("rgba(0, 0, 0, 0.3)", evaluate("opacify(rgba(0, 0, 0, 0.2), 0.1)"))
359
+ assert_equal("rgba(0, 0, 0, 0.7)", evaluate("fade-in(rgba(0, 0, 0, 0.2), 0.5px)"))
360
+ assert_equal("black", evaluate("fade_in(rgba(0, 0, 0, 0.2), 0.8)"))
361
+ assert_equal("black", evaluate("opacify(rgba(0, 0, 0, 0.2), 1)"))
362
+ assert_equal("rgba(0, 0, 0, 0.2)", evaluate("opacify(rgba(0, 0, 0, 0.2), 0%)"))
363
+ assert_equal("rgba(0, 0, 0, 0.2)", evaluate("opacify($color: rgba(0, 0, 0, 0.2), $amount: 0%)"))
364
+ assert_equal("rgba(0, 0, 0, 0.2)", evaluate("fade-in($color: rgba(0, 0, 0, 0.2), $amount: 0%)"))
365
+ end
366
+
367
+ def test_opacify_tests_bounds
368
+ assert_error_message("Amount -0.001 must be between 0 and 1 for `opacify'",
369
+ "opacify(rgba(0, 0, 0, 0.2), -0.001)")
370
+ assert_error_message("Amount 1.001 must be between 0 and 1 for `opacify'",
371
+ "opacify(rgba(0, 0, 0, 0.2), 1.001)")
372
+ end
373
+
374
+ def test_opacify_tests_types
375
+ assert_error_message("$color: \"foo\" is not a color for `opacify'", "opacify(\"foo\", 10%)")
376
+ assert_error_message("$amount: \"foo\" is not a number for `opacify'", "opacify(#fff, \"foo\")")
377
+ end
378
+
379
+ def test_transparentize
380
+ assert_equal("rgba(0, 0, 0, 0.3)", evaluate("transparentize(rgba(0, 0, 0, 0.5), 0.2)"))
381
+ assert_equal("rgba(0, 0, 0, 0.1)", evaluate("transparentize(rgba(0, 0, 0, 0.2), 0.1)"))
382
+ assert_equal("rgba(0, 0, 0, 0.2)", evaluate("fade-out(rgba(0, 0, 0, 0.5), 0.3px)"))
383
+ assert_equal("transparent", evaluate("fade_out(rgba(0, 0, 0, 0.2), 0.2)"))
384
+ assert_equal("transparent", evaluate("transparentize(rgba(0, 0, 0, 0.2), 1)"))
385
+ assert_equal("rgba(0, 0, 0, 0.2)", evaluate("transparentize(rgba(0, 0, 0, 0.2), 0)"))
386
+ assert_equal("rgba(0, 0, 0, 0.2)", evaluate("transparentize($color: rgba(0, 0, 0, 0.2), $amount: 0)"))
387
+ assert_equal("rgba(0, 0, 0, 0.2)", evaluate("fade-out($color: rgba(0, 0, 0, 0.2), $amount: 0)"))
388
+ end
389
+
390
+ def test_transparentize_tests_bounds
391
+ assert_error_message("Amount -0.001 must be between 0 and 1 for `transparentize'",
392
+ "transparentize(rgba(0, 0, 0, 0.2), -0.001)")
393
+ assert_error_message("Amount 1.001 must be between 0 and 1 for `transparentize'",
394
+ "transparentize(rgba(0, 0, 0, 0.2), 1.001)")
395
+ end
396
+
397
+ def test_transparentize_tests_types
398
+ assert_error_message("$color: \"foo\" is not a color for `transparentize'", "transparentize(\"foo\", 10%)")
399
+ assert_error_message("$amount: \"foo\" is not a number for `transparentize'", "transparentize(#fff, \"foo\")")
400
+ end
401
+
402
+ def test_lighten
403
+ assert_equal("#4d4d4d", evaluate("lighten(hsl(0, 0, 0), 30%)"))
404
+ assert_equal("#ee0000", evaluate("lighten(#800, 20%)"))
405
+ assert_equal("white", evaluate("lighten(#fff, 20%)"))
406
+ assert_equal("white", evaluate("lighten(#800, 100%)"))
407
+ assert_equal("#880000", evaluate("lighten(#800, 0%)"))
408
+ assert_equal("rgba(238, 0, 0, 0.5)", evaluate("lighten(rgba(136, 0, 0, 0.5), 20%)"))
409
+ assert_equal("rgba(238, 0, 0, 0.5)", evaluate("lighten($color: rgba(136, 0, 0, 0.5), $amount: 20%)"))
410
+ end
411
+
412
+ def test_lighten_tests_bounds
413
+ assert_error_message("Amount -0.001 must be between 0% and 100% for `lighten'",
414
+ "lighten(#123, -0.001)")
415
+ assert_error_message("Amount 100.001 must be between 0% and 100% for `lighten'",
416
+ "lighten(#123, 100.001)")
417
+ end
418
+
419
+ def test_lighten_tests_types
420
+ assert_error_message("$color: \"foo\" is not a color for `lighten'", "lighten(\"foo\", 10%)")
421
+ assert_error_message("$amount: \"foo\" is not a number for `lighten'", "lighten(#fff, \"foo\")")
422
+ end
423
+
424
+ def test_darken
425
+ assert_equal("#ff6a00", evaluate("darken(hsl(25, 100, 80), 30%)"))
426
+ assert_equal("#220000", evaluate("darken(#800, 20%)"))
427
+ assert_equal("black", evaluate("darken(#000, 20%)"))
428
+ assert_equal("black", evaluate("darken(#800, 100%)"))
429
+ assert_equal("#880000", evaluate("darken(#800, 0%)"))
430
+ assert_equal("rgba(34, 0, 0, 0.5)", evaluate("darken(rgba(136, 0, 0, 0.5), 20%)"))
431
+ assert_equal("rgba(34, 0, 0, 0.5)", evaluate("darken($color: rgba(136, 0, 0, 0.5), $amount: 20%)"))
432
+ end
433
+
434
+ def test_darken_tests_bounds
435
+ assert_error_message("Amount -0.001 must be between 0% and 100% for `darken'",
436
+ "darken(#123, -0.001)")
437
+ assert_error_message("Amount 100.001 must be between 0% and 100% for `darken'",
438
+ "darken(#123, 100.001)")
439
+ end
440
+
441
+ def test_darken_tests_types
442
+ assert_error_message("$color: \"foo\" is not a color for `darken'", "darken(\"foo\", 10%)")
443
+ assert_error_message("$amount: \"foo\" is not a number for `darken'", "darken(#fff, \"foo\")")
444
+ end
445
+
446
+ def test_saturate
447
+ assert_equal("#d9f2d9", evaluate("saturate(hsl(120, 30, 90), 20%)"))
448
+ assert_equal("#9e3f3f", evaluate("saturate(#855, 20%)"))
449
+ assert_equal("black", evaluate("saturate(#000, 20%)"))
450
+ assert_equal("white", evaluate("saturate(#fff, 20%)"))
451
+ assert_equal("#33ff33", evaluate("saturate(#8a8, 100%)"))
452
+ assert_equal("#88aa88", evaluate("saturate(#8a8, 0%)"))
453
+ assert_equal("rgba(158, 63, 63, 0.5)", evaluate("saturate(rgba(136, 85, 85, 0.5), 20%)"))
454
+ assert_equal("rgba(158, 63, 63, 0.5)", evaluate("saturate($color: rgba(136, 85, 85, 0.5), $amount: 20%)"))
455
+ assert_equal("saturate(50%)", evaluate("saturate(50%)"))
456
+ end
457
+
458
+ def test_saturate_tests_bounds
459
+ assert_error_message("Amount -0.001 must be between 0% and 100% for `saturate'",
460
+ "saturate(#123, -0.001)")
461
+ assert_error_message("Amount 100.001 must be between 0% and 100% for `saturate'",
462
+ "saturate(#123, 100.001)")
463
+ end
464
+
465
+ def test_saturate_tests_types
466
+ assert_error_message("$color: \"foo\" is not a color for `saturate'", "saturate(\"foo\", 10%)")
467
+ assert_error_message("$amount: \"foo\" is not a number for `saturate'", "saturate(#fff, \"foo\")")
468
+ end
469
+
470
+ def test_desaturate
471
+ assert_equal("#e3e8e3", evaluate("desaturate(hsl(120, 30, 90), 20%)"))
472
+ assert_equal("#726b6b", evaluate("desaturate(#855, 20%)"))
473
+ assert_equal("black", evaluate("desaturate(#000, 20%)"))
474
+ assert_equal("white", evaluate("desaturate(#fff, 20%)"))
475
+ assert_equal("#999999", evaluate("desaturate(#8a8, 100%)"))
476
+ assert_equal("#88aa88", evaluate("desaturate(#8a8, 0%)"))
477
+ assert_equal("rgba(114, 107, 107, 0.5)", evaluate("desaturate(rgba(136, 85, 85, 0.5), 20%)"))
478
+ assert_equal("rgba(114, 107, 107, 0.5)", evaluate("desaturate($color: rgba(136, 85, 85, 0.5), $amount: 20%)"))
479
+ end
480
+
481
+ def test_desaturate_tests_bounds
482
+ assert_error_message("Amount -0.001 must be between 0% and 100% for `desaturate'",
483
+ "desaturate(#123, -0.001)")
484
+ assert_error_message("Amount 100.001 must be between 0% and 100% for `desaturate'",
485
+ "desaturate(#123, 100.001)")
486
+ end
487
+
488
+ def test_desaturate_tests_types
489
+ assert_error_message("$color: \"foo\" is not a color for `desaturate'", "desaturate(\"foo\", 10%)")
490
+ assert_error_message("$amount: \"foo\" is not a number for `desaturate'", "desaturate(#fff, \"foo\")")
491
+ end
492
+
493
+ def test_adjust_hue
494
+ assert_equal("#deeded", evaluate("adjust-hue(hsl(120, 30, 90), 60deg)"))
495
+ assert_equal("#ededde", evaluate("adjust-hue(hsl(120, 30, 90), -60deg)"))
496
+ assert_equal("#886a11", evaluate("adjust-hue(#811, 45deg)"))
497
+ assert_equal("black", evaluate("adjust-hue(#000, 45deg)"))
498
+ assert_equal("white", evaluate("adjust-hue(#fff, 45deg)"))
499
+ assert_equal("#88aa88", evaluate("adjust-hue(#8a8, 360deg)"))
500
+ assert_equal("#88aa88", evaluate("adjust-hue(#8a8, 0deg)"))
501
+ assert_equal("rgba(136, 106, 17, 0.5)", evaluate("adjust-hue(rgba(136, 17, 17, 0.5), 45deg)"))
502
+ assert_equal("rgba(136, 106, 17, 0.5)", evaluate("adjust-hue($color: rgba(136, 17, 17, 0.5), $degrees: 45deg)"))
503
+ end
504
+
505
+ def test_adjust_hue_tests_types
506
+ assert_error_message("$color: \"foo\" is not a color for `adjust-hue'", "adjust-hue(\"foo\", 10%)")
507
+ assert_error_message("$degrees: \"foo\" is not a number for `adjust-hue'", "adjust-hue(#fff, \"foo\")")
508
+ end
509
+
510
+ def test_adjust_color
511
+ # HSL
512
+ assert_equal(evaluate("hsl(180, 30, 90)"),
513
+ evaluate("adjust-color(hsl(120, 30, 90), $hue: 60deg)"))
514
+ assert_equal(evaluate("hsl(120, 50, 90)"),
515
+ evaluate("adjust-color(hsl(120, 30, 90), $saturation: 20%)"))
516
+ assert_equal(evaluate("hsl(120, 30, 60)"),
517
+ evaluate("adjust-color(hsl(120, 30, 90), $lightness: -30%)"))
518
+ # RGB
519
+ assert_equal(evaluate("rgb(15, 20, 30)"),
520
+ evaluate("adjust-color(rgb(10, 20, 30), $red: 5)"))
521
+ assert_equal(evaluate("rgb(10, 15, 30)"),
522
+ evaluate("adjust-color(rgb(10, 20, 30), $green: -5)"))
523
+ assert_equal(evaluate("rgb(10, 20, 40)"),
524
+ evaluate("adjust-color(rgb(10, 20, 30), $blue: 10)"))
525
+ # Alpha
526
+ assert_equal(evaluate("hsla(120, 30, 90, 0.65)"),
527
+ evaluate("adjust-color(hsl(120, 30, 90), $alpha: -0.35)"))
528
+ assert_equal(evaluate("rgba(10, 20, 30, 0.9)"),
529
+ evaluate("adjust-color(rgba(10, 20, 30, 0.4), $alpha: 0.5)"))
530
+
531
+ # HSL composability
532
+ assert_equal(evaluate("hsl(180, 20, 90)"),
533
+ evaluate("adjust-color(hsl(120, 30, 90), $hue: 60deg, $saturation: -10%)"))
534
+ assert_equal(evaluate("hsl(180, 20, 95)"),
535
+ evaluate("adjust-color(hsl(120, 30, 90), $hue: 60deg, $saturation: -10%, $lightness: 5%)"))
536
+ assert_equal(evaluate("hsla(120, 20, 95, 0.3)"),
537
+ evaluate("adjust-color(hsl(120, 30, 90), $saturation: -10%, $lightness: 5%, $alpha: -0.7)"))
538
+
539
+ # RGB composability
540
+ assert_equal(evaluate("rgb(15, 20, 29)"),
541
+ evaluate("adjust-color(rgb(10, 20, 30), $red: 5, $blue: -1)"))
542
+ assert_equal(evaluate("rgb(15, 45, 29)"),
543
+ evaluate("adjust-color(rgb(10, 20, 30), $red: 5, $green: 25, $blue: -1)"))
544
+ assert_equal(evaluate("rgba(10, 25, 29, 0.7)"),
545
+ evaluate("adjust-color(rgb(10, 20, 30), $green: 5, $blue: -1, $alpha: -0.3)"))
546
+
547
+ # HSL range restriction
548
+ assert_equal(evaluate("hsl(120, 30, 90)"),
549
+ evaluate("adjust-color(hsl(120, 30, 90), $hue: 720deg)"))
550
+ assert_equal(evaluate("hsl(120, 0, 90)"),
551
+ evaluate("adjust-color(hsl(120, 30, 90), $saturation: -90%)"))
552
+ assert_equal(evaluate("hsl(120, 30, 100)"),
553
+ evaluate("adjust-color(hsl(120, 30, 90), $lightness: 30%)"))
554
+
555
+ # RGB range restriction
556
+ assert_equal(evaluate("rgb(255, 20, 30)"),
557
+ evaluate("adjust-color(rgb(10, 20, 30), $red: 250)"))
558
+ assert_equal(evaluate("rgb(10, 0, 30)"),
559
+ evaluate("adjust-color(rgb(10, 20, 30), $green: -30)"))
560
+ assert_equal(evaluate("rgb(10, 20, 0)"),
561
+ evaluate("adjust-color(rgb(10, 20, 30), $blue: -40)"))
562
+ end
563
+
564
+ def test_adjust_color_tests_types
565
+ assert_error_message("$color: \"foo\" is not a color for `adjust-color'", "adjust-color(foo, $hue: 10)")
566
+ # HSL
567
+ assert_error_message("$hue: \"foo\" is not a number for `adjust-color'",
568
+ "adjust-color(blue, $hue: foo)")
569
+ assert_error_message("$saturation: \"foo\" is not a number for `adjust-color'",
570
+ "adjust-color(blue, $saturation: foo)")
571
+ assert_error_message("$lightness: \"foo\" is not a number for `adjust-color'",
572
+ "adjust-color(blue, $lightness: foo)")
573
+ # RGB
574
+ assert_error_message("$red: \"foo\" is not a number for `adjust-color'",
575
+ "adjust-color(blue, $red: foo)")
576
+ assert_error_message("$green: \"foo\" is not a number for `adjust-color'",
577
+ "adjust-color(blue, $green: foo)")
578
+ assert_error_message("$blue: \"foo\" is not a number for `adjust-color'",
579
+ "adjust-color(blue, $blue: foo)")
580
+ # Alpha
581
+ assert_error_message("$alpha: \"foo\" is not a number for `adjust-color'",
582
+ "adjust-color(blue, $alpha: foo)")
583
+ end
584
+
585
+ def test_adjust_color_tests_arg_range
586
+ # HSL
587
+ assert_error_message("$saturation: Amount 101% must be between -100% and 100% for `adjust-color'",
588
+ "adjust-color(blue, $saturation: 101%)")
589
+ assert_error_message("$saturation: Amount -101% must be between -100% and 100% for `adjust-color'",
590
+ "adjust-color(blue, $saturation: -101%)")
591
+ assert_error_message("$lightness: Amount 101% must be between -100% and 100% for `adjust-color'",
592
+ "adjust-color(blue, $lightness: 101%)")
593
+ assert_error_message("$lightness: Amount -101% must be between -100% and 100% for `adjust-color'",
594
+ "adjust-color(blue, $lightness: -101%)")
595
+ # RGB
596
+ assert_error_message("$red: Amount 256 must be between -255 and 255 for `adjust-color'",
597
+ "adjust-color(blue, $red: 256)")
598
+ assert_error_message("$red: Amount -256 must be between -255 and 255 for `adjust-color'",
599
+ "adjust-color(blue, $red: -256)")
600
+ assert_error_message("$green: Amount 256 must be between -255 and 255 for `adjust-color'",
601
+ "adjust-color(blue, $green: 256)")
602
+ assert_error_message("$green: Amount -256 must be between -255 and 255 for `adjust-color'",
603
+ "adjust-color(blue, $green: -256)")
604
+ assert_error_message("$blue: Amount 256 must be between -255 and 255 for `adjust-color'",
605
+ "adjust-color(blue, $blue: 256)")
606
+ assert_error_message("$blue: Amount -256 must be between -255 and 255 for `adjust-color'",
607
+ "adjust-color(blue, $blue: -256)")
608
+ # Alpha
609
+ assert_error_message("$alpha: Amount 1.1 must be between -1 and 1 for `adjust-color'",
610
+ "adjust-color(blue, $alpha: 1.1)")
611
+ assert_error_message("$alpha: Amount -1.1 must be between -1 and 1 for `adjust-color'",
612
+ "adjust-color(blue, $alpha: -1.1)")
613
+ end
614
+
615
+ def test_adjust_color_argument_errors
616
+ assert_error_message("Unknown argument $hoo (260deg) for `adjust-color'",
617
+ "adjust-color(blue, $hoo: 260deg)")
618
+ assert_error_message("Cannot specify HSL and RGB values for a color at the same time for `adjust-color'",
619
+ "adjust-color(blue, $hue: 120deg, $red: 10)");
620
+ assert_error_message("10px is not a keyword argument for `adjust_color'",
621
+ "adjust-color(blue, 10px)")
622
+ assert_error_message("10px is not a keyword argument for `adjust_color'",
623
+ "adjust-color(blue, 10px, 20px)")
624
+ assert_error_message("10px is not a keyword argument for `adjust_color'",
625
+ "adjust-color(blue, 10px, $hue: 180deg)")
626
+ end
627
+
628
+ def test_scale_color
629
+ # HSL
630
+ assert_equal(evaluate("hsl(120, 51, 90)"),
631
+ evaluate("scale-color(hsl(120, 30, 90), $saturation: 30%)"))
632
+ assert_equal(evaluate("hsl(120, 30, 76.5)"),
633
+ evaluate("scale-color(hsl(120, 30, 90), $lightness: -15%)"))
634
+ # RGB
635
+ assert_equal(evaluate("rgb(157, 20, 30)"),
636
+ evaluate("scale-color(rgb(10, 20, 30), $red: 60%)"))
637
+ assert_equal(evaluate("rgb(10, 38.8, 30)"),
638
+ evaluate("scale-color(rgb(10, 20, 30), $green: 8%)"))
639
+ assert_equal(evaluate("rgb(10, 20, 20)"),
640
+ evaluate("scale-color(rgb(10, 20, 30), $blue: -(1/3)*100%)"))
641
+ # Alpha
642
+ assert_equal(evaluate("hsla(120, 30, 90, 0.86)"),
643
+ evaluate("scale-color(hsl(120, 30, 90), $alpha: -14%)"))
644
+ assert_equal(evaluate("rgba(10, 20, 30, 0.82)"),
645
+ evaluate("scale-color(rgba(10, 20, 30, 0.8), $alpha: 10%)"))
646
+
647
+ # HSL composability
648
+ assert_equal(evaluate("hsl(120, 51, 76.5)"),
649
+ evaluate("scale-color(hsl(120, 30, 90), $saturation: 30%, $lightness: -15%)"))
650
+ assert_equal(evaluate("hsla(120, 51, 90, 0.2)"),
651
+ evaluate("scale-color(hsl(120, 30, 90), $saturation: 30%, $alpha: -80%)"))
652
+
653
+ # RGB composability
654
+ assert_equal(evaluate("rgb(157, 38.8, 30)"),
655
+ evaluate("scale-color(rgb(10, 20, 30), $red: 60%, $green: 8%)"))
656
+ assert_equal(evaluate("rgb(157, 38.8, 20)"),
657
+ evaluate("scale-color(rgb(10, 20, 30), $red: 60%, $green: 8%, $blue: -(1/3)*100%)"))
658
+ assert_equal(evaluate("rgba(10, 38.8, 20, 0.55)"),
659
+ evaluate("scale-color(rgba(10, 20, 30, 0.5), $green: 8%, $blue: -(1/3)*100%, $alpha: 10%)"))
660
+
661
+ # Extremes
662
+ assert_equal(evaluate("hsl(120, 100, 90)"),
663
+ evaluate("scale-color(hsl(120, 30, 90), $saturation: 100%)"))
664
+ assert_equal(evaluate("hsl(120, 30, 90)"),
665
+ evaluate("scale-color(hsl(120, 30, 90), $saturation: 0%)"))
666
+ assert_equal(evaluate("hsl(120, 0, 90)"),
667
+ evaluate("scale-color(hsl(120, 30, 90), $saturation: -100%)"))
668
+ end
669
+
670
+ def test_scale_color_tests_types
671
+ assert_error_message("$color: \"foo\" is not a color for `scale-color'", "scale-color(foo, $red: 10%)")
672
+ # HSL
673
+ assert_error_message("$saturation: \"foo\" is not a number for `scale-color'",
674
+ "scale-color(blue, $saturation: foo)")
675
+ assert_error_message("$lightness: \"foo\" is not a number for `scale-color'",
676
+ "scale-color(blue, $lightness: foo)")
677
+ # RGB
678
+ assert_error_message("$red: \"foo\" is not a number for `scale-color'",
679
+ "scale-color(blue, $red: foo)")
680
+ assert_error_message("$green: \"foo\" is not a number for `scale-color'",
681
+ "scale-color(blue, $green: foo)")
682
+ assert_error_message("$blue: \"foo\" is not a number for `scale-color'",
683
+ "scale-color(blue, $blue: foo)")
684
+ # Alpha
685
+ assert_error_message("$alpha: \"foo\" is not a number for `scale-color'",
686
+ "scale-color(blue, $alpha: foo)")
687
+ end
688
+
689
+ def test_scale_color_argument_errors
690
+ # Range
691
+ assert_error_message("$saturation: Amount 101% must be between -100% and 100% for `scale-color'",
692
+ "scale-color(blue, $saturation: 101%)")
693
+ assert_error_message("$red: Amount -101% must be between -100% and 100% for `scale-color'",
694
+ "scale-color(blue, $red: -101%)")
695
+ assert_error_message("$alpha: Amount -101% must be between -100% and 100% for `scale-color'",
696
+ "scale-color(blue, $alpha: -101%)")
697
+
698
+ # Unit
699
+ assert_error_message("Expected $saturation to have a unit of % but got 80 for `scale-color'",
700
+ "scale-color(blue, $saturation: 80)")
701
+ assert_error_message("Expected $alpha to have a unit of % but got 0.5 for `scale-color'",
702
+ "scale-color(blue, $alpha: 0.5)")
703
+
704
+ # Unknown argument
705
+ assert_error_message("Unknown argument $hue (80%) for `scale-color'", "scale-color(blue, $hue: 80%)")
706
+
707
+ # Non-keyword arg
708
+ assert_error_message("10px is not a keyword argument for `scale_color'", "scale-color(blue, 10px)")
709
+
710
+ # HSL/RGB
711
+ assert_error_message("Cannot specify HSL and RGB values for a color at the same time for `scale-color'",
712
+ "scale-color(blue, $lightness: 10%, $red: 20%)");
713
+ end
714
+
715
+ def test_change_color
716
+ # HSL
717
+ assert_equal(evaluate("hsl(195, 30, 90)"),
718
+ evaluate("change-color(hsl(120, 30, 90), $hue: 195deg)"))
719
+ assert_equal(evaluate("hsl(120, 50, 90)"),
720
+ evaluate("change-color(hsl(120, 30, 90), $saturation: 50%)"))
721
+ assert_equal(evaluate("hsl(120, 30, 40)"),
722
+ evaluate("change-color(hsl(120, 30, 90), $lightness: 40%)"))
723
+ # RGB
724
+ assert_equal(evaluate("rgb(123, 20, 30)"),
725
+ evaluate("change-color(rgb(10, 20, 30), $red: 123)"))
726
+ assert_equal(evaluate("rgb(10, 234, 30)"),
727
+ evaluate("change-color(rgb(10, 20, 30), $green: 234)"))
728
+ assert_equal(evaluate("rgb(10, 20, 198)"),
729
+ evaluate("change-color(rgb(10, 20, 30), $blue: 198)"))
730
+ # Alpha
731
+ assert_equal(evaluate("rgba(10, 20, 30, 0.76)"),
732
+ evaluate("change-color(rgb(10, 20, 30), $alpha: 0.76)"))
733
+
734
+ # HSL composability
735
+ assert_equal(evaluate("hsl(56, 30, 47)"),
736
+ evaluate("change-color(hsl(120, 30, 90), $hue: 56deg, $lightness: 47%)"))
737
+ assert_equal(evaluate("hsla(56, 30, 47, 0.9)"),
738
+ evaluate("change-color(hsl(120, 30, 90), $hue: 56deg, $lightness: 47%, $alpha: 0.9)"))
739
+ end
740
+
741
+ def test_change_color_tests_types
742
+ assert_error_message("$color: \"foo\" is not a color for `change-color'", "change-color(foo, $red: 10%)")
743
+ # HSL
744
+ assert_error_message("$saturation: \"foo\" is not a number for `change-color'",
745
+ "change-color(blue, $saturation: foo)")
746
+ assert_error_message("$lightness: \"foo\" is not a number for `change-color'",
747
+ "change-color(blue, $lightness: foo)")
748
+ # RGB
749
+ assert_error_message("$red: \"foo\" is not a number for `change-color'", "change-color(blue, $red: foo)")
750
+ assert_error_message("$green: \"foo\" is not a number for `change-color'", "change-color(blue, $green: foo)")
751
+ assert_error_message("$blue: \"foo\" is not a number for `change-color'", "change-color(blue, $blue: foo)")
752
+ # Alpha
753
+ assert_error_message("$alpha: \"foo\" is not a number for `change-color'", "change-color(blue, $alpha: foo)")
754
+ end
755
+
756
+ def test_change_color_argument_errors
757
+ # Range
758
+ assert_error_message("Saturation 101% must be between 0% and 100% for `change-color'",
759
+ "change-color(blue, $saturation: 101%)")
760
+ assert_error_message("Lightness 101% must be between 0% and 100% for `change-color'",
761
+ "change-color(blue, $lightness: 101%)")
762
+ assert_error_message("Red value -1 must be between 0 and 255 for `change-color'",
763
+ "change-color(blue, $red: -1)")
764
+ assert_error_message("Green value 256 must be between 0 and 255 for `change-color'",
765
+ "change-color(blue, $green: 256)")
766
+ assert_error_message("Blue value 500 must be between 0 and 255 for `change-color'",
767
+ "change-color(blue, $blue: 500)")
768
+
769
+ # Unknown argument
770
+ assert_error_message("Unknown argument $hoo (80%) for `change-color'", "change-color(blue, $hoo: 80%)")
771
+
772
+ # Non-keyword arg
773
+ assert_error_message("10px is not a keyword argument for `change_color'", "change-color(blue, 10px)")
774
+
775
+ # HSL/RGB
776
+ assert_error_message("Cannot specify HSL and RGB values for a color at the same time for `change-color'",
777
+ "change-color(blue, $lightness: 10%, $red: 120)");
778
+ end
779
+
780
+ def test_ie_hex_str
781
+ assert_equal("#FFAA11CC", evaluate('ie-hex-str(#aa11cc)'))
782
+ assert_equal("#FFAA11CC", evaluate('ie-hex-str(#a1c)'))
783
+ assert_equal("#FFAA11CC", evaluate('ie-hex-str(#A1c)'))
784
+ assert_equal("#80FF0000", evaluate('ie-hex-str(rgba(255, 0, 0, 0.5))'))
785
+ end
786
+
787
+ def test_mix
788
+ assert_equal("#7f007f", evaluate("mix(#f00, #00f)"))
789
+ assert_equal("#7f7f7f", evaluate("mix(#f00, #0ff)"))
790
+ assert_equal("#7f9055", evaluate("mix(#f70, #0aa)"))
791
+ assert_equal("#3f00bf", evaluate("mix(#f00, #00f, 25%)"))
792
+ assert_equal("rgba(63, 0, 191, 0.75)", evaluate("mix(rgba(255, 0, 0, 0.5), #00f)"))
793
+ assert_equal("red", evaluate("mix(#f00, #00f, 100%)"))
794
+ assert_equal("blue", evaluate("mix(#f00, #00f, 0%)"))
795
+ assert_equal("rgba(255, 0, 0, 0.5)", evaluate("mix(#f00, transparentize(#00f, 1))"))
796
+ assert_equal("rgba(0, 0, 255, 0.5)", evaluate("mix(transparentize(#f00, 1), #00f)"))
797
+ assert_equal("red", evaluate("mix(#f00, transparentize(#00f, 1), 100%)"))
798
+ assert_equal("blue", evaluate("mix(transparentize(#f00, 1), #00f, 0%)"))
799
+ assert_equal("rgba(0, 0, 255, 0)", evaluate("mix(#f00, transparentize(#00f, 1), 0%)"))
800
+ assert_equal("rgba(255, 0, 0, 0)", evaluate("mix(transparentize(#f00, 1), #00f, 100%)"))
801
+ assert_equal("rgba(255, 0, 0, 0)", evaluate("mix($color1: transparentize(#f00, 1), $color2: #00f, $weight: 100%)"))
802
+ end
803
+
804
+ def test_mix_tests_types
805
+ assert_error_message("$color1: \"foo\" is not a color for `mix'", "mix(\"foo\", #f00, 10%)")
806
+ assert_error_message("$color2: \"foo\" is not a color for `mix'", "mix(#f00, \"foo\", 10%)")
807
+ assert_error_message("$weight: \"foo\" is not a number for `mix'", "mix(#f00, #baf, \"foo\")")
808
+ end
809
+
810
+ def test_mix_tests_bounds
811
+ assert_error_message("Weight -0.001 must be between 0% and 100% for `mix'",
812
+ "mix(#123, #456, -0.001)")
813
+ assert_error_message("Weight 100.001 must be between 0% and 100% for `mix'",
814
+ "mix(#123, #456, 100.001)")
815
+ end
816
+
817
+ def test_grayscale
818
+ assert_equal("#bbbbbb", evaluate("grayscale(#abc)"))
819
+ assert_equal("gray", evaluate("grayscale(#f00)"))
820
+ assert_equal("gray", evaluate("grayscale(#00f)"))
821
+ assert_equal("white", evaluate("grayscale(white)"))
822
+ assert_equal("black", evaluate("grayscale(black)"))
823
+ assert_equal("black", evaluate("grayscale($color: black)"))
824
+
825
+ assert_equal("grayscale(2)", evaluate("grayscale(2)"))
826
+ assert_equal("grayscale(-5px)", evaluate("grayscale(-5px)"))
827
+ end
828
+
829
+ def tets_grayscale_tests_types
830
+ assert_error_message("$color: \"foo\" is not a color for `grayscale'", "grayscale(\"foo\")")
831
+ end
832
+
833
+ def test_complement
834
+ assert_equal("#ccbbaa", evaluate("complement(#abc)"))
835
+ assert_equal("cyan", evaluate("complement(red)"))
836
+ assert_equal("red", evaluate("complement(cyan)"))
837
+ assert_equal("white", evaluate("complement(white)"))
838
+ assert_equal("black", evaluate("complement(black)"))
839
+ assert_equal("black", evaluate("complement($color: black)"))
840
+ end
841
+
842
+ def tets_complement_tests_types
843
+ assert_error_message("$color: \"foo\" is not a color for `complement'", "complement(\"foo\")")
844
+ end
845
+
846
+ def test_invert
847
+ assert_equal("#112233", evaluate("invert(#edc)"))
848
+ assert_equal("rgba(245, 235, 225, 0.5)", evaluate("invert(rgba(10, 20, 30, 0.5))"))
849
+ assert_equal("invert(20%)", evaluate("invert(20%)"))
850
+ end
851
+
852
+ def test_invert_tests_types
853
+ assert_error_message("$color: \"foo\" is not a color for `invert'", "invert(\"foo\")")
854
+ end
855
+
856
+ def test_unquote
857
+ assert_equal('foo', evaluate('unquote("foo")'))
858
+ assert_equal('foo', evaluate('unquote(foo)'))
859
+ assert_equal('foo', evaluate('unquote($string: foo)'))
860
+ end
861
+
862
+ def test_quote
863
+ assert_equal('"foo"', evaluate('quote(foo)'))
864
+ assert_equal('"foo"', evaluate('quote("foo")'))
865
+ assert_equal('"foo"', evaluate('quote($string: "foo")'))
866
+ end
867
+
868
+ def test_quote_tests_type
869
+ assert_error_message("$string: #ff0000 is not a string for `quote'", "quote(#f00)")
870
+ end
871
+
872
+ def test_str_length
873
+ assert_equal('3', evaluate('str-length(foo)'))
874
+ end
875
+
876
+ def test_str_length_requires_a_string
877
+ assert_error_message("$string: #ff0000 is not a string for `str-length'", "str-length(#f00)")
878
+ end
879
+
880
+ def test_str_insert
881
+ assert_equal('Xabcd', evaluate('str-insert(abcd, X, 0)'))
882
+ assert_equal('Xabcd', evaluate('str-insert(abcd, X, 1)'))
883
+ assert_equal('abcXd', evaluate('str-insert(abcd, X, 4)'))
884
+ assert_equal('abcdX', evaluate('str-insert(abcd, X, 100)'))
885
+ assert_equal('Xabcd', evaluate('str-insert(abcd, X, -100)'))
886
+ assert_equal('aXbcd', evaluate('str-insert(abcd, X, -4)'))
887
+ assert_equal('abcdX', evaluate('str-insert(abcd, X, -1)'))
888
+ end
889
+
890
+ def test_str_insert_maintains_quote_of_primary_string
891
+ assert_equal('"Xfoo"', evaluate('str-insert("foo", X, 1)'))
892
+ assert_equal('"Xfoo"', evaluate('str-insert("foo", "X", 1)'))
893
+ assert_equal('Xfoo', evaluate('str-insert(foo, "X", 1)'))
894
+ end
895
+
896
+ def test_str_insert_asserts_types
897
+ assert_error_message("$string: #ff0000 is not a string for `str-insert'", "str-insert(#f00, X, 1)")
898
+ assert_error_message("$insert: #ff0000 is not a string for `str-insert'", "str-insert(foo, #f00, 1)")
899
+ assert_error_message("$index: #ff0000 is not a number for `str-insert'", "str-insert(foo, X, #f00)")
900
+ assert_error_message("Expected $index to be unitless but got 10px for `str-insert'", "str-insert(foo, X, 10px)")
901
+ end
902
+
903
+ def test_str_index
904
+ assert_equal('1', evaluate('str-index(abcd, a)'))
905
+ assert_equal('1', evaluate('str-index(abcd, ab)'))
906
+ assert_equal(Sass::Script::Value::Null.new, perform('str-index(abcd, X)'))
907
+ assert_equal('3', evaluate('str-index(abcd, c)'))
908
+ end
909
+
910
+ def test_str_index_asserts_types
911
+ assert_error_message("$string: #ff0000 is not a string for `str-index'", "str-index(#f00, X)")
912
+ assert_error_message("$substring: #ff0000 is not a string for `str-index'", "str-index(asdf, #f00)")
913
+ end
914
+
915
+ def test_to_lower_case
916
+ assert_equal('abcd', evaluate('to-lower-case(ABCD)'))
917
+ assert_equal('"abcd"', evaluate('to-lower-case("ABCD")'))
918
+ assert_error_message("$string: #ff0000 is not a string for `to-lower-case'", "to-lower-case(#f00)")
919
+ end
920
+
921
+ def test_to_upper_case
922
+ assert_equal('ABCD', evaluate('to-upper-case(abcd)'))
923
+ assert_equal('"ABCD"', evaluate('to-upper-case("abcd")'))
924
+ assert_error_message("$string: #ff0000 is not a string for `to-upper-case'", "to-upper-case(#f00)")
925
+ end
926
+
927
+ def test_str_slice
928
+ assert_equal('bc', evaluate('str-slice(abcd,2,3)')) # in the middle of the string
929
+ assert_equal('a', evaluate('str-slice(abcd,1,1)')) # when start = end
930
+ assert_equal('ab', evaluate('str-slice(abcd,1,2)')) # for completeness
931
+ assert_equal('abcd', evaluate('str-slice(abcd,1,4)')) # at the end points
932
+ assert_equal('abcd', evaluate('str-slice(abcd,0,4)')) # when start is before the start of the string
933
+ assert_equal('', evaluate('str-slice(abcd,1,0)')) # when end is before the start of the string
934
+ assert_equal('abcd', evaluate('str-slice(abcd,1,100)')) # when end is past the end of the string
935
+ assert_equal('', evaluate('str-slice(abcd,2,1)')) # when end is before start
936
+ assert_equal('"bc"', evaluate('str-slice("abcd",2,3)')) # when used with a quoted string
937
+ assert_equal('bcd', evaluate('str-slice(abcd,2)')) # when end is omitted, you get the remainder of the string
938
+ assert_equal('cd', evaluate('str-slice(abcd,-2)')) # when start is negative, it counts from the beginning
939
+ assert_equal('bc', evaluate('str-slice(abcd,2,-2)')) # when end is negative it counts in from the end
940
+ assert_equal('', evaluate('str-slice(abcd,3,-3)')) # when end is negative and comes before the start
941
+ assert_equal('bc', evaluate('str-slice(abcd,-3,-2)')) # when both are negative
942
+ assert_error_message("$string: #ff0000 is not a string for `str-slice'", "str-slice(#f00,2,3)")
943
+ assert_error_message("$start-at: #ff0000 is not a number for `str-slice'", "str-slice(abcd,#f00,3)")
944
+ assert_error_message("$end-at: #ff0000 is not a number for `str-slice'", "str-slice(abcd,2,#f00)")
945
+ assert_error_message("Expected $end-at to be unitless but got 3px for `str-slice'", "str-slice(abcd,2,3px)")
946
+ assert_error_message("Expected $start-at to be unitless but got 2px for `str-slice'", "str-slice(abcd,2px,3)")
947
+ end
948
+
949
+ def test_user_defined_function
950
+ assert_equal("I'm a user-defined string!", evaluate("user_defined()"))
951
+ end
952
+
953
+ def test_user_defined_function_with_preceding_underscore
954
+ assert_equal("I'm another user-defined string!", evaluate("_preceding_underscore()"))
955
+ assert_equal("I'm another user-defined string!", evaluate("-preceding-underscore()"))
956
+ end
957
+
958
+ def test_user_defined_function_using_environment
959
+ environment = env('variable' => Sass::Script::Value::String.new('The variable'))
960
+ assert_equal("The variable", evaluate("fetch_the_variable()", environment))
961
+ end
962
+
963
+ def test_options_on_new_values_fails
964
+ assert_error_message(<<MSG, "call-options-on-new-value()")
965
+ The #options attribute is not set on this Sass::Script::Value::String.
966
+ This error is probably occurring because #to_s was called
967
+ on this value within a custom Sass function without first
968
+ setting the #options attribute.
969
+ MSG
970
+ end
971
+
972
+ def test_type_of
973
+ assert_equal("string", evaluate("type-of(\"asdf\")"))
974
+ assert_equal("string", evaluate("type-of(asdf)"))
975
+ assert_equal("number", evaluate("type-of(1px)"))
976
+ assert_equal("bool", evaluate("type-of(true)"))
977
+ assert_equal("color", evaluate("type-of(#fff)"))
978
+ assert_equal("color", evaluate("type-of($value: #fff)"))
979
+ assert_equal("null", evaluate("type-of(null)"))
980
+ assert_equal("list", evaluate("type-of(1 2 3)"))
981
+ assert_equal("list", evaluate("type-of((1, 2, 3))"))
982
+ assert_equal("list", evaluate("type-of(())"))
983
+ assert_equal("map", evaluate("type-of((foo: bar))"))
984
+ end
985
+
986
+ def test_feature_exists
987
+ assert_raises ArgumentError do
988
+ Sass.add_feature("my-test-feature")
989
+ end
990
+ Sass.add_feature("-my-test-feature")
991
+ assert_equal("true", evaluate("feature-exists(-my-test-feature)"))
992
+ assert_equal("false", evaluate("feature-exists(whatisthisidontevenknow)"))
993
+ assert_equal("true", evaluate("feature-exists($feature: -my-test-feature)"))
994
+ ensure
995
+ Sass::Features::KNOWN_FEATURES.delete("-my-test-feature")
996
+ end
997
+
998
+ def test_unit
999
+ assert_equal(%Q{""}, evaluate("unit(100)"))
1000
+ assert_equal(%Q{"px"}, evaluate("unit(100px)"))
1001
+ assert_equal(%Q{"em*px"}, evaluate("unit(10px * 5em)"))
1002
+ assert_equal(%Q{"em*px"}, evaluate("unit(5em * 10px)"))
1003
+ assert_equal(%Q{"em/rem"}, evaluate("unit(10px * 5em / 30cm / 1rem)"))
1004
+ assert_equal(%Q{"em*vh/cm*rem"}, evaluate("unit(10vh * 5em / 30cm / 1rem)"))
1005
+ assert_equal(%Q{"px"}, evaluate("unit($number: 100px)"))
1006
+ assert_error_message("$number: #ff0000 is not a number for `unit'", "unit(#f00)")
1007
+ end
1008
+
1009
+ def test_unitless
1010
+ assert_equal(%Q{true}, evaluate("unitless(100)"))
1011
+ assert_equal(%Q{false}, evaluate("unitless(100px)"))
1012
+ assert_equal(%Q{false}, evaluate("unitless($number: 100px)"))
1013
+ assert_error_message("$number: #ff0000 is not a number for `unitless'", "unitless(#f00)")
1014
+ end
1015
+
1016
+ def test_comparable
1017
+ assert_equal(%Q{true}, evaluate("comparable(2px, 1px)"))
1018
+ assert_equal(%Q{true}, evaluate("comparable(10cm, 3mm)"))
1019
+ assert_equal(%Q{false}, evaluate("comparable(100px, 3em)"))
1020
+ assert_equal(%Q{false}, evaluate("comparable($number1: 100px, $number2: 3em)"))
1021
+ end
1022
+
1023
+ def test_comparable_checks_types
1024
+ assert_error_message("$number1: #ff0000 is not a number for `comparable'", "comparable(#f00, 1px)")
1025
+ assert_error_message("$number2: #ff0000 is not a number for `comparable'", "comparable(1px, #f00)")
1026
+ end
1027
+
1028
+ def test_length
1029
+ assert_equal("5", evaluate("length(1 2 3 4 5)"))
1030
+ assert_equal("4", evaluate("length((foo, bar, baz, bip))"))
1031
+ assert_equal("3", evaluate("length((foo, bar, baz bip))"))
1032
+ assert_equal("3", evaluate("length((foo, bar, (baz, bip)))"))
1033
+ assert_equal("1", evaluate("length(#f00)"))
1034
+ assert_equal("0", evaluate("length(())"))
1035
+ assert_equal("4", evaluate("length(1 2 () 3)"))
1036
+
1037
+ assert_equal("2", evaluate("length((foo: bar, bar: baz))"))
1038
+ end
1039
+
1040
+ def test_nth
1041
+ assert_equal("1", evaluate("nth(1 2 3, 1)"))
1042
+ assert_equal("2", evaluate("nth(1 2 3, 2)"))
1043
+ assert_equal("3", evaluate("nth(1 2 3, -1)"))
1044
+ assert_equal("1", evaluate("nth(1 2 3, -3)"))
1045
+ assert_equal("3", evaluate("nth((1, 2, 3), 3)"))
1046
+ assert_equal("3", evaluate("nth($list: (1, 2, 3), $n: 3)"))
1047
+ assert_equal("foo", evaluate("nth(foo, 1)"))
1048
+ assert_equal("bar baz", evaluate("nth(foo (bar baz) bang, 2)"))
1049
+ assert_error_message("List index 0 must be a non-zero integer for `nth'", "nth(foo, 0)")
1050
+ assert_error_message("List index is -10 but list is only 1 item long for `nth'", "nth(foo, -10)")
1051
+ assert_error_message("List index 1.5 must be a non-zero integer for `nth'", "nth(foo, 1.5)")
1052
+ assert_error_message("List index is 5 but list is only 4 items long for `nth'", "nth(1 2 3 4, 5)")
1053
+ assert_error_message("List index is 2 but list is only 1 item long for `nth'", "nth(foo, 2)")
1054
+ assert_error_message("List index is 1 but list has no items for `nth'", "nth((), 1)")
1055
+ assert_error_message("$n: \"foo\" is not a number for `nth'", "nth(1 2 3, foo)")
1056
+
1057
+ assert_equal("foo bar", evaluate("nth((foo: bar, bar: baz), 1)"))
1058
+ assert_equal("bar baz", evaluate("nth((foo: bar, bar: baz), 2)"))
1059
+ end
1060
+
1061
+ def test_set_nth
1062
+ assert_equal("a 2 3", evaluate("set-nth(1 2 3, 1, a)"))
1063
+ assert_equal("1 a 3", evaluate("set-nth(1 2 3, 2, a)"))
1064
+ assert_equal("1 2 a", evaluate("set-nth(1 2 3, -1, a)"))
1065
+ assert_equal("a 2 3", evaluate("set-nth(1 2 3, -3, a)"))
1066
+ assert_equal("a 2 3", evaluate("set-nth($list: 1 2 3, $n: -3, $value: a)"))
1067
+ assert_equal("1, 2, a", evaluate("set-nth((1, 2, 3), 3, a)"))
1068
+ assert_equal("a", evaluate("set-nth(foo, 1, a)"))
1069
+ assert_equal("foo, a b, baz", evaluate("set-nth((foo, bar, baz), 2, (a b))"))
1070
+ assert_error_message("List index 0 must be a non-zero integer for `set-nth'", "set-nth(foo, 0, a)")
1071
+ assert_error_message("List index is -10 but list is only 1 item long for `set-nth'", "set-nth(foo, -10, a)")
1072
+ assert_error_message("List index 1.5 must be a non-zero integer for `set-nth'", "set-nth(foo, 1.5, a)")
1073
+ assert_error_message("List index is 5 but list is only 4 items long for `set-nth'", "set-nth(1 2 3 4, 5, a)")
1074
+ assert_error_message("List index is 2 but list is only 1 item long for `set-nth'", "set-nth(foo, 2, a)")
1075
+ assert_error_message("List index is 1 but list has no items for `set-nth'", "set-nth((), 1, a)")
1076
+ assert_error_message("$n: \"foo\" is not a number for `set-nth'", "set-nth(1 2 3, foo, a)")
1077
+ end
1078
+
1079
+ def test_join
1080
+ assert_equal("1 2 3", evaluate("join(1 2, 3)"))
1081
+ assert_equal("1 2 3", evaluate("join(1, 2 3)"))
1082
+ assert_equal("1 2 3 4", evaluate("join(1 2, 3 4)"))
1083
+ assert_equal("true", evaluate("(1 2 3 4) == join(1 2, 3 4)"))
1084
+ assert_equal("false", evaluate("(1 2 (3 4)) == join(1 2, 3 4)"))
1085
+ assert_equal("1, 2, 3", evaluate("join((1, 2), 3)"))
1086
+ assert_equal("1, 2, 3", evaluate("join(1, (2, 3))"))
1087
+ assert_equal("1, 2, 3, 4", evaluate("join((1, 2), (3, 4))"))
1088
+ assert_equal("true", evaluate("(1, 2, 3, 4) == join((1, 2), (3, 4))"))
1089
+ assert_equal("false", evaluate("(1, 2, (3, 4)) == join((1, 2), (3, 4))"))
1090
+
1091
+ assert_equal("1 2", evaluate("join(1, 2)"))
1092
+ assert_equal("1 2 3 4", evaluate("join(1 2, (3, 4))"))
1093
+ assert_equal("1, 2, 3, 4", evaluate("join((1, 2), 3 4)"))
1094
+
1095
+ assert_equal("1 2", evaluate("join(1, 2, auto)"))
1096
+ assert_equal("1, 2, 3, 4", evaluate("join(1 2, 3 4, comma)"))
1097
+ assert_equal("1 2 3 4", evaluate("join((1, 2), (3, 4), space)"))
1098
+ assert_equal("1, 2", evaluate("join(1, 2, comma)"))
1099
+
1100
+ assert_equal("1 2", evaluate("join(1 2, ())"))
1101
+ assert_equal("1, 2", evaluate("join((1, 2), ())"))
1102
+ assert_equal("true", evaluate("(1 2) == join(1 2, ())"))
1103
+ assert_equal("true", evaluate("(1, 2) == join((1, 2), ())"))
1104
+ assert_equal("false", evaluate("(1 2 ()) == join(1 2, ())"))
1105
+ assert_equal("false", evaluate("(1, 2, ()) == join((1, 2), ())"))
1106
+
1107
+ assert_equal("1 2", evaluate("join((), 1 2)"))
1108
+ assert_equal("1, 2", evaluate("join((), (1, 2))"))
1109
+ assert_equal("true", evaluate("(1 2) == join((), 1 2)"))
1110
+ assert_equal("true", evaluate("(1, 2) == join((), (1, 2))"))
1111
+ assert_equal("false", evaluate("(1 2 ()) == join((), 1 2)"))
1112
+ assert_equal("false", evaluate("(1, 2, ()) == join((), (1, 2))"))
1113
+
1114
+ assert_error_message("Separator name must be space, comma, or auto for `join'", "join(1, 2, baboon)")
1115
+ assert_error_message("$separator: 12 is not a string for `join'", "join(1, 2, 12)")
1116
+
1117
+ assert_equal("foo bar, bar baz, baz bip, bip bop",
1118
+ perform("join((foo: bar, bar: baz), (baz: bip, bip: bop))").to_sass)
1119
+ assert_equal("(foo bar) (bar baz) (baz bip) (bip bop)",
1120
+ perform("join((foo: bar, bar: baz), (baz: bip, bip: bop), space)").to_sass)
1121
+ assert_equal("foo bar (baz bip) (bip bop)",
1122
+ perform("join(foo bar, (baz: bip, bip: bop))").to_sass)
1123
+ assert_equal("foo bar, bar baz, bip, bop",
1124
+ perform("join((foo: bar, bar: baz), bip bop)").to_sass)
1125
+ assert_equal("baz bip, bip bop",
1126
+ perform("join((), (baz: bip, bip: bop))").to_sass)
1127
+ assert_equal("foo bar, bar baz",
1128
+ perform("join((foo: bar, bar: baz), ())").to_sass)
1129
+ end
1130
+
1131
+ def test_append
1132
+ assert_equal("1 2 3", evaluate("append(1 2, 3)"))
1133
+ assert_equal("1 2 3 4", evaluate("append(1 2, 3 4)"))
1134
+ assert_equal("false", evaluate("(1 2 3 4) == append(1 2, 3 4)"))
1135
+ assert_equal("true", evaluate("(1 2 (3 4)) == append(1 2, 3 4)"))
1136
+ assert_equal("1, 2, 3", evaluate("append((1, 2), 3)"))
1137
+ assert_equal("1, 2, 3, 4", evaluate("append((1, 2), (3, 4))"))
1138
+ assert_equal("false", evaluate("(1, 2, 3, 4) == append((1, 2), (3, 4))"))
1139
+ assert_equal("true", evaluate("(1, 2, (3, 4)) == append((1, 2), (3, 4))"))
1140
+
1141
+ assert_equal("1 2", evaluate("append(1, 2)"))
1142
+ assert_equal("1 2 3, 4", evaluate("append(1 2, (3, 4))"))
1143
+ assert_equal("true", evaluate("(1 2 (3, 4)) == append(1 2, (3, 4))"))
1144
+ assert_equal("1, 2, 3 4", evaluate("append((1, 2), 3 4)"))
1145
+ assert_equal("true", evaluate("(1, 2, 3 4) == append((1, 2), 3 4)"))
1146
+
1147
+ assert_equal("1 2", evaluate("append(1, 2, auto)"))
1148
+ assert_equal("1, 2, 3 4", evaluate("append(1 2, 3 4, comma)"))
1149
+ assert_equal("1 2 3, 4", evaluate("append((1, 2), (3, 4), space)"))
1150
+ assert_equal("1, 2", evaluate("append(1, 2, comma)"))
1151
+
1152
+ assert_equal("1 2", evaluate("append(1 2, ())"))
1153
+ assert_equal("1, 2", evaluate("append((1, 2), ())"))
1154
+ assert_equal("true", evaluate("(1 2 ()) == append(1 2, ())"))
1155
+ assert_equal("true", evaluate("(1, 2, ()) == append((1, 2), ())"))
1156
+
1157
+ assert_equal("1 2", evaluate("append((), 1 2)"))
1158
+ assert_equal("1, 2", evaluate("append((), (1, 2))"))
1159
+ assert_equal("false", evaluate("(1 2) == append((), 1 2)"))
1160
+ assert_equal("true", evaluate("(1 2) == nth(append((), 1 2), 1)"))
1161
+
1162
+ assert_error_message("Separator name must be space, comma, or auto for `append'", "append(1, 2, baboon)")
1163
+ assert_error_message("$separator: 12 is not a string for `append'", "append(1, 2, 12)")
1164
+
1165
+ assert_equal("1 2 (foo: bar)", perform("append(1 2, (foo: bar))").to_sass)
1166
+ assert_equal("foo bar, bar baz, 1", perform("append((foo: bar, bar: baz), 1)").to_sass)
1167
+ assert_equal("foo bar, bar baz, (baz: bip)",
1168
+ perform("append((foo: bar, bar: baz), (baz: bip))").to_sass)
1169
+ end
1170
+
1171
+ def test_zip
1172
+ assert_equal("1 3 5, 2 4 6", evaluate("zip(1 2, 3 4, 5 6)"))
1173
+ assert_equal("1 4 7, 2 5 8", evaluate("zip(1 2 3, 4 5 6, 7 8)"))
1174
+ assert_equal("1 2 3", evaluate("zip(1, 2, 3)"))
1175
+ assert_equal("(foo bar) 1 3, (bar baz) 2 4",
1176
+ perform("zip((foo: bar, bar: baz), 1 2, 3 4)").to_sass)
1177
+ end
1178
+
1179
+ def test_index
1180
+ null = Sass::Script::Value::Null.new
1181
+ assert_equal("1", evaluate("index(1px solid blue, 1px)"))
1182
+ assert_equal("2", evaluate("index(1px solid blue, solid)"))
1183
+ assert_equal("3", evaluate("index(1px solid blue, #00f)"))
1184
+ assert_equal("1", evaluate("index(1px, 1px)"))
1185
+ assert_equal(null, perform("index(1px solid blue, 1em)"))
1186
+ assert_equal(null, perform("index(1px solid blue, notfound)"))
1187
+ assert_equal(null, perform("index(1px, #00f)"))
1188
+
1189
+ assert_equal("1", evaluate("index((foo: bar, bar: baz), (foo bar))"))
1190
+ assert_equal(null, perform("index((foo: bar, bar: baz), (foo: bar))"))
1191
+ end
1192
+
1193
+ def test_list_separator
1194
+ assert_equal("space", evaluate("list-separator(1 2 3 4 5)"))
1195
+ assert_equal("comma", evaluate("list-separator((foo, bar, baz, bip))"))
1196
+ assert_equal("comma", evaluate("list-separator((foo, bar, baz bip))"))
1197
+ assert_equal("comma", evaluate("list-separator((foo, bar, (baz, bip)))"))
1198
+ assert_equal("space", evaluate("list-separator(#f00)"))
1199
+ assert_equal("space", evaluate("list-separator(())"))
1200
+ assert_equal("space", evaluate("list-separator(1 2 () 3)"))
1201
+
1202
+ assert_equal("comma", evaluate("list-separator((foo: bar, bar: baz))"))
1203
+ end
1204
+
1205
+ def test_if
1206
+ assert_equal("1px", evaluate("if(true, 1px, 2px)"))
1207
+ assert_equal("2px", evaluate("if(false, 1px, 2px)"))
1208
+ assert_equal("2px", evaluate("if(null, 1px, 2px)"))
1209
+ assert_equal("1px", evaluate("if(true, 1px, $broken)"))
1210
+ assert_equal("1px", evaluate("if(false, $broken, 1px)"))
1211
+ assert_equal("1px", evaluate("if(false, $if-true: $broken, $if-false: 1px)"))
1212
+ assert_equal("1px", evaluate("if(true, $if-true: 1px, $if-false: $broken)"))
1213
+ assert_equal(<<CSS, render(<<SCSS))
1214
+ .if {
1215
+ result: yay; }
1216
+ CSS
1217
+ .if {
1218
+ $something: yay;
1219
+ result: if(true, $if-true: $something, $if-false: $broken);
1220
+ }
1221
+ SCSS
1222
+ assert_equal(<<CSS, render(<<SCSS))
1223
+ .if {
1224
+ result: 1px; }
1225
+ CSS
1226
+ .if {
1227
+ $splat: 1px, 2px;
1228
+ result: if(true, $splat...);
1229
+ }
1230
+ SCSS
1231
+ end
1232
+
1233
+ def test_counter
1234
+ assert_equal("counter(foo)", evaluate("counter(foo)"))
1235
+ assert_equal('counter(item,".")', evaluate('counter(item, ".")'))
1236
+ assert_equal('counter(item,".")', evaluate('counter(item,".")'))
1237
+ end
1238
+
1239
+ def test_counters
1240
+ assert_equal("counters(foo)", evaluate("counters(foo)"))
1241
+ assert_equal('counters(item,".")', evaluate('counters(item, ".")'))
1242
+ assert_equal('counters(item,".")', evaluate('counters(item,".")'))
1243
+ end
1244
+
1245
+ def test_keyword_args_rgb
1246
+ assert_equal(%Q{white}, evaluate("rgb($red: 255, $green: 255, $blue: 255)"))
1247
+ end
1248
+
1249
+ def test_keyword_args_rgba
1250
+ assert_equal(%Q{rgba(255, 255, 255, 0.5)}, evaluate("rgba($red: 255, $green: 255, $blue: 255, $alpha: 0.5)"))
1251
+ assert_equal(%Q{rgba(255, 255, 255, 0.5)}, evaluate("rgba($color: #fff, $alpha: 0.5)"))
1252
+ end
1253
+
1254
+ def test_keyword_args_rgba_with_extra_args
1255
+ evaluate("rgba($red: 255, $green: 255, $blue: 255, $alpha: 0.5, $extra: error)")
1256
+ flunk("Expected exception")
1257
+ rescue Sass::SyntaxError => e
1258
+ assert_equal("Function rgba doesn't have an argument named $extra", e.message)
1259
+ end
1260
+
1261
+ def test_keyword_args_must_have_signature
1262
+ evaluate("no-kw-args($fake: value)")
1263
+ flunk("Expected exception")
1264
+ rescue Sass::SyntaxError => e
1265
+ assert_equal("Function no_kw_args doesn't support keyword arguments", e.message)
1266
+ end
1267
+
1268
+ def test_keyword_args_with_missing_argument
1269
+ evaluate("rgb($red: 255, $green: 255)")
1270
+ flunk("Expected exception")
1271
+ rescue Sass::SyntaxError => e
1272
+ assert_equal("Function rgb requires an argument named $blue", e.message)
1273
+ end
1274
+
1275
+ def test_keyword_args_with_extra_argument
1276
+ evaluate("rgb($red: 255, $green: 255, $blue: 255, $purple: 255)")
1277
+ flunk("Expected exception")
1278
+ rescue Sass::SyntaxError => e
1279
+ assert_equal("Function rgb doesn't have an argument named $purple", e.message)
1280
+ end
1281
+
1282
+ def test_keyword_args_with_positional_and_keyword_argument
1283
+ evaluate("rgb(255, 255, 255, $red: 255)")
1284
+ flunk("Expected exception")
1285
+ rescue Sass::SyntaxError => e
1286
+ assert_equal("Function rgb was passed argument $red both by position and by name", e.message)
1287
+ end
1288
+
1289
+ def test_keyword_args_with_keyword_before_positional_argument
1290
+ evaluate("rgb($red: 255, 255, 255)")
1291
+ flunk("Expected exception")
1292
+ rescue Sass::SyntaxError => e
1293
+ assert_equal("Positional arguments must come before keyword arguments.", e.message)
1294
+ end
1295
+
1296
+ def test_only_var_args
1297
+ assert_equal "only-var-args(2px, 3px, 4px)", evaluate("only-var-args(1px, 2px, 3px)")
1298
+ end
1299
+
1300
+ def test_only_kw_args
1301
+ assert_equal "only-kw-args(a, b, c)", evaluate("only-kw-args($a: 1, $b: 2, $c: 3)")
1302
+ end
1303
+
1304
+ def test_unique_id
1305
+ last_id, current_id = nil, evaluate("unique-id()")
1306
+
1307
+ 50.times do
1308
+ last_id, current_id = current_id, evaluate("unique-id()")
1309
+ assert_match(/u[a-z0-9]{8}/, current_id)
1310
+ refute_equal last_id, current_id
1311
+ end
1312
+ end
1313
+
1314
+ def test_map_get
1315
+ assert_equal "1", evaluate("map-get((foo: 1, bar: 2), foo)")
1316
+ assert_equal "2", evaluate("map-get((foo: 1, bar: 2), bar)")
1317
+ assert_equal "null", perform("map-get((foo: 1, bar: 2), baz)").to_sass
1318
+ assert_equal "null", perform("map-get((), foo)").to_sass
1319
+ end
1320
+
1321
+ def test_map_get_checks_type
1322
+ assert_error_message("$map: 12 is not a map for `map-get'", "map-get(12, bar)")
1323
+ end
1324
+
1325
+ def test_map_merge
1326
+ assert_equal("(foo: 1, bar: 2, baz: 3)",
1327
+ perform("map-merge((foo: 1, bar: 2), (baz: 3))").to_sass)
1328
+ assert_equal("(foo: 1, bar: 2)",
1329
+ perform("map-merge((), (foo: 1, bar: 2))").to_sass)
1330
+ assert_equal("(foo: 1, bar: 2)",
1331
+ perform("map-merge((foo: 1, bar: 2), ())").to_sass)
1332
+ end
1333
+
1334
+ def test_map_merge_checks_type
1335
+ assert_error_message("$map1: 12 is not a map for `map-merge'", "map-merge(12, (foo: 1))")
1336
+ assert_error_message("$map2: 12 is not a map for `map-merge'", "map-merge((foo: 1), 12)")
1337
+ end
1338
+
1339
+ def test_map_remove
1340
+ assert_equal("(foo: 1, baz: 3)",
1341
+ perform("map-remove((foo: 1, bar: 2, baz: 3), bar)").to_sass)
1342
+ assert_equal("(foo: 1, baz: 3)",
1343
+ perform("map-remove($map: (foo: 1, bar: 2, baz: 3), $key: bar)").to_sass)
1344
+ assert_equal("()",
1345
+ perform("map-remove((foo: 1, bar: 2, baz: 3), foo, bar, baz)").to_sass)
1346
+ assert_equal("()", perform("map-remove((), foo)").to_sass)
1347
+ assert_equal("()", perform("map-remove((), foo, bar)").to_sass)
1348
+ end
1349
+
1350
+ def test_map_remove_checks_type
1351
+ assert_error_message("$map: 12 is not a map for `map-remove'", "map-remove(12, foo)")
1352
+ end
1353
+
1354
+ def test_map_keys
1355
+ assert_equal("foo, bar",
1356
+ perform("map-keys((foo: 1, bar: 2))").to_sass)
1357
+ assert_equal("()", perform("map-keys(())").to_sass)
1358
+ end
1359
+
1360
+ def test_map_keys_checks_type
1361
+ assert_error_message("$map: 12 is not a map for `map-keys'", "map-keys(12)")
1362
+ end
1363
+
1364
+ def test_map_values
1365
+ assert_equal("1, 2", perform("map-values((foo: 1, bar: 2))").to_sass)
1366
+ assert_equal("1, 2, 2",
1367
+ perform("map-values((foo: 1, bar: 2, baz: 2))").to_sass)
1368
+ assert_equal("()", perform("map-values(())").to_sass)
1369
+ end
1370
+
1371
+ def test_map_values_checks_type
1372
+ assert_error_message("$map: 12 is not a map for `map-values'", "map-values(12)")
1373
+ end
1374
+
1375
+ def test_map_has_key
1376
+ assert_equal "true", evaluate("map-has-key((foo: 1, bar: 1), foo)")
1377
+ assert_equal "false", evaluate("map-has-key((foo: 1, bar: 1), baz)")
1378
+ assert_equal "false", evaluate("map-has-key((), foo)")
1379
+ end
1380
+
1381
+ def test_map_has_key_checks_type
1382
+ assert_error_message("$map: 12 is not a map for `map-has-key'", "map-has-key(12, foo)")
1383
+ end
1384
+
1385
+ def test_keywords
1386
+ # The actual functionality is tested in tests where real arglists are passed.
1387
+ assert_error_message("$args: 12 is not a variable argument list for `keywords'", "keywords(12)")
1388
+ assert_error_message(
1389
+ "$args: (1 2 3) is not a variable argument list for `keywords'", "keywords(1 2 3)")
1390
+ end
1391
+
1392
+ def test_partial_list_of_pairs_doesnt_work_as_a_map
1393
+ assert_raises(Sass::SyntaxError) {evaluate("map-get((foo bar, baz bang, bip), 1)")}
1394
+ assert_raises(Sass::SyntaxError) {evaluate("map-get((foo bar, baz bang, bip bap bop), 1)")}
1395
+ assert_raises(Sass::SyntaxError) {evaluate("map-get((foo bar), 1)")}
1396
+ end
1397
+
1398
+ def test_assert_unit
1399
+ ctx = Sass::Script::Functions::EvaluationContext.new(Sass::Environment.new(nil, {}))
1400
+ ctx.assert_unit Sass::Script::Value::Number.new(10, ["px"], []), "px"
1401
+ ctx.assert_unit Sass::Script::Value::Number.new(10, [], []), nil
1402
+
1403
+ begin
1404
+ ctx.assert_unit Sass::Script::Value::Number.new(10, [], []), "px"
1405
+ fail
1406
+ rescue ArgumentError => e
1407
+ assert_equal "Expected 10 to have a unit of px", e.message
1408
+ end
1409
+
1410
+ begin
1411
+ ctx.assert_unit Sass::Script::Value::Number.new(10, ["px"], []), nil
1412
+ fail
1413
+ rescue ArgumentError => e
1414
+ assert_equal "Expected 10px to be unitless", e.message
1415
+ end
1416
+
1417
+ begin
1418
+ ctx.assert_unit Sass::Script::Value::Number.new(10, [], []), "px", "arg"
1419
+ fail
1420
+ rescue ArgumentError => e
1421
+ assert_equal "Expected $arg to have a unit of px but got 10", e.message
1422
+ end
1423
+
1424
+ begin
1425
+ ctx.assert_unit Sass::Script::Value::Number.new(10, ["px"], []), nil, "arg"
1426
+ fail
1427
+ rescue ArgumentError => e
1428
+ assert_equal "Expected $arg to be unitless but got 10px", e.message
1429
+ end
1430
+ end
1431
+
1432
+ def test_call_with_positional_arguments
1433
+ assert_equal evaluate("lighten(blue, 5%)"), evaluate("call(lighten, blue, 5%)")
1434
+ end
1435
+
1436
+ def test_call_with_keyword_arguments
1437
+ assert_equal(
1438
+ evaluate("lighten($color: blue, $amount: 5%)"),
1439
+ evaluate("call(lighten, $color: blue, $amount: 5%)"))
1440
+ end
1441
+
1442
+ def test_call_with_keyword_and_positional_arguments
1443
+ assert_equal(
1444
+ evaluate("lighten(blue, $amount: 5%)"),
1445
+ evaluate("call(lighten, blue, $amount: 5%)"))
1446
+ end
1447
+
1448
+ def test_call_with_dynamic_name
1449
+ assert_equal(
1450
+ evaluate("lighten($color: blue, $amount: 5%)"),
1451
+ evaluate("call($fn, $color: blue, $amount: 5%)",
1452
+ env("fn" => Sass::Script::String.new("lighten"))))
1453
+ end
1454
+
1455
+ def test_call_uses_local_scope
1456
+ assert_equal <<CSS, render(<<SCSS)
1457
+ .first-scope {
1458
+ a: local; }
1459
+
1460
+ .second-scope {
1461
+ a: global; }
1462
+ CSS
1463
+ @function foo() {@return global}
1464
+
1465
+ .first-scope {
1466
+ @function foo() {@return local}
1467
+ a: call(foo);
1468
+ }
1469
+
1470
+ .second-scope {
1471
+ a: call(foo);
1472
+ }
1473
+ SCSS
1474
+ end
1475
+
1476
+ def test_call_unknown_function
1477
+ assert_equal evaluate("unknown(red, blue)"), evaluate("call(unknown, red, blue)")
1478
+ end
1479
+
1480
+ def test_call_with_non_string_argument
1481
+ assert_error_message "$name: 3px is not a string for `call'", "call(3px)"
1482
+ end
1483
+
1484
+ def test_errors_in_called_function
1485
+ assert_error_message "$color: 3px is not a color for `lighten'", "call(lighten, 3px, 5%)"
1486
+ end
1487
+
1488
+ def test_variable_exists
1489
+ assert_equal <<CSS, render(<<SCSS)
1490
+ .test {
1491
+ false: false;
1492
+ true: true;
1493
+ true: true;
1494
+ true: true;
1495
+ true: true; }
1496
+ CSS
1497
+ $global-var: has-value;
1498
+ .test {
1499
+ false: variable-exists(foo);
1500
+ $foo: has-value;
1501
+ true: variable-exists(foo);
1502
+ true: variable-exists($name: foo);
1503
+ true: variable-exists(global-var);
1504
+ true: variable-exists($name: global-var);
1505
+ }
1506
+ SCSS
1507
+ end
1508
+
1509
+ def test_variable_exists_checks_type
1510
+ assert_error_message("$name: 1 is not a string for `variable-exists'", "variable-exists(1)")
1511
+ end
1512
+
1513
+ def test_global_variable_exists
1514
+ assert_equal <<CSS, render(<<SCSS)
1515
+ .test {
1516
+ false: false;
1517
+ false: false;
1518
+ true: true;
1519
+ true: true;
1520
+ false: false;
1521
+ true: true;
1522
+ true: true; }
1523
+ CSS
1524
+ $g: something;
1525
+ $h: null;
1526
+ $false: global-variable-exists(foo);
1527
+ $true: global-variable-exists(g);
1528
+ $named: global-variable-exists($name: g);
1529
+ .test {
1530
+ $foo: locally-defined;
1531
+ false: global-variable-exists(foo);
1532
+ false: global-variable-exists(foo2);
1533
+ true: global-variable-exists(g);
1534
+ true: global-variable-exists(h);
1535
+ false: $false;
1536
+ true: $true;
1537
+ true: $named;
1538
+ }
1539
+ SCSS
1540
+ end
1541
+
1542
+ def test_global_variable_exists_checks_type
1543
+ assert_error_message("$name: 1 is not a string for `global-variable-exists'",
1544
+ "global-variable-exists(1)")
1545
+ end
1546
+
1547
+ def test_function_exists
1548
+ # built-ins
1549
+ assert_equal "true", evaluate("function-exists(lighten)")
1550
+ # with named argument
1551
+ assert_equal "true", evaluate("function-exists($name: lighten)")
1552
+ # user-defined
1553
+ assert_equal <<CSS, render(<<SCSS)
1554
+ .test {
1555
+ foo-exists: true;
1556
+ bar-exists: false; }
1557
+ CSS
1558
+ @function foo() { @return "foo" }
1559
+ .test {
1560
+ foo-exists: function-exists(foo);
1561
+ bar-exists: function-exists(bar);
1562
+ }
1563
+ SCSS
1564
+ end
1565
+
1566
+ def test_function_exists_checks_type
1567
+ assert_error_message("$name: 1 is not a string for `function-exists'", "function-exists(1)")
1568
+ end
1569
+
1570
+ def test_mixin_exists
1571
+ assert_equal "false", evaluate("mixin-exists(foo)")
1572
+ # with named argument
1573
+ assert_equal "false", evaluate("mixin-exists($name: foo)")
1574
+ assert_equal <<CSS, render(<<SCSS)
1575
+ .test {
1576
+ foo-exists: true;
1577
+ bar-exists: false; }
1578
+ CSS
1579
+ @mixin foo() { foo: exists }
1580
+ .test {
1581
+ foo-exists: mixin-exists(foo);
1582
+ bar-exists: mixin-exists(bar);
1583
+ }
1584
+ SCSS
1585
+ end
1586
+
1587
+ def test_mixin_exists_checks_type
1588
+ assert_error_message("$name: 1 is not a string for `mixin-exists'", "mixin-exists(1)")
1589
+ end
1590
+
1591
+ def test_inspect
1592
+ assert_equal "()", evaluate("inspect(())")
1593
+ assert_equal "null", evaluate("inspect(null)")
1594
+ assert_equal "1px null 3px", evaluate("inspect(1px null 3px)")
1595
+ assert_equal "(a: 1, b: 2)", evaluate("inspect((a: 1, b: 2))")
1596
+ end
1597
+
1598
+ def test_random
1599
+ Sass::Script::Functions.random_seed = 1
1600
+ assert_equal "0.41702", evaluate("random()")
1601
+ assert_equal "13", evaluate("random(100)")
1602
+ end
1603
+
1604
+ def test_random_works_without_a_seed
1605
+ if Sass::Script::Functions.instance_variable_defined?("@random_number_generator")
1606
+ Sass::Script::Functions.send(:remove_instance_variable, "@random_number_generator")
1607
+ end
1608
+
1609
+ result = perform("random()")
1610
+ assert_kind_of Sass::Script::Number, result
1611
+ assert result.value >= 0, "Random number was below 0"
1612
+ assert result.value <= 1, "Random number was above 1"
1613
+ end
1614
+
1615
+ def test_random_with_limit_one
1616
+ # Passing 1 as the limit should always return 1, since limit calls return
1617
+ # integers from 1 to the argument, so when the argument is 1, its a predicatble
1618
+ # outcome
1619
+ assert "1", evaluate("random(1)")
1620
+ end
1621
+
1622
+ def test_random_with_limit_too_low
1623
+ assert_error_message("$limit 0 must be greater than or equal to 1 for `random'", "random(0)")
1624
+ end
1625
+
1626
+ def test_random_with_non_integer_limit
1627
+ assert_error_message("Expected $limit to be an integer but got 1.5 for `random'", "random(1.5)")
1628
+ end
1629
+
1630
+ # This could *possibly* fail, but exceedingly unlikely
1631
+ def test_random_is_semi_unique
1632
+ if Sass::Script::Functions.instance_variable_defined?("@random_number_generator")
1633
+ Sass::Script::Functions.send(:remove_instance_variable, "@random_number_generator")
1634
+ end
1635
+ refute_equal evaluate("random()"), evaluate("random()")
1636
+ end
1637
+
1638
+ def test_deprecated_arg_names
1639
+ assert_warning <<WARNING do
1640
+ DEPRECATION WARNING: The `$arg-1' argument for `deprecated-arg-fn()' has been renamed to `$arg1'.
1641
+ DEPRECATION WARNING: The `$arg-2' argument for `deprecated-arg-fn()' has been renamed to `$arg2'.
1642
+ WARNING
1643
+ assert_equal("1 2 3",
1644
+ evaluate("deprecated-arg-fn($arg-1: 1, $arg-2: 2, $arg3: 3)"))
1645
+ end
1646
+
1647
+ assert_warning <<WARNING do
1648
+ DEPRECATION WARNING: The `$arg-1' argument for `deprecated-arg-fn()' has been renamed to `$arg1'.
1649
+ DEPRECATION WARNING: The `$arg-2' argument for `deprecated-arg-fn()' has been renamed to `$arg2'.
1650
+ WARNING
1651
+ assert_equal("1 2",
1652
+ evaluate("deprecated-arg-fn($arg-1: 1, $arg-2: 2)"))
1653
+ end
1654
+
1655
+ assert_warning <<WARNING do
1656
+ DEPRECATION WARNING: The `$arg_1' argument for `deprecated-arg-fn()' has been renamed to `$arg1'.
1657
+ DEPRECATION WARNING: The `$arg_2' argument for `deprecated-arg-fn()' has been renamed to `$arg2'.
1658
+ WARNING
1659
+ assert_equal("1 2",
1660
+ evaluate("deprecated-arg-fn($arg_1: 1, $arg_2: 2)"))
1661
+ end
1662
+ end
1663
+
1664
+ def test_non_deprecated_arg_names
1665
+ assert_equal("1 2 3", evaluate("deprecated-arg-fn($arg1: 1, $arg2: 2, $arg3: 3)"))
1666
+ assert_equal("1 2", evaluate("deprecated-arg-fn($arg1: 1, $arg2: 2)"))
1667
+ end
1668
+
1669
+ ## Selector Functions
1670
+
1671
+ def test_selector_argument_parsing
1672
+ assert_equal("true", evaluate("selector-parse('.foo') == (join(('.foo',), (), space),)"))
1673
+ assert_equal("true", evaluate("selector-parse('.foo .bar') == ('.foo' '.bar',)"))
1674
+ assert_equal("true",
1675
+ evaluate("selector-parse('.foo .bar, .baz .bang') == ('.foo' '.bar', '.baz' '.bang')"))
1676
+
1677
+ assert_equal(".foo %bar", evaluate("selector-parse('.foo %bar')"))
1678
+
1679
+ assert_equal("true",
1680
+ evaluate("selector-parse(('.foo', '.bar')) == selector-parse('.foo, .bar')"))
1681
+ assert_equal("true",
1682
+ evaluate("selector-parse('.foo' '.bar') == selector-parse('.foo .bar')"))
1683
+
1684
+ assert_equal("true", evaluate("selector-parse(('.foo' '.bar', '.baz' '.bang')) == " +
1685
+ "selector-parse('.foo .bar, .baz .bang')"))
1686
+ assert_equal("true", evaluate("selector-parse(('.foo .bar', '.baz .bang')) == " +
1687
+ "selector-parse('.foo .bar, .baz .bang')"))
1688
+
1689
+ # This may throw an error in the future.
1690
+ assert_equal("true", evaluate("selector-parse(('.foo, .bar' '.baz, .bang')) == " +
1691
+ "selector-parse('.foo, .bar .baz, .bang')"))
1692
+ end
1693
+
1694
+ def test_selector_argument_validation
1695
+ assert_error_message("$selector: 12 is not a valid selector: it must be a string,\n" +
1696
+ "a list of strings, or a list of lists of strings for `selector-parse'", "selector-parse(12)")
1697
+ assert_error_message("$selector: (((\".foo\" \".bar\"), \".baz\") (\".bang\", \".qux\")) is not a valid selector: it must be a string,\n" +
1698
+ "a list of strings, or a list of lists of strings for `selector-parse'",
1699
+ "selector-parse(('.foo' '.bar', '.baz') ('.bang', '.qux'))")
1700
+ assert_error_message("$selector: \".#\" is not a valid selector: Invalid CSS after \".\": " +
1701
+ "expected class name, was \"#\" for `selector-parse'", "selector-parse('.#')")
1702
+ assert_error_message("$selector: \"&.foo\" is not a valid selector: Invalid CSS after \"\": " +
1703
+ "expected selector, was \"&.foo\" for `selector-parse'", "selector-parse('&.foo')")
1704
+ end
1705
+
1706
+ def test_selector_nest
1707
+ assert_equal(".foo", evaluate("selector-nest('.foo')"))
1708
+ assert_equal(".foo .bar", evaluate("selector-nest('.foo', '.bar')"))
1709
+ assert_equal(".foo .bar .baz", evaluate("selector-nest('.foo', '.bar', '.baz')"))
1710
+ assert_equal(".a .foo .b .bar", evaluate("selector-nest('.a .foo', '.b .bar')"))
1711
+ assert_equal(".foo.bar", evaluate("selector-nest('.foo', '&.bar')"))
1712
+ assert_equal(".baz .foo.bar", evaluate("selector-nest('.foo', '&.bar', '.baz &')"))
1713
+ end
1714
+
1715
+ def test_selector_nest_checks_types
1716
+ assert_error_message("$selectors: 12 is not a valid selector: it must be a string,\n" +
1717
+ "a list of strings, or a list of lists of strings for `selector-nest'",
1718
+ "selector-nest(12)")
1719
+ assert_error_message("$selectors: 12 is not a valid selector: it must be a string,\n" +
1720
+ "a list of strings, or a list of lists of strings for `selector-nest'",
1721
+ "selector-nest('.foo', 12)")
1722
+ end
1723
+
1724
+ def test_selector_nest_argument_validation
1725
+ assert_error_message("$selectors: At least one selector must be passed for `selector-nest'",
1726
+ "selector-nest()")
1727
+ end
1728
+
1729
+ def test_selector_append
1730
+ assert_equal(".foo.bar", evaluate("selector-append('.foo', '.bar')"))
1731
+ assert_equal(".a .foo.b .bar", evaluate("selector-append('.a .foo', '.b .bar')"))
1732
+ assert_equal(".foo-suffix", evaluate("selector-append('.foo', '-suffix')"))
1733
+ assert_equal(".foo.bar, .foo-suffix", evaluate("selector-append('.foo', '.bar, -suffix')"))
1734
+ assert_equal(".foo--suffix", evaluate("selector-append('.foo', '--suffix')"))
1735
+ assert_equal(".foo.bar, .foo--suffix", evaluate("selector-append('.foo', '.bar, --suffix')"))
1736
+ end
1737
+
1738
+ def test_selector_append_checks_types
1739
+ assert_error_message("$selectors: 12 is not a valid selector: it must be a string,\n" +
1740
+ "a list of strings, or a list of lists of strings for `selector-append'",
1741
+ "selector-append(12)")
1742
+ assert_error_message("$selectors: 12 is not a valid selector: it must be a string,\n" +
1743
+ "a list of strings, or a list of lists of strings for `selector-append'",
1744
+ "selector-append('.foo', 12)")
1745
+ end
1746
+
1747
+ def test_selector_append_errors
1748
+ assert_error_message("$selectors: At least one selector must be passed for `selector-append'",
1749
+ "selector-append()")
1750
+ assert_error_message("Can't append \"> .bar\" to \".foo\" for `selector-append'",
1751
+ "selector-append('.foo', '> .bar')")
1752
+ assert_error_message("Can't append \"*.bar\" to \".foo\" for `selector-append'",
1753
+ "selector-append('.foo', '*.bar')")
1754
+ assert_error_message("Can't append \"ns|suffix\" to \".foo\" for `selector-append'",
1755
+ "selector-append('.foo', 'ns|suffix')")
1756
+ end
1757
+
1758
+ def test_selector_extend
1759
+ assert_equal(".foo .x, .foo .a .bar, .a .foo .bar",
1760
+ evaluate("selector-extend('.foo .x', '.x', '.a .bar')"))
1761
+ assert_equal(".foo .x, .foo .bang, .x.bar, .bar.bang",
1762
+ evaluate("selector-extend('.foo .x, .x.bar', '.x', '.bang')"))
1763
+ assert_equal(".y .x, .foo .x, .y .foo, .foo .foo",
1764
+ evaluate("selector-extend('.y .x', '.x, .y', '.foo')"))
1765
+ assert_equal(".foo .x, .foo .bar, .foo .bang",
1766
+ evaluate("selector-extend('.foo .x', '.x', '.bar, .bang')"))
1767
+ assert_equal(".foo.x, .foo",
1768
+ evaluate("selector-extend('.foo.x', '.x', '.foo')"))
1769
+ end
1770
+
1771
+ def test_selector_extend_checks_types
1772
+ assert_error_message("$selector: 12 is not a valid selector: it must be a string,\n" +
1773
+ "a list of strings, or a list of lists of strings for `selector-extend'",
1774
+ "selector-extend(12, '.foo', '.bar')")
1775
+ assert_error_message("$extendee: 12 is not a valid selector: it must be a string,\n" +
1776
+ "a list of strings, or a list of lists of strings for `selector-extend'",
1777
+ "selector-extend('.foo', 12, '.bar')")
1778
+ assert_error_message("$extender: 12 is not a valid selector: it must be a string,\n" +
1779
+ "a list of strings, or a list of lists of strings for `selector-extend'",
1780
+ "selector-extend('.foo', '.bar', 12)")
1781
+ end
1782
+
1783
+ def test_selector_extend_errors
1784
+ assert_error_message("Can't extend .bar .baz: can't extend nested selectors for " +
1785
+ "`selector-extend'", "selector-extend('.foo', '.bar .baz', '.bang')")
1786
+ assert_error_message("Can't extend >: invalid selector for `selector-extend'",
1787
+ "selector-extend('.foo', '>', '.bang')")
1788
+ assert_error_message(".bang > can't extend: invalid selector for `selector-extend'",
1789
+ "selector-extend('.foo', '.bar', '.bang >')")
1790
+ end
1791
+
1792
+ def test_selector_replace
1793
+ assert_equal(".bar", evaluate("selector-replace('.foo', '.foo', '.bar')"))
1794
+ assert_equal(".foo.baz", evaluate("selector-replace('.foo.bar', '.bar', '.baz')"))
1795
+ assert_equal(".a .foo.baz", evaluate("selector-replace('.foo.bar', '.bar', '.a .baz')"))
1796
+ assert_equal(".foo.bar", evaluate("selector-replace('.foo.bar', '.baz.bar', '.qux')"))
1797
+ assert_equal(".bar.qux", evaluate("selector-replace('.foo.bar.baz', '.foo.baz', '.qux')"))
1798
+
1799
+ assert_equal(":not(.bar)", evaluate("selector-replace(':not(.foo)', '.foo', '.bar')"))
1800
+ assert_equal(".bar", evaluate("selector-replace(':not(.foo)', ':not(.foo)', '.bar')"))
1801
+ end
1802
+
1803
+ def test_selector_replace_checks_types
1804
+ assert_error_message("$selector: 12 is not a valid selector: it must be a string,\n" +
1805
+ "a list of strings, or a list of lists of strings for `selector-replace'",
1806
+ "selector-replace(12, '.foo', '.bar')")
1807
+ assert_error_message("$original: 12 is not a valid selector: it must be a string,\n" +
1808
+ "a list of strings, or a list of lists of strings for `selector-replace'",
1809
+ "selector-replace('.foo', 12, '.bar')")
1810
+ assert_error_message("$replacement: 12 is not a valid selector: it must be a string,\n" +
1811
+ "a list of strings, or a list of lists of strings for `selector-replace'",
1812
+ "selector-replace('.foo', '.bar', 12)")
1813
+ end
1814
+
1815
+ def test_selector_replace_errors
1816
+ assert_error_message("Can't extend .bar .baz: can't extend nested selectors for " +
1817
+ "`selector-replace'", "selector-replace('.foo', '.bar .baz', '.bang')")
1818
+ assert_error_message("Can't extend >: invalid selector for `selector-replace'",
1819
+ "selector-replace('.foo', '>', '.bang')")
1820
+ assert_error_message(".bang > can't extend: invalid selector for `selector-replace'",
1821
+ "selector-replace('.foo', '.bar', '.bang >')")
1822
+ end
1823
+
1824
+ def test_selector_unify
1825
+ assert_equal(".foo", evaluate("selector-unify('.foo', '.foo')"))
1826
+ assert_equal(".foo.bar", evaluate("selector-unify('.foo', '.bar')"))
1827
+ assert_equal(".foo.bar.baz", evaluate("selector-unify('.foo.bar', '.bar.baz')"))
1828
+ assert_equal(".a .b .foo.bar, .b .a .foo.bar", evaluate("selector-unify('.a .foo', '.b .bar')"))
1829
+ assert_equal(".a .foo.bar", evaluate("selector-unify('.a .foo', '.a .bar')"))
1830
+ assert_equal("", evaluate("selector-unify('p', 'a')"))
1831
+ assert_equal("", evaluate("selector-unify('.foo >', '.bar')"))
1832
+ assert_equal("", evaluate("selector-unify('.foo', '.bar >')"))
1833
+ assert_equal(".foo.baz, .foo.bang, .bar.baz, .bar.bang",
1834
+ evaluate("selector-unify('.foo, .bar', '.baz, .bang')"))
1835
+ end
1836
+
1837
+ def test_selector_unify_checks_types
1838
+ assert_error_message("$selector1: 12 is not a valid selector: it must be a string,\n" +
1839
+ "a list of strings, or a list of lists of strings for `selector-unify'",
1840
+ "selector-unify(12, '.foo')")
1841
+ assert_error_message("$selector2: 12 is not a valid selector: it must be a string,\n" +
1842
+ "a list of strings, or a list of lists of strings for `selector-unify'",
1843
+ "selector-unify('.foo', 12)")
1844
+ end
1845
+
1846
+ def test_simple_selectors
1847
+ assert_equal('(.foo,)', evaluate("inspect(simple-selectors('.foo'))"))
1848
+ assert_equal('.foo, .bar', evaluate("inspect(simple-selectors('.foo.bar'))"))
1849
+ assert_equal('.foo, .bar, :pseudo("flip, flap")',
1850
+ evaluate("inspect(simple-selectors('.foo.bar:pseudo(\"flip, flap\")'))"))
1851
+ end
1852
+
1853
+ def test_simple_selectors_checks_types
1854
+ assert_error_message("$selector: 12 is not a string for `simple-selectors'",
1855
+ "simple-selectors(12)")
1856
+ end
1857
+
1858
+ def test_simple_selectors_errors
1859
+ assert_error_message("$selector: \".foo .bar\" is not a compound selector for `simple-selectors'",
1860
+ "simple-selectors('.foo .bar')")
1861
+ assert_error_message("$selector: \".foo,.bar\" is not a compound selector for `simple-selectors'",
1862
+ "simple-selectors('.foo,.bar')")
1863
+ assert_error_message("$selector: \".#\" is not a valid selector: Invalid CSS after \".\": " +
1864
+ "expected class name, was \"#\" for `simple-selectors'", "simple-selectors('.#')")
1865
+ end
1866
+
1867
+ def test_is_superselector
1868
+ assert_equal("true", evaluate("is-superselector('.foo', '.foo.bar')"))
1869
+ assert_equal("false", evaluate("is-superselector('.foo.bar', '.foo')"))
1870
+ assert_equal("true", evaluate("is-superselector('.foo', '.foo')"))
1871
+ assert_equal("true", evaluate("is-superselector('.bar', '.foo .bar')"))
1872
+ assert_equal("false", evaluate("is-superselector('.foo .bar', '.bar')"))
1873
+ assert_equal("true", evaluate("is-superselector('.foo .bar', '.foo > .bar')"))
1874
+ assert_equal("false", evaluate("is-superselector('.foo > .bar', '.foo .bar')"))
1875
+ end
1876
+
1877
+ def test_is_superselector_checks_types
1878
+ assert_error_message("$super: 12 is not a valid selector: it must be a string,\n" +
1879
+ "a list of strings, or a list of lists of strings for `is-superselector'",
1880
+ "is-superselector(12, '.foo')")
1881
+ assert_error_message("$sub: 12 is not a valid selector: it must be a string,\n" +
1882
+ "a list of strings, or a list of lists of strings for `is-superselector'",
1883
+ "is-superselector('.foo', 12)")
1884
+ end
1885
+
1886
+ ## Regression Tests
1887
+
1888
+ def test_inspect_nested_empty_lists
1889
+ assert_equal "() ()", evaluate("inspect(() ())")
1890
+ end
1891
+
1892
+ def test_saturation_bounds
1893
+ assert_equal "#fbfdff", evaluate("hsl(hue(#fbfdff), saturation(#fbfdff), lightness(#fbfdff))")
1894
+ end
1895
+
1896
+ private
1897
+ def env(hash = {}, parent = nil)
1898
+ env = Sass::Environment.new(parent)
1899
+ hash.each {|k, v| env.set_var(k, v)}
1900
+ env
1901
+ end
1902
+
1903
+ def evaluate(value, environment = env)
1904
+ result = perform(value, environment)
1905
+ assert_kind_of Sass::Script::Value::Base, result
1906
+ return result.to_s
1907
+ end
1908
+
1909
+ def perform(value, environment = env)
1910
+ Sass::Script::Parser.parse(value, 0, 0).perform(environment)
1911
+ end
1912
+
1913
+ def render(sass, options = {})
1914
+ options[:syntax] ||= :scss
1915
+ munge_filename options
1916
+ options[:importer] ||= MockImporter.new
1917
+ Sass::Engine.new(sass, options).render
1918
+ end
1919
+
1920
+ def assert_error_message(message, value)
1921
+ evaluate(value)
1922
+ flunk("Error message expected but not raised: #{message}")
1923
+ rescue Sass::SyntaxError => e
1924
+ assert_equal(message, e.message)
1925
+ end
1926
+ end