xass 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (242) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +11 -0
  3. data/CONTRIBUTING +3 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.md +201 -0
  6. data/Rakefile +349 -0
  7. data/VERSION +1 -0
  8. data/VERSION_NAME +1 -0
  9. data/bin/push +13 -0
  10. data/bin/sass +13 -0
  11. data/bin/sass-convert +12 -0
  12. data/bin/scss +13 -0
  13. data/extra/update_watch.rb +13 -0
  14. data/init.rb +18 -0
  15. data/lib/sass/cache_stores/base.rb +88 -0
  16. data/lib/sass/cache_stores/chain.rb +33 -0
  17. data/lib/sass/cache_stores/filesystem.rb +64 -0
  18. data/lib/sass/cache_stores/memory.rb +47 -0
  19. data/lib/sass/cache_stores/null.rb +25 -0
  20. data/lib/sass/cache_stores.rb +15 -0
  21. data/lib/sass/callbacks.rb +66 -0
  22. data/lib/sass/css.rb +409 -0
  23. data/lib/sass/engine.rb +930 -0
  24. data/lib/sass/environment.rb +101 -0
  25. data/lib/sass/error.rb +201 -0
  26. data/lib/sass/exec.rb +707 -0
  27. data/lib/sass/importers/base.rb +139 -0
  28. data/lib/sass/importers/filesystem.rb +186 -0
  29. data/lib/sass/importers.rb +22 -0
  30. data/lib/sass/logger/base.rb +32 -0
  31. data/lib/sass/logger/log_level.rb +49 -0
  32. data/lib/sass/logger.rb +15 -0
  33. data/lib/sass/media.rb +213 -0
  34. data/lib/sass/plugin/compiler.rb +406 -0
  35. data/lib/sass/plugin/configuration.rb +123 -0
  36. data/lib/sass/plugin/generic.rb +15 -0
  37. data/lib/sass/plugin/merb.rb +48 -0
  38. data/lib/sass/plugin/rack.rb +60 -0
  39. data/lib/sass/plugin/rails.rb +47 -0
  40. data/lib/sass/plugin/staleness_checker.rb +199 -0
  41. data/lib/sass/plugin.rb +133 -0
  42. data/lib/sass/railtie.rb +10 -0
  43. data/lib/sass/repl.rb +57 -0
  44. data/lib/sass/root.rb +7 -0
  45. data/lib/sass/script/arg_list.rb +52 -0
  46. data/lib/sass/script/bool.rb +18 -0
  47. data/lib/sass/script/color.rb +606 -0
  48. data/lib/sass/script/css_lexer.rb +29 -0
  49. data/lib/sass/script/css_parser.rb +31 -0
  50. data/lib/sass/script/funcall.rb +245 -0
  51. data/lib/sass/script/functions.rb +1543 -0
  52. data/lib/sass/script/interpolation.rb +79 -0
  53. data/lib/sass/script/lexer.rb +345 -0
  54. data/lib/sass/script/list.rb +85 -0
  55. data/lib/sass/script/literal.rb +221 -0
  56. data/lib/sass/script/node.rb +99 -0
  57. data/lib/sass/script/null.rb +37 -0
  58. data/lib/sass/script/number.rb +453 -0
  59. data/lib/sass/script/operation.rb +110 -0
  60. data/lib/sass/script/parser.rb +502 -0
  61. data/lib/sass/script/string.rb +51 -0
  62. data/lib/sass/script/string_interpolation.rb +103 -0
  63. data/lib/sass/script/unary_operation.rb +69 -0
  64. data/lib/sass/script/variable.rb +58 -0
  65. data/lib/sass/script.rb +39 -0
  66. data/lib/sass/scss/css_parser.rb +36 -0
  67. data/lib/sass/scss/parser.rb +1180 -0
  68. data/lib/sass/scss/rx.rb +133 -0
  69. data/lib/sass/scss/script_lexer.rb +15 -0
  70. data/lib/sass/scss/script_parser.rb +25 -0
  71. data/lib/sass/scss/static_parser.rb +54 -0
  72. data/lib/sass/scss.rb +16 -0
  73. data/lib/sass/selector/abstract_sequence.rb +94 -0
  74. data/lib/sass/selector/comma_sequence.rb +92 -0
  75. data/lib/sass/selector/sequence.rb +507 -0
  76. data/lib/sass/selector/simple.rb +119 -0
  77. data/lib/sass/selector/simple_sequence.rb +215 -0
  78. data/lib/sass/selector.rb +452 -0
  79. data/lib/sass/shared.rb +76 -0
  80. data/lib/sass/supports.rb +229 -0
  81. data/lib/sass/tree/charset_node.rb +22 -0
  82. data/lib/sass/tree/comment_node.rb +82 -0
  83. data/lib/sass/tree/content_node.rb +9 -0
  84. data/lib/sass/tree/css_import_node.rb +60 -0
  85. data/lib/sass/tree/debug_node.rb +18 -0
  86. data/lib/sass/tree/directive_node.rb +42 -0
  87. data/lib/sass/tree/each_node.rb +24 -0
  88. data/lib/sass/tree/extend_node.rb +36 -0
  89. data/lib/sass/tree/for_node.rb +36 -0
  90. data/lib/sass/tree/function_node.rb +34 -0
  91. data/lib/sass/tree/if_node.rb +52 -0
  92. data/lib/sass/tree/import_node.rb +75 -0
  93. data/lib/sass/tree/media_node.rb +58 -0
  94. data/lib/sass/tree/mixin_def_node.rb +38 -0
  95. data/lib/sass/tree/mixin_node.rb +39 -0
  96. data/lib/sass/tree/node.rb +196 -0
  97. data/lib/sass/tree/prop_node.rb +152 -0
  98. data/lib/sass/tree/return_node.rb +18 -0
  99. data/lib/sass/tree/root_node.rb +78 -0
  100. data/lib/sass/tree/rule_node.rb +132 -0
  101. data/lib/sass/tree/supports_node.rb +51 -0
  102. data/lib/sass/tree/trace_node.rb +32 -0
  103. data/lib/sass/tree/variable_node.rb +30 -0
  104. data/lib/sass/tree/visitors/base.rb +75 -0
  105. data/lib/sass/tree/visitors/check_nesting.rb +147 -0
  106. data/lib/sass/tree/visitors/convert.rb +316 -0
  107. data/lib/sass/tree/visitors/cssize.rb +241 -0
  108. data/lib/sass/tree/visitors/deep_copy.rb +102 -0
  109. data/lib/sass/tree/visitors/extend.rb +68 -0
  110. data/lib/sass/tree/visitors/perform.rb +446 -0
  111. data/lib/sass/tree/visitors/set_options.rb +125 -0
  112. data/lib/sass/tree/visitors/to_css.rb +228 -0
  113. data/lib/sass/tree/warn_node.rb +18 -0
  114. data/lib/sass/tree/while_node.rb +18 -0
  115. data/lib/sass/util/multibyte_string_scanner.rb +155 -0
  116. data/lib/sass/util/subset_map.rb +109 -0
  117. data/lib/sass/util/test.rb +10 -0
  118. data/lib/sass/util.rb +948 -0
  119. data/lib/sass/version.rb +126 -0
  120. data/lib/sass.rb +95 -0
  121. data/rails/init.rb +1 -0
  122. data/test/Gemfile +3 -0
  123. data/test/Gemfile.lock +10 -0
  124. data/test/sass/cache_test.rb +89 -0
  125. data/test/sass/callbacks_test.rb +61 -0
  126. data/test/sass/conversion_test.rb +1760 -0
  127. data/test/sass/css2sass_test.rb +458 -0
  128. data/test/sass/data/hsl-rgb.txt +319 -0
  129. data/test/sass/engine_test.rb +3244 -0
  130. data/test/sass/exec_test.rb +86 -0
  131. data/test/sass/extend_test.rb +1482 -0
  132. data/test/sass/fixtures/test_staleness_check_across_importers.css +1 -0
  133. data/test/sass/fixtures/test_staleness_check_across_importers.scss +1 -0
  134. data/test/sass/functions_test.rb +1139 -0
  135. data/test/sass/importer_test.rb +192 -0
  136. data/test/sass/logger_test.rb +58 -0
  137. data/test/sass/mock_importer.rb +49 -0
  138. data/test/sass/more_results/more1.css +9 -0
  139. data/test/sass/more_results/more1_with_line_comments.css +26 -0
  140. data/test/sass/more_results/more_import.css +29 -0
  141. data/test/sass/more_templates/_more_partial.sass +2 -0
  142. data/test/sass/more_templates/more1.sass +23 -0
  143. data/test/sass/more_templates/more_import.sass +11 -0
  144. data/test/sass/plugin_test.rb +564 -0
  145. data/test/sass/results/alt.css +4 -0
  146. data/test/sass/results/basic.css +9 -0
  147. data/test/sass/results/cached_import_option.css +3 -0
  148. data/test/sass/results/compact.css +5 -0
  149. data/test/sass/results/complex.css +86 -0
  150. data/test/sass/results/compressed.css +1 -0
  151. data/test/sass/results/expanded.css +19 -0
  152. data/test/sass/results/filename_fn.css +3 -0
  153. data/test/sass/results/if.css +3 -0
  154. data/test/sass/results/import.css +31 -0
  155. data/test/sass/results/import_charset.css +5 -0
  156. data/test/sass/results/import_charset_1_8.css +5 -0
  157. data/test/sass/results/import_charset_ibm866.css +5 -0
  158. data/test/sass/results/import_content.css +1 -0
  159. data/test/sass/results/line_numbers.css +49 -0
  160. data/test/sass/results/mixins.css +95 -0
  161. data/test/sass/results/multiline.css +24 -0
  162. data/test/sass/results/nested.css +22 -0
  163. data/test/sass/results/options.css +1 -0
  164. data/test/sass/results/parent_ref.css +13 -0
  165. data/test/sass/results/script.css +16 -0
  166. data/test/sass/results/scss_import.css +31 -0
  167. data/test/sass/results/scss_importee.css +2 -0
  168. data/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
  169. data/test/sass/results/subdir/subdir.css +3 -0
  170. data/test/sass/results/units.css +11 -0
  171. data/test/sass/results/warn.css +0 -0
  172. data/test/sass/results/warn_imported.css +0 -0
  173. data/test/sass/script_conversion_test.rb +299 -0
  174. data/test/sass/script_test.rb +622 -0
  175. data/test/sass/scss/css_test.rb +1100 -0
  176. data/test/sass/scss/rx_test.rb +156 -0
  177. data/test/sass/scss/scss_test.rb +2106 -0
  178. data/test/sass/scss/test_helper.rb +37 -0
  179. data/test/sass/templates/_cached_import_option_partial.scss +1 -0
  180. data/test/sass/templates/_double_import_loop2.sass +1 -0
  181. data/test/sass/templates/_filename_fn_import.scss +11 -0
  182. data/test/sass/templates/_imported_charset_ibm866.sass +4 -0
  183. data/test/sass/templates/_imported_charset_utf8.sass +4 -0
  184. data/test/sass/templates/_imported_content.sass +3 -0
  185. data/test/sass/templates/_partial.sass +2 -0
  186. data/test/sass/templates/_same_name_different_partiality.scss +1 -0
  187. data/test/sass/templates/alt.sass +16 -0
  188. data/test/sass/templates/basic.sass +23 -0
  189. data/test/sass/templates/bork1.sass +2 -0
  190. data/test/sass/templates/bork2.sass +2 -0
  191. data/test/sass/templates/bork3.sass +2 -0
  192. data/test/sass/templates/bork4.sass +2 -0
  193. data/test/sass/templates/bork5.sass +3 -0
  194. data/test/sass/templates/cached_import_option.scss +3 -0
  195. data/test/sass/templates/compact.sass +17 -0
  196. data/test/sass/templates/complex.sass +305 -0
  197. data/test/sass/templates/compressed.sass +15 -0
  198. data/test/sass/templates/double_import_loop1.sass +1 -0
  199. data/test/sass/templates/expanded.sass +17 -0
  200. data/test/sass/templates/filename_fn.scss +18 -0
  201. data/test/sass/templates/if.sass +11 -0
  202. data/test/sass/templates/import.sass +12 -0
  203. data/test/sass/templates/import_charset.sass +9 -0
  204. data/test/sass/templates/import_charset_1_8.sass +6 -0
  205. data/test/sass/templates/import_charset_ibm866.sass +11 -0
  206. data/test/sass/templates/import_content.sass +4 -0
  207. data/test/sass/templates/importee.less +2 -0
  208. data/test/sass/templates/importee.sass +19 -0
  209. data/test/sass/templates/line_numbers.sass +13 -0
  210. data/test/sass/templates/mixin_bork.sass +5 -0
  211. data/test/sass/templates/mixins.sass +76 -0
  212. data/test/sass/templates/multiline.sass +20 -0
  213. data/test/sass/templates/nested.sass +25 -0
  214. data/test/sass/templates/nested_bork1.sass +2 -0
  215. data/test/sass/templates/nested_bork2.sass +2 -0
  216. data/test/sass/templates/nested_bork3.sass +2 -0
  217. data/test/sass/templates/nested_bork4.sass +2 -0
  218. data/test/sass/templates/nested_import.sass +2 -0
  219. data/test/sass/templates/nested_mixin_bork.sass +6 -0
  220. data/test/sass/templates/options.sass +2 -0
  221. data/test/sass/templates/parent_ref.sass +25 -0
  222. data/test/sass/templates/same_name_different_ext.sass +2 -0
  223. data/test/sass/templates/same_name_different_ext.scss +1 -0
  224. data/test/sass/templates/same_name_different_partiality.scss +1 -0
  225. data/test/sass/templates/script.sass +101 -0
  226. data/test/sass/templates/scss_import.scss +11 -0
  227. data/test/sass/templates/scss_importee.scss +1 -0
  228. data/test/sass/templates/single_import_loop.sass +1 -0
  229. data/test/sass/templates/subdir/import_up1.scss +1 -0
  230. data/test/sass/templates/subdir/import_up2.scss +1 -0
  231. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
  232. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
  233. data/test/sass/templates/subdir/subdir.sass +6 -0
  234. data/test/sass/templates/units.sass +11 -0
  235. data/test/sass/templates/warn.sass +3 -0
  236. data/test/sass/templates/warn_imported.sass +4 -0
  237. data/test/sass/test_helper.rb +8 -0
  238. data/test/sass/util/multibyte_string_scanner_test.rb +147 -0
  239. data/test/sass/util/subset_map_test.rb +91 -0
  240. data/test/sass/util_test.rb +382 -0
  241. data/test/test_helper.rb +80 -0
  242. metadata +354 -0
