aliddle-sass 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 (238) hide show
  1. data/.yardopts +11 -0
  2. data/CONTRIBUTING +3 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.md +201 -0
  5. data/Rakefile +347 -0
  6. data/VERSION +1 -0
  7. data/VERSION_NAME +1 -0
  8. data/bin/sass +9 -0
  9. data/bin/sass-convert +8 -0
  10. data/bin/scss +9 -0
  11. data/extra/update_watch.rb +13 -0
  12. data/init.rb +18 -0
  13. data/lib/sass.rb +95 -0
  14. data/lib/sass/cache_stores.rb +15 -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 +60 -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/callbacks.rb +66 -0
  21. data/lib/sass/css.rb +409 -0
  22. data/lib/sass/engine.rb +928 -0
  23. data/lib/sass/environment.rb +101 -0
  24. data/lib/sass/error.rb +201 -0
  25. data/lib/sass/exec.rb +707 -0
  26. data/lib/sass/importers.rb +22 -0
  27. data/lib/sass/importers/base.rb +139 -0
  28. data/lib/sass/importers/filesystem.rb +190 -0
  29. data/lib/sass/logger.rb +15 -0
  30. data/lib/sass/logger/base.rb +32 -0
  31. data/lib/sass/logger/log_level.rb +49 -0
  32. data/lib/sass/media.rb +213 -0
  33. data/lib/sass/plugin.rb +132 -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 +183 -0
  41. data/lib/sass/railtie.rb +9 -0
  42. data/lib/sass/repl.rb +57 -0
  43. data/lib/sass/root.rb +7 -0
  44. data/lib/sass/script.rb +39 -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 +237 -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 +348 -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 +495 -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/scss.rb +16 -0
  66. data/lib/sass/scss/css_parser.rb +36 -0
  67. data/lib/sass/scss/parser.rb +1179 -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/selector.rb +452 -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 +212 -0
  78. data/lib/sass/shared.rb +76 -0
  79. data/lib/sass/supports.rb +229 -0
  80. data/lib/sass/tree/charset_node.rb +22 -0
  81. data/lib/sass/tree/comment_node.rb +82 -0
  82. data/lib/sass/tree/content_node.rb +9 -0
  83. data/lib/sass/tree/css_import_node.rb +60 -0
  84. data/lib/sass/tree/debug_node.rb +18 -0
  85. data/lib/sass/tree/directive_node.rb +42 -0
  86. data/lib/sass/tree/each_node.rb +24 -0
  87. data/lib/sass/tree/extend_node.rb +36 -0
  88. data/lib/sass/tree/for_node.rb +36 -0
  89. data/lib/sass/tree/function_node.rb +34 -0
  90. data/lib/sass/tree/if_node.rb +52 -0
  91. data/lib/sass/tree/import_node.rb +75 -0
  92. data/lib/sass/tree/media_node.rb +58 -0
  93. data/lib/sass/tree/mixin_def_node.rb +38 -0
  94. data/lib/sass/tree/mixin_node.rb +39 -0
  95. data/lib/sass/tree/node.rb +196 -0
  96. data/lib/sass/tree/prop_node.rb +152 -0
  97. data/lib/sass/tree/return_node.rb +18 -0
  98. data/lib/sass/tree/root_node.rb +28 -0
  99. data/lib/sass/tree/rule_node.rb +132 -0
  100. data/lib/sass/tree/supports_node.rb +51 -0
  101. data/lib/sass/tree/trace_node.rb +32 -0
  102. data/lib/sass/tree/variable_node.rb +30 -0
  103. data/lib/sass/tree/visitors/base.rb +75 -0
  104. data/lib/sass/tree/visitors/check_nesting.rb +147 -0
  105. data/lib/sass/tree/visitors/convert.rb +316 -0
  106. data/lib/sass/tree/visitors/cssize.rb +229 -0
  107. data/lib/sass/tree/visitors/deep_copy.rb +102 -0
  108. data/lib/sass/tree/visitors/extend.rb +68 -0
  109. data/lib/sass/tree/visitors/perform.rb +446 -0
  110. data/lib/sass/tree/visitors/set_options.rb +125 -0
  111. data/lib/sass/tree/visitors/to_css.rb +230 -0
  112. data/lib/sass/tree/warn_node.rb +18 -0
  113. data/lib/sass/tree/while_node.rb +18 -0
  114. data/lib/sass/util.rb +906 -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/version.rb +126 -0
  119. data/rails/init.rb +1 -0
  120. data/test/Gemfile +3 -0
  121. data/test/Gemfile.lock +10 -0
  122. data/test/sass/cache_test.rb +89 -0
  123. data/test/sass/callbacks_test.rb +61 -0
  124. data/test/sass/conversion_test.rb +1760 -0
  125. data/test/sass/css2sass_test.rb +439 -0
  126. data/test/sass/data/hsl-rgb.txt +319 -0
  127. data/test/sass/engine_test.rb +3243 -0
  128. data/test/sass/exec_test.rb +86 -0
  129. data/test/sass/extend_test.rb +1461 -0
  130. data/test/sass/fixtures/test_staleness_check_across_importers.css +1 -0
  131. data/test/sass/fixtures/test_staleness_check_across_importers.scss +1 -0
  132. data/test/sass/functions_test.rb +1139 -0
  133. data/test/sass/importer_test.rb +192 -0
  134. data/test/sass/logger_test.rb +58 -0
  135. data/test/sass/mock_importer.rb +49 -0
  136. data/test/sass/more_results/more1.css +9 -0
  137. data/test/sass/more_results/more1_with_line_comments.css +26 -0
  138. data/test/sass/more_results/more_import.css +29 -0
  139. data/test/sass/more_templates/_more_partial.sass +2 -0
  140. data/test/sass/more_templates/more1.sass +23 -0
  141. data/test/sass/more_templates/more_import.sass +11 -0
  142. data/test/sass/plugin_test.rb +550 -0
  143. data/test/sass/results/alt.css +4 -0
  144. data/test/sass/results/basic.css +9 -0
  145. data/test/sass/results/cached_import_option.css +3 -0
  146. data/test/sass/results/compact.css +5 -0
  147. data/test/sass/results/complex.css +86 -0
  148. data/test/sass/results/compressed.css +1 -0
  149. data/test/sass/results/expanded.css +19 -0
  150. data/test/sass/results/filename_fn.css +3 -0
  151. data/test/sass/results/if.css +3 -0
  152. data/test/sass/results/import.css +31 -0
  153. data/test/sass/results/import_charset.css +5 -0
  154. data/test/sass/results/import_charset_1_8.css +5 -0
  155. data/test/sass/results/import_charset_ibm866.css +5 -0
  156. data/test/sass/results/import_content.css +1 -0
  157. data/test/sass/results/line_numbers.css +49 -0
  158. data/test/sass/results/mixins.css +95 -0
  159. data/test/sass/results/multiline.css +24 -0
  160. data/test/sass/results/nested.css +22 -0
  161. data/test/sass/results/options.css +1 -0
  162. data/test/sass/results/parent_ref.css +13 -0
  163. data/test/sass/results/script.css +16 -0
  164. data/test/sass/results/scss_import.css +31 -0
  165. data/test/sass/results/scss_importee.css +2 -0
  166. data/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
  167. data/test/sass/results/subdir/subdir.css +3 -0
  168. data/test/sass/results/units.css +11 -0
  169. data/test/sass/results/warn.css +0 -0
  170. data/test/sass/results/warn_imported.css +0 -0
  171. data/test/sass/script_conversion_test.rb +299 -0
  172. data/test/sass/script_test.rb +591 -0
  173. data/test/sass/scss/css_test.rb +1093 -0
  174. data/test/sass/scss/rx_test.rb +156 -0
  175. data/test/sass/scss/scss_test.rb +2043 -0
  176. data/test/sass/scss/test_helper.rb +37 -0
  177. data/test/sass/templates/_cached_import_option_partial.scss +1 -0
  178. data/test/sass/templates/_double_import_loop2.sass +1 -0
  179. data/test/sass/templates/_filename_fn_import.scss +11 -0
  180. data/test/sass/templates/_imported_charset_ibm866.sass +4 -0
  181. data/test/sass/templates/_imported_charset_utf8.sass +4 -0
  182. data/test/sass/templates/_imported_content.sass +3 -0
  183. data/test/sass/templates/_partial.sass +2 -0
  184. data/test/sass/templates/_same_name_different_partiality.scss +1 -0
  185. data/test/sass/templates/alt.sass +16 -0
  186. data/test/sass/templates/basic.sass +23 -0
  187. data/test/sass/templates/bork1.sass +2 -0
  188. data/test/sass/templates/bork2.sass +2 -0
  189. data/test/sass/templates/bork3.sass +2 -0
  190. data/test/sass/templates/bork4.sass +2 -0
  191. data/test/sass/templates/bork5.sass +3 -0
  192. data/test/sass/templates/cached_import_option.scss +3 -0
  193. data/test/sass/templates/compact.sass +17 -0
  194. data/test/sass/templates/complex.sass +305 -0
  195. data/test/sass/templates/compressed.sass +15 -0
  196. data/test/sass/templates/double_import_loop1.sass +1 -0
  197. data/test/sass/templates/expanded.sass +17 -0
  198. data/test/sass/templates/filename_fn.scss +18 -0
  199. data/test/sass/templates/if.sass +11 -0
  200. data/test/sass/templates/import.sass +12 -0
  201. data/test/sass/templates/import_charset.sass +9 -0
  202. data/test/sass/templates/import_charset_1_8.sass +6 -0
  203. data/test/sass/templates/import_charset_ibm866.sass +11 -0
  204. data/test/sass/templates/import_content.sass +4 -0
  205. data/test/sass/templates/importee.less +2 -0
  206. data/test/sass/templates/importee.sass +19 -0
  207. data/test/sass/templates/line_numbers.sass +13 -0
  208. data/test/sass/templates/mixin_bork.sass +5 -0
  209. data/test/sass/templates/mixins.sass +76 -0
  210. data/test/sass/templates/multiline.sass +20 -0
  211. data/test/sass/templates/nested.sass +25 -0
  212. data/test/sass/templates/nested_bork1.sass +2 -0
  213. data/test/sass/templates/nested_bork2.sass +2 -0
  214. data/test/sass/templates/nested_bork3.sass +2 -0
  215. data/test/sass/templates/nested_bork4.sass +2 -0
  216. data/test/sass/templates/nested_import.sass +2 -0
  217. data/test/sass/templates/nested_mixin_bork.sass +6 -0
  218. data/test/sass/templates/options.sass +2 -0
  219. data/test/sass/templates/parent_ref.sass +25 -0
  220. data/test/sass/templates/same_name_different_ext.sass +2 -0
  221. data/test/sass/templates/same_name_different_ext.scss +1 -0
  222. data/test/sass/templates/same_name_different_partiality.scss +1 -0
  223. data/test/sass/templates/script.sass +101 -0
  224. data/test/sass/templates/scss_import.scss +11 -0
  225. data/test/sass/templates/scss_importee.scss +1 -0
  226. data/test/sass/templates/single_import_loop.sass +1 -0
  227. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
  228. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
  229. data/test/sass/templates/subdir/subdir.sass +6 -0
  230. data/test/sass/templates/units.sass +11 -0
  231. data/test/sass/templates/warn.sass +3 -0
  232. data/test/sass/templates/warn_imported.sass +4 -0
  233. data/test/sass/test_helper.rb +8 -0
  234. data/test/sass/util/multibyte_string_scanner_test.rb +147 -0
  235. data/test/sass/util/subset_map_test.rb +91 -0
  236. data/test/sass/util_test.rb +313 -0
  237. data/test/test_helper.rb +80 -0
  238. metadata +348 -0
