sass 3.7.4 → 4.0.0.alpha.1

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