@@ -0,0 +1,622 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../test_helper'
3
+ require 'sass/engine'
4
+
5
+ module Sass::Script::Functions::UserFunctions
6
+ def assert_options(val)
7
+ val.options[:foo]
8
+ Sass::Script::String.new("Options defined!")
9
+ end
10
+
11
+ def arg_error
12
+ assert_options
13
+ end
14
+ end
15
+
16
+ module Sass::Script::Functions
17
+ include Sass::Script::Functions::UserFunctions
18
+ end
19
+
20
+ class SassScriptTest < Test::Unit::TestCase
21
+ include Sass::Script
22
+
23
+ def test_color_checks_input
24
+ assert_raise_message(ArgumentError, "Blue value -1 must be between 0 and 255") {Color.new([1, 2, -1])}
25
+ assert_raise_message(ArgumentError, "Red value 256 must be between 0 and 255") {Color.new([256, 2, 3])}
26
+ end
27
+
28
+ def test_color_checks_rgba_input
29
+ assert_raise_message(ArgumentError, "Alpha channel 1.1 must be between 0 and 1") {Color.new([1, 2, 3, 1.1])}
30
+ assert_raise_message(ArgumentError, "Alpha channel -0.1 must be between 0 and 1") {Color.new([1, 2, 3, -0.1])}
31
+ end
32
+
33
+ def test_string_escapes
34
+ assert_equal "'", resolve("\"'\"")
35
+ assert_equal '"', resolve("\"\\\"\"")
36
+ assert_equal "\\\\", resolve("\"\\\\\"")
37
+ assert_equal "\\02fa", resolve("\"\\02fa\"")
38
+
39
+ assert_equal "'", resolve("'\\''")
40
+ assert_equal '"', resolve("'\"'")
41
+ assert_equal "\\\\", resolve("'\\\\'")
42
+ assert_equal "\\02fa", resolve("'\\02fa'")
43
+ end
44
+
45
+ def test_color_names
46
+ assert_equal "white", resolve("white")
47
+ assert_equal "white", resolve("#ffffff")
48
+ assert_equal "#fffffe", resolve("white - #000001")
49
+ end
50
+
51
+ def test_rgba_color_literals
52
+ assert_equal Sass::Script::Color.new([1, 2, 3, 0.75]), eval("rgba(1, 2, 3, 0.75)")
53
+ assert_equal "rgba(1, 2, 3, 0.75)", resolve("rgba(1, 2, 3, 0.75)")
54
+
55
+ assert_equal Sass::Script::Color.new([1, 2, 3, 0]), eval("rgba(1, 2, 3, 0)")
56
+ assert_equal "rgba(1, 2, 3, 0)", resolve("rgba(1, 2, 3, 0)")
57
+
58
+ assert_equal Sass::Script::Color.new([1, 2, 3]), eval("rgba(1, 2, 3, 1)")
59
+ assert_equal Sass::Script::Color.new([1, 2, 3, 1]), eval("rgba(1, 2, 3, 1)")
60
+ assert_equal "#010203", resolve("rgba(1, 2, 3, 1)")
61
+ assert_equal "white", resolve("rgba(255, 255, 255, 1)")
62
+ end
63
+
64
+ def test_rgba_color_math
65
+ assert_equal "rgba(50, 50, 100, 0.35)", resolve("rgba(1, 1, 2, 0.35) * rgba(50, 50, 50, 0.35)")
66
+ assert_equal "rgba(52, 52, 52, 0.25)", resolve("rgba(2, 2, 2, 0.25) + rgba(50, 50, 50, 0.25)")
67
+
68
+ assert_raise_message(Sass::SyntaxError, "Alpha channels must be equal: rgba(1, 2, 3, 0.15) + rgba(50, 50, 50, 0.75)") do
69
+ resolve("rgba(1, 2, 3, 0.15) + rgba(50, 50, 50, 0.75)")
70
+ end
71
+ assert_raise_message(Sass::SyntaxError, "Alpha channels must be equal: #123456 * rgba(50, 50, 50, 0.75)") do
72
+ resolve("#123456 * rgba(50, 50, 50, 0.75)")
73
+ end
74
+ assert_raise_message(Sass::SyntaxError, "Alpha channels must be equal: rgba(50, 50, 50, 0.75) / #123456") do
75
+ resolve("rgba(50, 50, 50, 0.75) / #123456")
76
+ end
77
+ end
78
+
79
+ def test_rgba_number_math
80
+ assert_equal "rgba(49, 49, 49, 0.75)", resolve("rgba(50, 50, 50, 0.75) - 1")
81
+ assert_equal "rgba(100, 100, 100, 0.75)", resolve("rgba(50, 50, 50, 0.75) * 2")
82
+ end
83
+
84
+ def test_rgba_rounding
85
+ assert_equal "rgba(10, 1, 0, 0.12346)", resolve("rgba(10.0, 1.23456789, 0.0, 0.1234567)")
86
+ end
87
+
88
+ def test_compressed_colors
89
+ assert_equal "#123456", resolve("#123456", :style => :compressed)
90
+ assert_equal "rgba(1,2,3,0.5)", resolve("rgba(1, 2, 3, 0.5)", :style => :compressed)
91
+ assert_equal "#123", resolve("#112233", :style => :compressed)
92
+ assert_equal "#000", resolve("black", :style => :compressed)
93
+ assert_equal "red", resolve("#f00", :style => :compressed)
94
+ assert_equal "blue", resolve("#00f", :style => :compressed)
95
+ assert_equal "navy", resolve("#000080", :style => :compressed)
96
+ assert_equal "navy #fff", resolve("#000080 white", :style => :compressed)
97
+ assert_equal "This color is #fff", resolve('"This color is #{ white }"', :style => :compressed)
98
+ end
99
+
100
+ def test_compressed_comma
101
+ # assert_equal "foo,bar,baz", resolve("foo, bar, baz", :style => :compressed)
102
+ # assert_equal "foo,#baf,baz", resolve("foo, #baf, baz", :style => :compressed)
103
+ assert_equal "foo,#baf,red", resolve("foo, #baf, #f00", :style => :compressed)
104
+ end
105
+
106
+ def test_implicit_strings
107
+ assert_equal Sass::Script::String.new("foo"), eval("foo")
108
+ assert_equal Sass::Script::String.new("foo/bar"), eval("foo/bar")
109
+ end
110
+
111
+ def test_basic_interpolation
112
+ assert_equal "foo3bar", resolve("foo\#{1 + 2}bar")
113
+ assert_equal "foo3 bar", resolve("foo\#{1 + 2} bar")
114
+ assert_equal "foo 3bar", resolve("foo \#{1 + 2}bar")
115
+ assert_equal "foo 3 bar", resolve("foo \#{1 + 2} bar")
116
+ assert_equal "foo 35 bar", resolve("foo \#{1 + 2}\#{2 + 3} bar")
117
+ assert_equal "foo 3 5 bar", resolve("foo \#{1 + 2} \#{2 + 3} bar")
118
+ assert_equal "3bar", resolve("\#{1 + 2}bar")
119
+ assert_equal "foo3", resolve("foo\#{1 + 2}")
120
+ assert_equal "3", resolve("\#{1 + 2}")
121
+ end
122
+
123
+ def test_interpolation_in_function
124
+ assert_equal 'flabnabbit(1foo)', resolve('flabnabbit(#{1 + "foo"})')
125
+ assert_equal 'flabnabbit(foo 1foobaz)', resolve('flabnabbit(foo #{1 + "foo"}baz)')
126
+ assert_equal('flabnabbit(foo 1foo2bar baz)',
127
+ resolve('flabnabbit(foo #{1 + "foo"}#{2 + "bar"} baz)'))
128
+ end
129
+
130
+ def test_interpolation_near_operators
131
+ assert_equal '3 , 7', resolve('#{1 + 2} , #{3 + 4}')
132
+ assert_equal '3, 7', resolve('#{1 + 2}, #{3 + 4}')
133
+ assert_equal '3 ,7', resolve('#{1 + 2} ,#{3 + 4}')
134
+ assert_equal '3,7', resolve('#{1 + 2},#{3 + 4}')
135
+ assert_equal '3, 7, 11', resolve('#{1 + 2}, #{3 + 4}, #{5 + 6}')
136
+ assert_equal '3, 7, 11', resolve('3, #{3 + 4}, 11')
137
+ assert_equal '3, 7, 11', resolve('3, 7, #{5 + 6}')
138
+
139
+ assert_equal '3 / 7', resolve('3 / #{3 + 4}')
140
+ assert_equal '3 /7', resolve('3 /#{3 + 4}')
141
+ assert_equal '3/ 7', resolve('3/ #{3 + 4}')
142
+ assert_equal '3/7', resolve('3/#{3 + 4}')
143
+
144
+ assert_equal '3 * 7', resolve('#{1 + 2} * 7')
145
+ assert_equal '3* 7', resolve('#{1 + 2}* 7')
146
+ assert_equal '3 *7', resolve('#{1 + 2} *7')
147
+ assert_equal '3*7', resolve('#{1 + 2}*7')
148
+
149
+ assert_equal '-3', resolve('-#{1 + 2}')
150
+ assert_equal '- 3', resolve('- #{1 + 2}')
151
+
152
+ assert_equal '5 + 3 * 7', resolve('5 + #{1 + 2} * #{3 + 4}')
153
+ assert_equal '5 +3 * 7', resolve('5 +#{1 + 2} * #{3 + 4}')
154
+ assert_equal '5+3 * 7', resolve('5+#{1 + 2} * #{3 + 4}')
155
+ assert_equal '3 * 7 + 5', resolve('#{1 + 2} * #{3 + 4} + 5')
156
+ assert_equal '3 * 7+ 5', resolve('#{1 + 2} * #{3 + 4}+ 5')
157
+ assert_equal '3 * 7+5', resolve('#{1 + 2} * #{3 + 4}+5')
158
+
159
+ assert_equal '5/3 + 7', resolve('5 / (#{1 + 2} + #{3 + 4})')
160
+ assert_equal '5/3 + 7', resolve('5 /(#{1 + 2} + #{3 + 4})')
161
+ assert_equal '5/3 + 7', resolve('5 /( #{1 + 2} + #{3 + 4} )')
162
+ assert_equal '3 + 7/5', resolve('(#{1 + 2} + #{3 + 4}) / 5')
163
+ assert_equal '3 + 7/5', resolve('(#{1 + 2} + #{3 + 4})/ 5')
164
+ assert_equal '3 + 7/5', resolve('( #{1 + 2} + #{3 + 4} )/ 5')
165
+
166
+ assert_equal '3 + 5', resolve('#{1 + 2} + 2 + 3')
167
+ assert_equal '3 +5', resolve('#{1 + 2} +2 + 3')
168
+ end
169
+
170
+ def test_string_interpolation
171
+ assert_equal "foo bar, baz bang", resolve('"foo #{"bar"}, #{"baz"} bang"')
172
+ assert_equal "foo bar baz bang", resolve('"foo #{"#{"ba" + "r"} baz"} bang"')
173
+ assert_equal 'foo #{bar baz} bang', resolve('"foo \#{#{"ba" + "r"} baz} bang"')
174
+ assert_equal 'foo #{baz bang', resolve('"foo #{"\#{" + "baz"} bang"')
175
+ assert_equal "foo2bar", resolve('\'foo#{1 + 1}bar\'')
176
+ assert_equal "foo2bar", resolve('"foo#{1 + 1}bar"')
177
+ assert_equal "foo1bar5baz4bang", resolve('\'foo#{1 + "bar#{2 + 3}baz" + 4}bang\'')
178
+ end
179
+
180
+ def test_rule_interpolation
181
+ assert_equal(<<CSS, render(<<SASS))
182
+ foo bar baz bang {
183
+ a: b; }
184
+ CSS
185
+ foo \#{"\#{"ba" + "r"} baz"} bang
186
+ a: b
187
+ SASS
188
+ assert_equal(<<CSS, render(<<SASS))
189
+ foo [bar="\#{bar baz}"] bang {
190
+ a: b; }
191
+ CSS
192
+ foo [bar="\\\#{\#{"ba" + "r"} baz}"] bang
193
+ a: b
194
+ SASS
195
+ assert_equal(<<CSS, render(<<SASS))
196
+ foo [bar="\#{baz"] bang {
197
+ a: b; }
198
+ CSS
199
+ foo [bar="\#{"\\\#{" + "baz"}"] bang
200
+ a: b
201
+ SASS
202
+ end
203
+
204
+ def test_inaccessible_functions
205
+ assert_equal "send(to_s)", resolve("send(to_s)", :line => 2)
206
+ assert_equal "public_instance_methods()", resolve("public_instance_methods()")
207
+ end
208
+
209
+ def test_adding_functions_directly_to_functions_module
210
+ assert !Functions.callable?('nonexistant')
211
+ Functions.class_eval { def nonexistant; end }
212
+ assert Functions.callable?('nonexistant')
213
+ Functions.send :remove_method, :nonexistant
214
+ end
215
+
216
+ def test_default_functions
217
+ assert_equal "url(12)", resolve("url(12)")
218
+ assert_equal 'blam("foo")', resolve('blam("foo")')
219
+ end
220
+
221
+ def test_function_results_have_options
222
+ assert_equal "Options defined!", resolve("assert_options(abs(1))")
223
+ assert_equal "Options defined!", resolve("assert_options(round(1.2))")
224
+ end
225
+
226
+ def test_funcall_requires_no_whitespace_before_lparen
227
+ assert_equal "no-repeat 15px", resolve("no-repeat (7px + 8px)")
228
+ assert_equal "no-repeat(15px)", resolve("no-repeat(7px + 8px)")
229
+ end
230
+
231
+ def test_dynamic_url
232
+ assert_equal "url(foo-bar)", resolve("url($foo)", {}, env('foo' => Sass::Script::String.new("foo-bar")))
233
+ assert_equal "url(foo-bar baz)", resolve("url($foo $bar)", {}, env('foo' => Sass::Script::String.new("foo-bar"), 'bar' => Sass::Script::String.new("baz")))
234
+ assert_equal "url(foo baz)", resolve("url(foo $bar)", {}, env('bar' => Sass::Script::String.new("baz")))
235
+ assert_equal "url(foo bar)", resolve("url(foo bar)")
236
+ end
237
+
238
+ def test_url_with_interpolation
239
+ assert_equal "url(http://sass-lang.com/images/foo-bar)", resolve("url(http://sass-lang.com/images/\#{foo-bar})")
240
+ assert_equal 'url("http://sass-lang.com/images/foo-bar")', resolve("url('http://sass-lang.com/images/\#{foo-bar}')")
241
+ assert_equal 'url("http://sass-lang.com/images/foo-bar")', resolve('url("http://sass-lang.com/images/#{foo-bar}")')
242
+ assert_unquoted "url(http://sass-lang.com/images/\#{foo-bar})"
243
+ end
244
+
245
+ def test_hyphenated_variables
246
+ assert_equal("a-b", resolve("$a-b", {}, env("a-b" => Sass::Script::String.new("a-b"))))
247
+ end
248
+
249
+ def test_ruby_equality
250
+ assert_equal eval('"foo"'), eval('"foo"')
251
+ assert_equal eval('1'), eval('1.0')
252
+ assert_equal eval('1 2 3.0'), eval('1 2 3')
253
+ assert_equal eval('1, 2, 3.0'), eval('1, 2, 3')
254
+ assert_equal eval('(1 2), (3, 4), (5 6)'), eval('(1 2), (3, 4), (5 6)')
255
+ assert_not_equal eval('1, 2, 3'), eval('1 2 3')
256
+ assert_not_equal eval('1'), eval('"1"')
257
+ end
258
+
259
+ def test_booleans
260
+ assert_equal "true", resolve("true")
261
+ assert_equal "false", resolve("false")
262
+ end
263
+
264
+ def test_null
265
+ assert_equal "", resolve("null")
266
+ end
267
+
268
+ def test_boolean_ops
269
+ assert_equal "true", resolve("true and true")
270
+ assert_equal "true", resolve("false or true")
271
+ assert_equal "true", resolve("true or false")
272
+ assert_equal "true", resolve("true or true")
273
+ assert_equal "false", resolve("false or false")
274
+ assert_equal "false", resolve("false and true")
275
+ assert_equal "false", resolve("true and false")
276
+ assert_equal "false", resolve("false and false")
277
+
278
+ assert_equal "true", resolve("not false")
279
+ assert_equal "false", resolve("not true")
280
+ assert_equal "true", resolve("not not true")
281
+
282
+ assert_equal "1", resolve("false or 1")
283
+ assert_equal "false", resolve("false and 1")
284
+ assert_equal "2", resolve("2 or 3")
285
+ assert_equal "3", resolve("2 and 3")
286
+
287
+ assert_equal "true", resolve("null or true")
288
+ assert_equal "true", resolve("true or null")
289
+ assert_equal "", resolve("null or null")
290
+ assert_equal "", resolve("null and true")
291
+ assert_equal "", resolve("true and null")
292
+ assert_equal "", resolve("null and null")
293
+
294
+ assert_equal "true", resolve("not null")
295
+
296
+ assert_equal "1", resolve("null or 1")
297
+ assert_equal "", resolve("null and 1")
298
+ end
299
+
300
+ def test_arithmetic_ops
301
+ assert_equal "2", resolve("1 + 1")
302
+ assert_equal "0", resolve("1 - 1")
303
+ assert_equal "8", resolve("2 * 4")
304
+ assert_equal "0.5", resolve("(2 / 4)")
305
+ assert_equal "2", resolve("(4 / 2)")
306
+
307
+ assert_equal "-1", resolve("-1")
308
+ end
309
+
310
+ def test_string_ops
311
+ assert_equal '"foo" "bar"', resolve('"foo" "bar"')
312
+ assert_equal "true 1", resolve('true 1')
313
+ assert_equal '"foo", "bar"', resolve("'foo' , 'bar'")
314
+ assert_equal "true, 1", resolve('true , 1')
315
+ assert_equal "foobar", resolve('"foo" + "bar"')
316
+ assert_equal "true1", resolve('true + 1')
317
+ assert_equal '"foo"-"bar"', resolve("'foo' - 'bar'")
318
+ assert_equal "true-1", resolve('true - 1')
319
+ assert_equal '"foo"/"bar"', resolve('"foo" / "bar"')
320
+ assert_equal "true/1", resolve('true / 1')
321
+
322
+ assert_equal '-"bar"', resolve("- 'bar'")
323
+ assert_equal "-true", resolve('- true')
324
+ assert_equal '/"bar"', resolve('/ "bar"')
325
+ assert_equal "/true", resolve('/ true')
326
+ end
327
+
328
+ def test_relational_ops
329
+ assert_equal "false", resolve("1 > 2")
330
+ assert_equal "false", resolve("2 > 2")
331
+ assert_equal "true", resolve("3 > 2")
332
+ assert_equal "false", resolve("1 >= 2")
333
+ assert_equal "true", resolve("2 >= 2")
334
+ assert_equal "true", resolve("3 >= 2")
335
+ assert_equal "true", resolve("1 < 2")
336
+ assert_equal "false", resolve("2 < 2")
337
+ assert_equal "false", resolve("3 < 2")
338
+ assert_equal "true", resolve("1 <= 2")
339
+ assert_equal "true", resolve("2 <= 2")
340
+ assert_equal "false", resolve("3 <= 2")
341
+ end
342
+
343
+ def test_null_ops
344
+ assert_raise_message(Sass::SyntaxError,
345
+ 'Invalid null operation: "null plus 1".') {eval("null + 1")}
346
+ assert_raise_message(Sass::SyntaxError,
347
+ 'Invalid null operation: "null minus 1".') {eval("null - 1")}
348
+ assert_raise_message(Sass::SyntaxError,
349
+ 'Invalid null operation: "null times 1".') {eval("null * 1")}
350
+ assert_raise_message(Sass::SyntaxError,
351
+ 'Invalid null operation: "null div 1".') {eval("null / 1")}
352
+ assert_raise_message(Sass::SyntaxError,
353
+ 'Invalid null operation: "null mod 1".') {eval("null % 1")}
354
+ assert_raise_message(Sass::SyntaxError,
355
+ 'Invalid null operation: "1 plus null".') {eval("1 + null")}
356
+ assert_raise_message(Sass::SyntaxError,
357
+ 'Invalid null operation: "1 minus null".') {eval("1 - null")}
358
+ assert_raise_message(Sass::SyntaxError,
359
+ 'Invalid null operation: "1 times null".') {eval("1 * null")}
360
+ assert_raise_message(Sass::SyntaxError,
361
+ 'Invalid null operation: "1 div null".') {eval("1 / null")}
362
+ assert_raise_message(Sass::SyntaxError,
363
+ 'Invalid null operation: "1 mod null".') {eval("1 % null")}
364
+ assert_raise_message(Sass::SyntaxError,
365
+ 'Invalid null operation: "1 gt null".') {eval("1 > null")}
366
+ assert_raise_message(Sass::SyntaxError,
367
+ 'Invalid null operation: "null lt 1".') {eval("null < 1")}
368
+ assert_raise_message(Sass::SyntaxError,
369
+ 'Invalid null operation: "null plus null".') {eval("null + null")}
370
+ assert_raise_message(Sass::SyntaxError,
371
+ 'Invalid null operation: ""foo" plus null".') {eval("foo + null")}
372
+ end
373
+
374
+ def test_equals
375
+ assert_equal("true", resolve('"foo" == $foo', {},
376
+ env("foo" => Sass::Script::String.new("foo"))))
377
+ assert_equal "true", resolve("1 == 1.0")
378
+ assert_equal "true", resolve("false != true")
379
+ assert_equal "false", resolve("1em == 1px")
380
+ assert_equal "false", resolve("12 != 12")
381
+ assert_equal "true", resolve("(foo bar baz) == (foo bar baz)")
382
+ assert_equal "true", resolve("(foo, bar, baz) == (foo, bar, baz)")
383
+ assert_equal "true", resolve('((1 2), (3, 4), (5 6)) == ((1 2), (3, 4), (5 6))')
384
+ assert_equal "true", resolve('((1 2), (3 4)) == (1 2, 3 4)')
385
+ assert_equal "false", resolve('((1 2) 3) == (1 2 3)')
386
+ assert_equal "false", resolve('(1 (2 3)) == (1 2 3)')
387
+ assert_equal "false", resolve('((1, 2) (3, 4)) == (1, 2 3, 4)')
388
+ assert_equal "false", resolve('(1 2 3) == (1, 2, 3)')
389
+
390
+ assert_equal "true", resolve('null == null')
391
+ assert_equal "false", resolve('"null" == null')
392
+ assert_equal "false", resolve('0 == null')
393
+ assert_equal "false", resolve('() == null')
394
+
395
+ assert_equal "false", resolve('null != null')
396
+ assert_equal "true", resolve('"null" != null')
397
+ assert_equal "true", resolve('0 != null')
398
+ assert_equal "true", resolve('() != null')
399
+ end
400
+
401
+ def test_operation_precedence
402
+ assert_equal "false true", resolve("true and false false or true")
403
+ assert_equal "true", resolve("false and true or true and true")
404
+ assert_equal "true", resolve("1 == 2 or 3 == 3")
405
+ assert_equal "true", resolve("1 < 2 == 3 >= 3")
406
+ assert_equal "true", resolve("1 + 3 > 4 - 2")
407
+ assert_equal "11", resolve("1 + 2 * 3 + 4")
408
+ end
409
+
410
+ def test_functions
411
+ assert_equal "#80ff80", resolve("hsl(120, 100%, 75%)")
412
+ assert_equal "#81ff81", resolve("hsl(120, 100%, 75%) + #010001")
413
+ end
414
+
415
+ def test_operator_unit_conversion
416
+ assert_equal "1.1cm", resolve("1cm + 1mm")
417
+ assert_equal "2in", resolve("1in + 96px")
418
+ assert_equal "true", resolve("2mm < 1cm")
419
+ assert_equal "true", resolve("10mm == 1cm")
420
+ assert_equal "true", resolve("1 == 1cm")
421
+ assert_equal "true", resolve("1.1cm == 11mm")
422
+ end
423
+
424
+ def test_operations_have_options
425
+ assert_equal "Options defined!", resolve("assert_options(1 + 1)")
426
+ assert_equal "Options defined!", resolve("assert_options('bar' + 'baz')")
427
+ end
428
+
429
+ def test_slash_compiles_literally_when_left_alone
430
+ assert_equal "1px/2px", resolve("1px/2px")
431
+ assert_equal "1px/2px/3px/4px", resolve("1px/2px/3px/4px")
432
+
433
+ assert_equal "1px/2px redpx bluepx", resolve("1px/2px redpx bluepx")
434
+ assert_equal "foo 1px/2px/3px bar", resolve("foo 1px/2px/3px bar")
435
+ end
436
+
437
+ def test_slash_divides_with_parens
438
+ assert_equal "0.5", resolve("(1px/2px)")
439
+ assert_equal "0.5", resolve("(1px)/2px")
440
+ assert_equal "0.5", resolve("1px/(2px)")
441
+ end
442
+
443
+ def test_slash_divides_with_other_arithmetic
444
+ assert_equal "0.5px", resolve("1px*1px/2px")
445
+ assert_equal "0.5px", resolve("1px/2px*1px")
446
+ assert_equal "0.5", resolve("0+1px/2px")
447
+ assert_equal "0.5", resolve("1px/2px+0")
448
+ end
449
+
450
+ def test_slash_divides_with_variable
451
+ assert_equal "0.5", resolve("$var/2px", {}, env("var" => eval("1px")))
452
+ assert_equal "0.5", resolve("1px/$var", {}, env("var" => eval("2px")))
453
+ assert_equal "0.5", resolve("$var", {}, env("var" => eval("1px/2px")))
454
+ end
455
+
456
+ def test_colors_with_wrong_number_of_digits
457
+ assert_raise_message(Sass::SyntaxError,
458
+ "Colors must have either three or six digits: '#0'") {eval("#0")}
459
+ assert_raise_message(Sass::SyntaxError,
460
+ "Colors must have either three or six digits: '#12'") {eval("#12")}
461
+ assert_raise_message(Sass::SyntaxError,
462
+ "Colors must have either three or six digits: '#abcd'") {eval("#abcd")}
463
+ assert_raise_message(Sass::SyntaxError,
464
+ "Colors must have either three or six digits: '#abcdE'") {eval("#abcdE")}
465
+ assert_raise_message(Sass::SyntaxError,
466
+ "Colors must have either three or six digits: '#abcdEFA'") {eval("#abcdEFA")}
467
+ end
468
+
469
+ def test_case_insensitive_color_names
470
+ assert_equal "blue", resolve("BLUE")
471
+ assert_equal "red", resolve("rEd")
472
+ assert_equal "#7f4000", resolve("mix(GrEeN, ReD)")
473
+ end
474
+
475
+ def test_empty_list
476
+ assert_equal "1 2 3", resolve("1 2 () 3")
477
+ assert_equal "1 2 3", resolve("1 2 3 ()")
478
+ assert_equal "1 2 3", resolve("() 1 2 3")
479
+ assert_raise_message(Sass::SyntaxError, "() isn't a valid CSS value.") {resolve("()")}
480
+ assert_raise_message(Sass::SyntaxError, "() isn't a valid CSS value.") {resolve("nth(append((), ()), 1)")}
481
+ end
482
+
483
+ def test_list_with_nulls
484
+ assert_equal "1, 2, 3", resolve("1, 2, null, 3")
485
+ assert_equal "1 2 3", resolve("1 2 null 3")
486
+ assert_equal "1, 2, 3", resolve("1, 2, 3, null")
487
+ assert_equal "1 2 3", resolve("1 2 3 null")
488
+ assert_equal "1, 2, 3", resolve("null, 1, 2, 3")
489
+ assert_equal "1 2 3", resolve("null 1 2 3")
490
+ end
491
+
492
+ def test_deep_argument_error_not_unwrapped
493
+ # JRuby (as of 1.6.7.2) offers no way of distinguishing between
494
+ # argument errors caused by programming errors in a function and
495
+ # argument errors explicitly thrown within that function.
496
+ return if RUBY_PLATFORM =~ /java/
497
+
498
+ # Don't validate the message; it's different on Rubinius.
499
+ assert_raise(ArgumentError) {resolve("arg-error()")}
500
+ end
501
+
502
+ def test_shallow_argument_error_unwrapped
503
+ assert_raise_message(Sass::SyntaxError, "wrong number of arguments (1 for 0) for `arg-error'") {resolve("arg-error(1)")}
504
+ end
505
+
506
+ def test_boolean_ops_short_circuit
507
+ assert_equal "false", resolve("$ie and $ie <= 7", {}, env('ie' => Sass::Script::Bool.new(false)))
508
+ assert_equal "true", resolve("$ie or $undef", {}, env('ie' => Sass::Script::Bool.new(true)))
509
+ end
510
+
511
+ # Regression Tests
512
+
513
+ def test_user_defined_function_forces_division
514
+ assert_equal(<<CSS, render(<<SASS))
515
+ a {
516
+ b: 10px; }
517
+ CSS
518
+ @function foo()
519
+ @return 20px
520
+
521
+ a
522
+ b: (foo() / 2)
523
+ SASS
524
+
525
+ assert_equal(<<CSS, render(<<SASS))
526
+ a {
527
+ b: 10px; }
528
+ CSS
529
+ @function foo()
530
+ @return 20px
531
+
532
+ a
533
+ b: foo() / 2
534
+ SASS
535
+ end
536
+
537
+ def test_funcall_has_higher_precedence_than_color_name
538
+ assert_equal "teal(12)", resolve("teal(12)")
539
+ assert_equal "tealbang(12)", resolve("tealbang(12)")
540
+ assert_equal "teal-bang(12)", resolve("teal-bang(12)")
541
+ assert_equal "teal\\+bang(12)", resolve("teal\\+bang(12)")
542
+ end
543
+
544
+ def test_funcall_has_higher_precedence_than_true_false_null
545
+ assert_equal "teal(12)", resolve("teal(12)")
546
+ assert_equal "tealbang(12)", resolve("tealbang(12)")
547
+ assert_equal "teal-bang(12)", resolve("teal-bang(12)")
548
+ assert_equal "teal\\+bang(12)", resolve("teal\\+bang(12)")
549
+ end
550
+
551
+ def test_interpolation_after_hash
552
+ assert_equal "#2", resolve('"##{1 + 1}"')
553
+ end
554
+
555
+ def test_misplaced_comma_in_funcall
556
+ assert_raise_message(Sass::SyntaxError,
557
+ 'Invalid CSS after "foo(bar, ": expected function argument, was ")"') {eval('foo(bar, )')}
558
+ end
559
+
560
+ def test_color_prefixed_identifier
561
+ assert_equal "tealbang", resolve("tealbang")
562
+ assert_equal "teal-bang", resolve("teal-bang")
563
+ end
564
+
565
+ def test_op_prefixed_identifier
566
+ assert_equal "notbang", resolve("notbang")
567
+ assert_equal "not-bang", resolve("not-bang")
568
+ assert_equal "or-bang", resolve("or-bang")
569
+ assert_equal "and-bang", resolve("and-bang")
570
+ end
571
+
572
+ private
573
+
574
+ def resolve(str, opts = {}, environment = env)
575
+ munge_filename opts
576
+ val = eval(str, opts, environment)
577
+ assert_kind_of Sass::Script::Literal, val
578
+ val.is_a?(Sass::Script::String) ? val.value : val.to_s
579
+ end
580
+
581
+ def assert_unquoted(str, opts = {}, environment = env)
582
+ munge_filename opts
583
+ val = eval(str, opts, environment)
584
+ assert_kind_of Sass::Script::String, val
585
+ assert_equal :identifier, val.type
586
+ end
587
+
588
+ def assert_quoted(str, opts = {}, environment = env)
589
+ munge_filename opts
590
+ val = eval(str, opts, environment)
591
+ assert_kind_of Sass::Script::String, val
592
+ assert_equal :string, val.type
593
+ end
594
+
595
+ def eval(str, opts = {}, environment = env)
596
+ munge_filename opts
597
+ Sass::Script.parse(str, opts.delete(:line) || 1,
598
+ opts.delete(:offset) || 0, opts).
599
+ perform(Sass::Environment.new(environment, opts))
600
+ end
601
+
602
+ def render(sass, options = {})
603
+ munge_filename options
604
+ Sass::Engine.new(sass, options).render
605
+ end
606
+
607
+ def env(hash = {})
608
+ env = Sass::Environment.new
609
+ hash.each {|k, v| env.set_var(k, v)}
610
+ env
611
+ end
612
+
613
+ def test_number_printing
614
+ assert_equal "1", eval("1")
615
+ assert_equal "1", eval("1.0")
616
+ assert_equal "1.121", eval("1.1214")
617
+ assert_equal "1.122", eval("1.1215")
618
+ assert_equal "Infinity", eval("1.0/0.0")
619
+ assert_equal "-Infinity", eval("-1.0/0.0")
620
+ assert_equal "NaN", eval("0.0/0.0")
621
+ end
622
+ end