@@ -0,0 +1,2 @@
1
+ scss {
2
+ imported: yes; }
@@ -0,0 +1 @@
1
+ #pi { width: 314px; }
@@ -0,0 +1,3 @@
1
+ #nested { relative: true; }
2
+
3
+ #subdir { font-size: 20px; font-weight: bold; }
@@ -0,0 +1,11 @@
1
+ b {
2
+ foo: 5px;
3
+ bar: 24px;
4
+ baz: 66.66667%;
5
+ many-units: 32em;
6
+ mm: 15mm;
7
+ pc: 2pc;
8
+ pt: -72pt;
9
+ inches: 2in;
10
+ more-inches: 3.5in;
11
+ mixed: 2.04167in; }
File without changes
File without changes
@@ -0,0 +1,299 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+ require File.dirname(__FILE__) + '/../test_helper'
4
+ require 'sass/engine'
5
+
6
+ class SassScriptConversionTest < Test::Unit::TestCase
7
+ def test_bool
8
+ assert_renders "true"
9
+ assert_renders "false"
10
+ end
11
+
12
+ def test_color
13
+ assert_renders "#abcdef"
14
+ assert_renders "blue"
15
+ assert_renders "rgba(0, 1, 2, 0.2)"
16
+
17
+ assert_equal "#aabbcc", render("#abc")
18
+ assert_equal "blue", render("#0000ff")
19
+ end
20
+
21
+ def test_number
22
+ assert_renders "10"
23
+ assert_renders "10.35"
24
+ assert_renders "12px"
25
+ assert_renders "12.45px"
26
+
27
+ assert_equal "12.34568", render("12.345678901")
28
+ end
29
+
30
+ def test_string
31
+ assert_renders '"foo"'
32
+ assert_renders '"bar baz"'
33
+ assert_equal '"baz bang"', render("'baz bang'")
34
+ end
35
+
36
+ def test_string_quotes
37
+ assert_equal "'quote\"quote'", render('"quote\\"quote"')
38
+ assert_equal '"quote\'quote"', render("'quote\\'quote'")
39
+ assert_renders '"quote\'quote\\"quote"'
40
+ assert_equal '"quote\'quote\\"quote"', render("'quote\\'quote\"quote'")
41
+ end
42
+
43
+ def test_string_escapes
44
+ assert_renders '"foo\\\\bar"'
45
+ end
46
+
47
+ def test_funcall
48
+ assert_renders "foo(true, blue)"
49
+ assert_renders "hsla(20deg, 30%, 50%, 0.3)"
50
+ assert_renders "blam()"
51
+
52
+ assert_renders "-\xC3\xBFoo(12px)"
53
+ assert_renders "-foo(12px)"
54
+ end
55
+
56
+ def test_funcall_with_keyword_args
57
+ assert_renders "foo(arg1, arg2, $karg1: val, $karg2: val2)"
58
+ assert_renders "foo($karg1: val, $karg2: val2)"
59
+ end
60
+
61
+ def test_url
62
+ assert_renders "url(foo.gif)"
63
+ assert_renders "url($var)"
64
+ assert_renders "url(\#{$var}/flip.gif)"
65
+ end
66
+
67
+ def test_variable
68
+ assert_renders "$foo-bar"
69
+ assert_renders "$flaznicate"
70
+ end
71
+
72
+ def test_null
73
+ assert_renders "null"
74
+ end
75
+
76
+ def test_empty_list
77
+ assert_renders "()"
78
+ end
79
+
80
+ def test_list_in_args
81
+ assert_renders "foo((a, b, c))"
82
+ assert_renders "foo($arg: (a, b, c))"
83
+ assert_renders "foo(a, b, (a, b, c)...)"
84
+ end
85
+
86
+ def self.test_precedence(outer, inner)
87
+ op_outer = Sass::Script::Lexer::OPERATORS_REVERSE[outer]
88
+ op_inner = Sass::Script::Lexer::OPERATORS_REVERSE[inner]
89
+ class_eval <<RUBY
90
+ def test_precedence_#{outer}_#{inner}
91
+ assert_renders "$foo #{op_outer} $bar #{op_inner} $baz"
92
+ assert_renders "$foo #{op_inner} $bar #{op_outer} $baz"
93
+
94
+ assert_renders "($foo #{op_outer} $bar) #{op_inner} $baz"
95
+ assert_renders "$foo #{op_inner} ($bar #{op_outer} $baz)"
96
+
97
+ assert_equal "$foo #{op_outer} $bar #{op_inner} $baz",
98
+ render("$foo #{op_outer} ($bar #{op_inner} $baz)")
99
+ assert_equal "$foo #{op_inner} $bar #{op_outer} $baz",
100
+ render("($foo #{op_inner} $bar) #{op_outer} $baz")
101
+ end
102
+ RUBY
103
+ end
104
+
105
+ def self.assert_associative(op_name, sibling_name)
106
+ op = separator_for(op_name)
107
+ sibling = separator_for(sibling_name)
108
+ class_eval <<RUBY
109
+ def test_associative_#{op_name}_#{sibling_name}
110
+ assert_renders "$foo#{op}$bar#{op}$baz"
111
+
112
+ assert_equal "$foo#{op}$bar#{op}$baz",
113
+ render("$foo#{op}($bar#{op}$baz)")
114
+ assert_equal "$foo#{op}$bar#{op}$baz",
115
+ render("($foo#{op}$bar)#{op}$baz")
116
+
117
+ assert_equal "$foo#{op}$bar#{sibling}$baz",
118
+ render("$foo#{op}($bar#{sibling}$baz)")
119
+ assert_equal "$foo#{sibling}$bar#{op}$baz",
120
+ render("($foo#{sibling}$bar)#{op}$baz")
121
+ end
122
+ RUBY
123
+ end
124
+
125
+ def self.separator_for(op_name)
126
+ case op_name
127
+ when :comma; ", "
128
+ when :space; " "
129
+ else; " #{Sass::Script::Lexer::OPERATORS_REVERSE[op_name]} "
130
+ end
131
+ end
132
+
133
+ def self.assert_non_associative(op_name, sibling_name)
134
+ op = Sass::Script::Lexer::OPERATORS_REVERSE[op_name]
135
+ sibling = Sass::Script::Lexer::OPERATORS_REVERSE[sibling_name]
136
+ class_eval <<RUBY
137
+ def test_non_associative_#{op_name}_#{sibling_name}
138
+ assert_renders "$foo #{op} $bar #{op} $baz"
139
+
140
+ assert_renders "$foo #{op} ($bar #{op} $baz)"
141
+ assert_equal "$foo #{op} $bar #{op} $baz",
142
+ render("($foo #{op} $bar) #{op} $baz")
143
+
144
+ assert_renders "$foo #{op} ($bar #{sibling} $baz)"
145
+ assert_equal "$foo #{sibling} $bar #{op} $baz",
146
+ render("($foo #{sibling} $bar) #{op} $baz")
147
+ end
148
+ RUBY
149
+ end
150
+
151
+ test_precedence :or, :and
152
+ test_precedence :and, :eq
153
+ test_precedence :and, :neq
154
+ test_precedence :eq, :gt
155
+ test_precedence :eq, :gte
156
+ test_precedence :eq, :lt
157
+ test_precedence :eq, :lte
158
+ test_precedence :gt, :plus
159
+ test_precedence :gt, :minus
160
+ test_precedence :plus, :times
161
+ test_precedence :plus, :div
162
+ test_precedence :plus, :mod
163
+
164
+ assert_associative :plus, :minus
165
+ assert_associative :times, :div
166
+ assert_associative :times, :mod
167
+
168
+ assert_non_associative :minus, :plus
169
+ assert_non_associative :div, :times
170
+ assert_non_associative :mod, :times
171
+ assert_non_associative :gt, :gte
172
+ assert_non_associative :gte, :lt
173
+ assert_non_associative :lt, :lte
174
+ assert_non_associative :lte, :gt
175
+
176
+ def test_comma_precedence
177
+ assert_renders "$foo, $bar, $baz"
178
+
179
+ assert_renders "$foo ($bar, $baz)"
180
+ assert_renders "($foo, $bar) $baz"
181
+
182
+ assert_equal "$foo, $bar $baz", render("$foo, ($bar $baz)")
183
+ assert_equal "$foo $bar, $baz", render("($foo $bar), $baz")
184
+
185
+ assert_equal "$foo, ($bar, $baz)", render("$foo, ($bar, $baz)")
186
+ assert_equal "($foo, $bar), $baz", render("($foo, $bar), $baz")
187
+ end
188
+
189
+ def test_space_precedence
190
+ assert_renders "$foo $bar $baz"
191
+
192
+ assert_renders "$foo or ($bar $baz)"
193
+ assert_renders "($foo $bar) or $baz"
194
+
195
+ assert_equal "$foo $bar or $baz", render("$foo ($bar or $baz)")
196
+ assert_equal "$foo or $bar $baz", render("($foo or $bar) $baz")
197
+
198
+ assert_equal "$foo ($bar $baz)", render("$foo ($bar $baz)")
199
+ assert_equal "($foo $bar) $baz", render("($foo $bar) $baz")
200
+ end
201
+
202
+ def test_unary_op
203
+ assert_renders "-12px"
204
+ assert_renders '/"foo"'
205
+ assert_renders 'not true'
206
+
207
+ assert_renders "-(foo(12px))"
208
+ assert_renders "-(-foo(12px))"
209
+ assert_renders "-(_foo(12px))"
210
+ assert_renders "-(\xC3\xBFoo(12px))"
211
+ assert_renders "-(blue)"
212
+
213
+ assert_equal 'not true or false', render('(not true) or false')
214
+ assert_equal 'not (true or false)', render('not (true or false)')
215
+ end
216
+
217
+ def test_interpolation
218
+ assert_renders "$foo\#{$bar}$baz"
219
+ assert_renders "$foo\#{$bar} $baz"
220
+ assert_renders "$foo \#{$bar}$baz"
221
+ assert_renders "$foo \#{$bar} $baz"
222
+ assert_renders "$foo \#{$bar}\#{$bang} $baz"
223
+ assert_renders "$foo \#{$bar} \#{$bang} $baz"
224
+ assert_renders "\#{$bar}$baz"
225
+ assert_renders "$foo\#{$bar}"
226
+ assert_renders "\#{$bar}"
227
+ end
228
+
229
+ def test_interpolation_in_function
230
+ assert_renders 'flabnabbit(#{1 + "foo"})'
231
+ assert_renders 'flabnabbit($foo #{1 + "foo"}$baz)'
232
+ assert_renders 'flabnabbit($foo #{1 + "foo"}#{2 + "bar"} $baz)'
233
+ end
234
+
235
+ def test_interpolation_near_operators
236
+ assert_renders '#{1 + 2} , #{3 + 4}'
237
+ assert_renders '#{1 + 2}, #{3 + 4}'
238
+ assert_renders '#{1 + 2} ,#{3 + 4}'
239
+ assert_renders '#{1 + 2},#{3 + 4}'
240
+ assert_renders '#{1 + 2}, #{3 + 4}, #{5 + 6}'
241
+ assert_renders '3, #{3 + 4}, 11'
242
+
243
+ assert_renders '3 / #{3 + 4}'
244
+ assert_renders '3 /#{3 + 4}'
245
+ assert_renders '3/ #{3 + 4}'
246
+ assert_renders '3/#{3 + 4}'
247
+
248
+ assert_renders '#{1 + 2} * 7'
249
+ assert_renders '#{1 + 2}* 7'
250
+ assert_renders '#{1 + 2} *7'
251
+ assert_renders '#{1 + 2}*7'
252
+
253
+ assert_renders '-#{1 + 2}'
254
+ assert_renders '- #{1 + 2}'
255
+
256
+ assert_renders '5 + #{1 + 2} * #{3 + 4}'
257
+ assert_renders '5 +#{1 + 2} * #{3 + 4}'
258
+ assert_renders '5+#{1 + 2} * #{3 + 4}'
259
+ assert_renders '#{1 + 2} * #{3 + 4} + 5'
260
+ assert_renders '#{1 + 2} * #{3 + 4}+ 5'
261
+ assert_renders '#{1 + 2} * #{3 + 4}+5'
262
+
263
+ assert_equal '5 / #{1 + 2} + #{3 + 4}', render('5 / (#{1 + 2} + #{3 + 4})')
264
+ assert_equal '5 / #{1 + 2} + #{3 + 4}', render('5 /(#{1 + 2} + #{3 + 4})')
265
+ assert_equal '5 / #{1 + 2} + #{3 + 4}', render('5 /( #{1 + 2} + #{3 + 4} )')
266
+ assert_equal '#{1 + 2} + #{3 + 4} / 5', render('(#{1 + 2} + #{3 + 4}) / 5')
267
+ assert_equal '#{1 + 2} + #{3 + 4} / 5', render('(#{1 + 2} + #{3 + 4})/ 5')
268
+ assert_equal '#{1 + 2} + #{3 + 4} / 5', render('( #{1 + 2} + #{3 + 4} )/ 5')
269
+
270
+ assert_renders '#{1 + 2} + 2 + 3'
271
+ assert_renders '#{1 + 2} +2 + 3'
272
+ end
273
+
274
+ def test_string_interpolation
275
+ assert_renders '"foo#{$bar}baz"'
276
+ assert_renders '"foo #{$bar}baz"'
277
+ assert_renders '"foo#{$bar} baz"'
278
+ assert_renders '"foo #{$bar} baz"'
279
+ assert_renders '"foo #{$bar}#{$bang} baz"'
280
+ assert_renders '"foo #{$bar} #{$bang} baz"'
281
+ assert_renders '"#{$bar}baz"'
282
+ assert_renders '"foo#{$bar}"'
283
+ assert_equal '#{$bar}', render('"#{$bar}"')
284
+
285
+ assert_equal '"foo#{$bar}baz"', render("'foo\#{$bar}baz'")
286
+ end
287
+
288
+ private
289
+
290
+ def assert_renders(script, options = {})
291
+ assert_equal(script, render(script, options))
292
+ end
293
+
294
+ def render(script, options = {})
295
+ munge_filename(options)
296
+ node = Sass::Script.parse(script, 1, 0, options)
297
+ node.to_sass
298
+ end
299
+ end
@@ -0,0 +1,591 @@
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_funcall_has_higher_precedence_than_color_name
514
+ assert_equal "teal(12)", resolve("teal(12)")
515
+ assert_equal "tealbang(12)", resolve("tealbang(12)")
516
+ assert_equal "teal-bang(12)", resolve("teal-bang(12)")
517
+ assert_equal "teal\\+bang(12)", resolve("teal\\+bang(12)")
518
+ end
519
+
520
+ def test_interpolation_after_hash
521
+ assert_equal "#2", resolve('"##{1 + 1}"')
522
+ end
523
+
524
+ def test_misplaced_comma_in_funcall
525
+ assert_raise_message(Sass::SyntaxError,
526
+ 'Invalid CSS after "foo(bar, ": expected function argument, was ")"') {eval('foo(bar, )')}
527
+ end
528
+
529
+ def test_color_prefixed_identifier
530
+ assert_equal "tealbang", resolve("tealbang")
531
+ assert_equal "teal-bang", resolve("teal-bang")
532
+ end
533
+
534
+ def test_op_prefixed_identifier
535
+ assert_equal "notbang", resolve("notbang")
536
+ assert_equal "not-bang", resolve("not-bang")
537
+ assert_equal "or-bang", resolve("or-bang")
538
+ assert_equal "and-bang", resolve("and-bang")
539
+ end
540
+
541
+ private
542
+
543
+ def resolve(str, opts = {}, environment = env)
544
+ munge_filename opts
545
+ val = eval(str, opts, environment)
546
+ assert_kind_of Sass::Script::Literal, val
547
+ val.is_a?(Sass::Script::String) ? val.value : val.to_s
548
+ end
549
+
550
+ def assert_unquoted(str, opts = {}, environment = env)
551
+ munge_filename opts
552
+ val = eval(str, opts, environment)
553
+ assert_kind_of Sass::Script::String, val
554
+ assert_equal :identifier, val.type
555
+ end
556
+
557
+ def assert_quoted(str, opts = {}, environment = env)
558
+ munge_filename opts
559
+ val = eval(str, opts, environment)
560
+ assert_kind_of Sass::Script::String, val
561
+ assert_equal :string, val.type
562
+ end
563
+
564
+ def eval(str, opts = {}, environment = env)
565
+ munge_filename opts
566
+ Sass::Script.parse(str, opts.delete(:line) || 1,
567
+ opts.delete(:offset) || 0, opts).
568
+ perform(Sass::Environment.new(environment, opts))
569
+ end
570
+
571
+ def render(sass, options = {})
572
+ munge_filename options
573
+ Sass::Engine.new(sass, options).render
574
+ end
575
+
576
+ def env(hash = {})
577
+ env = Sass::Environment.new
578
+ hash.each {|k, v| env.set_var(k, v)}
579
+ env
580
+ end
581
+
582
+ def test_number_printing
583
+ assert_equal "1", eval("1")
584
+ assert_equal "1", eval("1.0")
585
+ assert_equal "1.121", eval("1.1214")
586
+ assert_equal "1.122", eval("1.1215")
587
+ assert_equal "Infinity", eval("1.0/0.0")
588
+ assert_equal "-Infinity", eval("-1.0/0.0")
589
+ assert_equal "NaN", eval("0.0/0.0")
590
+ end
591
+ end