oreorenasass 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (268) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +11 -0
  3. data/CONTRIBUTING +3 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.md +221 -0
  6. data/Rakefile +370 -0
  7. data/VERSION +1 -0
  8. data/VERSION_NAME +1 -0
  9. data/bin/sass +13 -0
  10. data/bin/sass-convert +12 -0
  11. data/bin/scss +13 -0
  12. data/extra/update_watch.rb +13 -0
  13. data/init.rb +18 -0
  14. data/lib/sass/cache_stores/base.rb +88 -0
  15. data/lib/sass/cache_stores/chain.rb +34 -0
  16. data/lib/sass/cache_stores/filesystem.rb +60 -0
  17. data/lib/sass/cache_stores/memory.rb +47 -0
  18. data/lib/sass/cache_stores/null.rb +25 -0
  19. data/lib/sass/cache_stores.rb +15 -0
  20. data/lib/sass/callbacks.rb +67 -0
  21. data/lib/sass/css.rb +407 -0
  22. data/lib/sass/engine.rb +1181 -0
  23. data/lib/sass/environment.rb +191 -0
  24. data/lib/sass/error.rb +198 -0
  25. data/lib/sass/exec/base.rb +187 -0
  26. data/lib/sass/exec/sass_convert.rb +264 -0
  27. data/lib/sass/exec/sass_scss.rb +424 -0
  28. data/lib/sass/exec.rb +9 -0
  29. data/lib/sass/features.rb +47 -0
  30. data/lib/sass/importers/base.rb +182 -0
  31. data/lib/sass/importers/filesystem.rb +211 -0
  32. data/lib/sass/importers.rb +22 -0
  33. data/lib/sass/logger/base.rb +30 -0
  34. data/lib/sass/logger/log_level.rb +45 -0
  35. data/lib/sass/logger.rb +12 -0
  36. data/lib/sass/media.rb +210 -0
  37. data/lib/sass/plugin/compiler.rb +565 -0
  38. data/lib/sass/plugin/configuration.rb +118 -0
  39. data/lib/sass/plugin/generic.rb +15 -0
  40. data/lib/sass/plugin/merb.rb +48 -0
  41. data/lib/sass/plugin/rack.rb +60 -0
  42. data/lib/sass/plugin/rails.rb +47 -0
  43. data/lib/sass/plugin/staleness_checker.rb +199 -0
  44. data/lib/sass/plugin.rb +133 -0
  45. data/lib/sass/railtie.rb +10 -0
  46. data/lib/sass/repl.rb +57 -0
  47. data/lib/sass/root.rb +7 -0
  48. data/lib/sass/script/css_lexer.rb +33 -0
  49. data/lib/sass/script/css_parser.rb +34 -0
  50. data/lib/sass/script/functions.rb +2626 -0
  51. data/lib/sass/script/lexer.rb +449 -0
  52. data/lib/sass/script/parser.rb +637 -0
  53. data/lib/sass/script/tree/funcall.rb +306 -0
  54. data/lib/sass/script/tree/interpolation.rb +118 -0
  55. data/lib/sass/script/tree/list_literal.rb +77 -0
  56. data/lib/sass/script/tree/literal.rb +45 -0
  57. data/lib/sass/script/tree/map_literal.rb +64 -0
  58. data/lib/sass/script/tree/node.rb +109 -0
  59. data/lib/sass/script/tree/operation.rb +103 -0
  60. data/lib/sass/script/tree/selector.rb +26 -0
  61. data/lib/sass/script/tree/string_interpolation.rb +104 -0
  62. data/lib/sass/script/tree/unary_operation.rb +69 -0
  63. data/lib/sass/script/tree/variable.rb +57 -0
  64. data/lib/sass/script/tree.rb +16 -0
  65. data/lib/sass/script/value/arg_list.rb +36 -0
  66. data/lib/sass/script/value/base.rb +240 -0
  67. data/lib/sass/script/value/bool.rb +35 -0
  68. data/lib/sass/script/value/color.rb +680 -0
  69. data/lib/sass/script/value/helpers.rb +262 -0
  70. data/lib/sass/script/value/list.rb +113 -0
  71. data/lib/sass/script/value/map.rb +70 -0
  72. data/lib/sass/script/value/null.rb +44 -0
  73. data/lib/sass/script/value/number.rb +530 -0
  74. data/lib/sass/script/value/string.rb +97 -0
  75. data/lib/sass/script/value.rb +11 -0
  76. data/lib/sass/script.rb +66 -0
  77. data/lib/sass/scss/css_parser.rb +42 -0
  78. data/lib/sass/scss/parser.rb +1209 -0
  79. data/lib/sass/scss/rx.rb +141 -0
  80. data/lib/sass/scss/script_lexer.rb +15 -0
  81. data/lib/sass/scss/script_parser.rb +25 -0
  82. data/lib/sass/scss/static_parser.rb +368 -0
  83. data/lib/sass/scss.rb +16 -0
  84. data/lib/sass/selector/abstract_sequence.rb +109 -0
  85. data/lib/sass/selector/comma_sequence.rb +175 -0
  86. data/lib/sass/selector/pseudo.rb +256 -0
  87. data/lib/sass/selector/sequence.rb +600 -0
  88. data/lib/sass/selector/simple.rb +117 -0
  89. data/lib/sass/selector/simple_sequence.rb +325 -0
  90. data/lib/sass/selector.rb +326 -0
  91. data/lib/sass/shared.rb +76 -0
  92. data/lib/sass/source/map.rb +210 -0
  93. data/lib/sass/source/position.rb +39 -0
  94. data/lib/sass/source/range.rb +41 -0
  95. data/lib/sass/stack.rb +120 -0
  96. data/lib/sass/supports.rb +227 -0
  97. data/lib/sass/tree/at_root_node.rb +83 -0
  98. data/lib/sass/tree/charset_node.rb +22 -0
  99. data/lib/sass/tree/comment_node.rb +82 -0
  100. data/lib/sass/tree/content_node.rb +9 -0
  101. data/lib/sass/tree/css_import_node.rb +60 -0
  102. data/lib/sass/tree/debug_node.rb +18 -0
  103. data/lib/sass/tree/directive_node.rb +59 -0
  104. data/lib/sass/tree/each_node.rb +24 -0
  105. data/lib/sass/tree/error_node.rb +18 -0
  106. data/lib/sass/tree/extend_node.rb +43 -0
  107. data/lib/sass/tree/for_node.rb +36 -0
  108. data/lib/sass/tree/function_node.rb +39 -0
  109. data/lib/sass/tree/if_node.rb +52 -0
  110. data/lib/sass/tree/import_node.rb +74 -0
  111. data/lib/sass/tree/keyframe_rule_node.rb +15 -0
  112. data/lib/sass/tree/media_node.rb +48 -0
  113. data/lib/sass/tree/mixin_def_node.rb +38 -0
  114. data/lib/sass/tree/mixin_node.rb +52 -0
  115. data/lib/sass/tree/node.rb +238 -0
  116. data/lib/sass/tree/prop_node.rb +171 -0
  117. data/lib/sass/tree/return_node.rb +19 -0
  118. data/lib/sass/tree/root_node.rb +44 -0
  119. data/lib/sass/tree/rule_node.rb +145 -0
  120. data/lib/sass/tree/supports_node.rb +38 -0
  121. data/lib/sass/tree/trace_node.rb +33 -0
  122. data/lib/sass/tree/variable_node.rb +36 -0
  123. data/lib/sass/tree/visitors/base.rb +72 -0
  124. data/lib/sass/tree/visitors/check_nesting.rb +177 -0
  125. data/lib/sass/tree/visitors/convert.rb +334 -0
  126. data/lib/sass/tree/visitors/cssize.rb +369 -0
  127. data/lib/sass/tree/visitors/deep_copy.rb +107 -0
  128. data/lib/sass/tree/visitors/extend.rb +68 -0
  129. data/lib/sass/tree/visitors/perform.rb +539 -0
  130. data/lib/sass/tree/visitors/set_options.rb +139 -0
  131. data/lib/sass/tree/visitors/to_css.rb +381 -0
  132. data/lib/sass/tree/warn_node.rb +18 -0
  133. data/lib/sass/tree/while_node.rb +18 -0
  134. data/lib/sass/util/cross_platform_random.rb +19 -0
  135. data/lib/sass/util/multibyte_string_scanner.rb +157 -0
  136. data/lib/sass/util/normalized_map.rb +130 -0
  137. data/lib/sass/util/ordered_hash.rb +192 -0
  138. data/lib/sass/util/subset_map.rb +110 -0
  139. data/lib/sass/util/test.rb +9 -0
  140. data/lib/sass/util.rb +1318 -0
  141. data/lib/sass/version.rb +124 -0
  142. data/lib/sass.rb +102 -0
  143. data/rails/init.rb +1 -0
  144. data/test/sass/cache_test.rb +131 -0
  145. data/test/sass/callbacks_test.rb +61 -0
  146. data/test/sass/compiler_test.rb +232 -0
  147. data/test/sass/conversion_test.rb +2054 -0
  148. data/test/sass/css2sass_test.rb +477 -0
  149. data/test/sass/data/hsl-rgb.txt +319 -0
  150. data/test/sass/encoding_test.rb +219 -0
  151. data/test/sass/engine_test.rb +3301 -0
  152. data/test/sass/exec_test.rb +86 -0
  153. data/test/sass/extend_test.rb +1661 -0
  154. data/test/sass/fixtures/test_staleness_check_across_importers.css +1 -0
  155. data/test/sass/fixtures/test_staleness_check_across_importers.scss +1 -0
  156. data/test/sass/functions_test.rb +1926 -0
  157. data/test/sass/importer_test.rb +412 -0
  158. data/test/sass/logger_test.rb +58 -0
  159. data/test/sass/mock_importer.rb +49 -0
  160. data/test/sass/more_results/more1.css +9 -0
  161. data/test/sass/more_results/more1_with_line_comments.css +26 -0
  162. data/test/sass/more_results/more_import.css +29 -0
  163. data/test/sass/more_templates/_more_partial.sass +2 -0
  164. data/test/sass/more_templates/more1.sass +23 -0
  165. data/test/sass/more_templates/more_import.sass +11 -0
  166. data/test/sass/plugin_test.rb +554 -0
  167. data/test/sass/results/alt.css +4 -0
  168. data/test/sass/results/basic.css +9 -0
  169. data/test/sass/results/cached_import_option.css +3 -0
  170. data/test/sass/results/compact.css +5 -0
  171. data/test/sass/results/complex.css +86 -0
  172. data/test/sass/results/compressed.css +1 -0
  173. data/test/sass/results/expanded.css +19 -0
  174. data/test/sass/results/filename_fn.css +3 -0
  175. data/test/sass/results/if.css +3 -0
  176. data/test/sass/results/import.css +31 -0
  177. data/test/sass/results/import_charset.css +5 -0
  178. data/test/sass/results/import_charset_1_8.css +5 -0
  179. data/test/sass/results/import_charset_ibm866.css +5 -0
  180. data/test/sass/results/import_content.css +1 -0
  181. data/test/sass/results/line_numbers.css +49 -0
  182. data/test/sass/results/mixins.css +95 -0
  183. data/test/sass/results/multiline.css +24 -0
  184. data/test/sass/results/nested.css +22 -0
  185. data/test/sass/results/options.css +1 -0
  186. data/test/sass/results/parent_ref.css +13 -0
  187. data/test/sass/results/script.css +16 -0
  188. data/test/sass/results/scss_import.css +31 -0
  189. data/test/sass/results/scss_importee.css +2 -0
  190. data/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
  191. data/test/sass/results/subdir/subdir.css +3 -0
  192. data/test/sass/results/units.css +11 -0
  193. data/test/sass/results/warn.css +0 -0
  194. data/test/sass/results/warn_imported.css +0 -0
  195. data/test/sass/script_conversion_test.rb +328 -0
  196. data/test/sass/script_test.rb +1054 -0
  197. data/test/sass/scss/css_test.rb +1215 -0
  198. data/test/sass/scss/rx_test.rb +156 -0
  199. data/test/sass/scss/scss_test.rb +3900 -0
  200. data/test/sass/scss/test_helper.rb +37 -0
  201. data/test/sass/source_map_test.rb +977 -0
  202. data/test/sass/superselector_test.rb +191 -0
  203. data/test/sass/templates/_cached_import_option_partial.scss +1 -0
  204. data/test/sass/templates/_double_import_loop2.sass +1 -0
  205. data/test/sass/templates/_filename_fn_import.scss +11 -0
  206. data/test/sass/templates/_imported_charset_ibm866.sass +4 -0
  207. data/test/sass/templates/_imported_charset_utf8.sass +4 -0
  208. data/test/sass/templates/_imported_content.sass +3 -0
  209. data/test/sass/templates/_partial.sass +2 -0
  210. data/test/sass/templates/_same_name_different_partiality.scss +1 -0
  211. data/test/sass/templates/alt.sass +16 -0
  212. data/test/sass/templates/basic.sass +23 -0
  213. data/test/sass/templates/bork1.sass +2 -0
  214. data/test/sass/templates/bork2.sass +2 -0
  215. data/test/sass/templates/bork3.sass +2 -0
  216. data/test/sass/templates/bork4.sass +2 -0
  217. data/test/sass/templates/bork5.sass +3 -0
  218. data/test/sass/templates/cached_import_option.scss +3 -0
  219. data/test/sass/templates/compact.sass +17 -0
  220. data/test/sass/templates/complex.sass +305 -0
  221. data/test/sass/templates/compressed.sass +15 -0
  222. data/test/sass/templates/double_import_loop1.sass +1 -0
  223. data/test/sass/templates/expanded.sass +17 -0
  224. data/test/sass/templates/filename_fn.scss +18 -0
  225. data/test/sass/templates/if.sass +11 -0
  226. data/test/sass/templates/import.sass +12 -0
  227. data/test/sass/templates/import_charset.sass +9 -0
  228. data/test/sass/templates/import_charset_1_8.sass +6 -0
  229. data/test/sass/templates/import_charset_ibm866.sass +11 -0
  230. data/test/sass/templates/import_content.sass +4 -0
  231. data/test/sass/templates/importee.less +2 -0
  232. data/test/sass/templates/importee.sass +19 -0
  233. data/test/sass/templates/line_numbers.sass +13 -0
  234. data/test/sass/templates/mixin_bork.sass +5 -0
  235. data/test/sass/templates/mixins.sass +76 -0
  236. data/test/sass/templates/multiline.sass +20 -0
  237. data/test/sass/templates/nested.sass +25 -0
  238. data/test/sass/templates/nested_bork1.sass +2 -0
  239. data/test/sass/templates/nested_bork2.sass +2 -0
  240. data/test/sass/templates/nested_bork3.sass +2 -0
  241. data/test/sass/templates/nested_bork4.sass +2 -0
  242. data/test/sass/templates/nested_import.sass +2 -0
  243. data/test/sass/templates/nested_mixin_bork.sass +6 -0
  244. data/test/sass/templates/options.sass +2 -0
  245. data/test/sass/templates/parent_ref.sass +25 -0
  246. data/test/sass/templates/same_name_different_ext.sass +2 -0
  247. data/test/sass/templates/same_name_different_ext.scss +1 -0
  248. data/test/sass/templates/same_name_different_partiality.scss +1 -0
  249. data/test/sass/templates/script.sass +101 -0
  250. data/test/sass/templates/scss_import.scss +12 -0
  251. data/test/sass/templates/scss_importee.scss +1 -0
  252. data/test/sass/templates/single_import_loop.sass +1 -0
  253. data/test/sass/templates/subdir/import_up1.scss +1 -0
  254. data/test/sass/templates/subdir/import_up2.scss +1 -0
  255. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
  256. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
  257. data/test/sass/templates/subdir/subdir.sass +6 -0
  258. data/test/sass/templates/units.sass +11 -0
  259. data/test/sass/templates/warn.sass +3 -0
  260. data/test/sass/templates/warn_imported.sass +4 -0
  261. data/test/sass/test_helper.rb +8 -0
  262. data/test/sass/util/multibyte_string_scanner_test.rb +147 -0
  263. data/test/sass/util/normalized_map_test.rb +51 -0
  264. data/test/sass/util/subset_map_test.rb +91 -0
  265. data/test/sass/util_test.rb +467 -0
  266. data/test/sass/value_helpers_test.rb +179 -0
  267. data/test/test_helper.rb +109 -0
  268. metadata +386 -0
@@ -0,0 +1,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,328 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+ require File.dirname(__FILE__) + '/../test_helper'
4
+ require 'sass/engine'
5
+
6
+ class SassScriptConversionTest < MiniTest::Test
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
+ assert_renders "#abc"
17
+ assert_renders "#0000ff"
18
+ end
19
+
20
+ def test_number
21
+ assert_renders "10"
22
+ assert_renders "10.35"
23
+ assert_renders "12px"
24
+ assert_renders "12.45px"
25
+
26
+ assert_equal "12.34568", render("12.345678901")
27
+ end
28
+
29
+ def test_string
30
+ assert_renders '"foo"'
31
+ assert_renders '"bar baz"'
32
+ assert_equal '"baz bang"', render("'baz bang'")
33
+ end
34
+
35
+ def test_string_quotes
36
+ assert_equal "'quote\"quote'", render('"quote\\"quote"')
37
+ assert_equal '"quote\'quote"', render("'quote\\'quote'")
38
+ assert_renders '"quote\'quote\\"quote"'
39
+ assert_equal '"quote\'quote\\"quote"', render("'quote\\'quote\"quote'")
40
+ end
41
+
42
+ def test_string_escapes
43
+ assert_renders '"foo\\\\bar"'
44
+ end
45
+
46
+ def test_funcall
47
+ assert_renders "foo(true, blue)"
48
+ assert_renders "hsla(20deg, 30%, 50%, 0.3)"
49
+ assert_renders "blam()"
50
+
51
+ assert_renders "-\xC3\xBFoo(12px)"
52
+ assert_renders "-foo(12px)"
53
+ end
54
+
55
+ def test_funcall_with_keyword_args
56
+ assert_renders "foo(arg1, arg2, $karg1: val, $karg2: val2)"
57
+ assert_renders "foo($karg1: val, $karg2: val2)"
58
+ end
59
+
60
+ def test_funcall_with_hyphen_conversion_keyword_arg
61
+ assert_renders "foo($a-b_c: val)"
62
+ end
63
+
64
+ def test_url
65
+ assert_renders "url(foo.gif)"
66
+ assert_renders "url($var)"
67
+ assert_renders "url(\#{$var}/flip.gif)"
68
+ end
69
+
70
+ def test_variable
71
+ assert_renders "$foo-bar"
72
+ assert_renders "$flaznicate"
73
+ end
74
+
75
+ def test_null
76
+ assert_renders "null"
77
+ end
78
+
79
+ def test_empty_list
80
+ assert_renders "()"
81
+ end
82
+
83
+ def test_list_in_args
84
+ assert_renders "foo((a, b, c))"
85
+ assert_renders "foo($arg: (a, b, c))"
86
+ assert_renders "foo(a, b, (a, b, c)...)"
87
+ end
88
+
89
+ def test_singleton_list
90
+ assert_renders "(1,)"
91
+ assert_renders "(1 2 3,)"
92
+ assert_renders "((1, 2, 3),)"
93
+ end
94
+
95
+ def test_map
96
+ assert_renders "(foo: bar)"
97
+ assert_renders "(foo: bar, baz: bip)"
98
+ assert_renders "(foo: bar, baz: (bip: bap))"
99
+ end
100
+
101
+ def test_map_in_list
102
+ assert_renders "(foo: bar) baz"
103
+ assert_renders "(foo: bar), (baz: bip)"
104
+ end
105
+
106
+ def test_list_in_map
107
+ assert_renders "(foo: bar baz)"
108
+ assert_renders "(foo: (bar, baz), bip: bop)"
109
+ end
110
+
111
+ def test_selector
112
+ assert_renders "&"
113
+ end
114
+
115
+ def self.test_precedence(outer, inner)
116
+ op_outer = Sass::Script::Lexer::OPERATORS_REVERSE[outer]
117
+ op_inner = Sass::Script::Lexer::OPERATORS_REVERSE[inner]
118
+ class_eval <<RUBY
119
+ def test_precedence_#{outer}_#{inner}
120
+ assert_renders "$foo #{op_outer} $bar #{op_inner} $baz"
121
+ assert_renders "$foo #{op_inner} $bar #{op_outer} $baz"
122
+
123
+ assert_renders "($foo #{op_outer} $bar) #{op_inner} $baz"
124
+ assert_renders "$foo #{op_inner} ($bar #{op_outer} $baz)"
125
+
126
+ assert_equal "$foo #{op_outer} $bar #{op_inner} $baz",
127
+ render("$foo #{op_outer} ($bar #{op_inner} $baz)")
128
+ assert_equal "$foo #{op_inner} $bar #{op_outer} $baz",
129
+ render("($foo #{op_inner} $bar) #{op_outer} $baz")
130
+ end
131
+ RUBY
132
+ end
133
+
134
+ def self.assert_associative(op_name, sibling_name)
135
+ op = separator_for(op_name)
136
+ sibling = separator_for(sibling_name)
137
+ class_eval <<RUBY
138
+ def test_associative_#{op_name}_#{sibling_name}
139
+ assert_renders "$foo#{op}$bar#{op}$baz"
140
+
141
+ assert_equal "$foo#{op}$bar#{op}$baz",
142
+ render("$foo#{op}($bar#{op}$baz)")
143
+ assert_equal "$foo#{op}$bar#{op}$baz",
144
+ render("($foo#{op}$bar)#{op}$baz")
145
+
146
+ assert_equal "$foo#{op}$bar#{sibling}$baz",
147
+ render("$foo#{op}($bar#{sibling}$baz)")
148
+ assert_equal "$foo#{sibling}$bar#{op}$baz",
149
+ render("($foo#{sibling}$bar)#{op}$baz")
150
+ end
151
+ RUBY
152
+ end
153
+
154
+ def self.separator_for(op_name)
155
+ case op_name
156
+ when :comma; ", "
157
+ when :space; " "
158
+ else; " #{Sass::Script::Lexer::OPERATORS_REVERSE[op_name]} "
159
+ end
160
+ end
161
+
162
+ def self.assert_non_associative(op_name, sibling_name)
163
+ op = Sass::Script::Lexer::OPERATORS_REVERSE[op_name]
164
+ sibling = Sass::Script::Lexer::OPERATORS_REVERSE[sibling_name]
165
+ class_eval <<RUBY
166
+ def test_non_associative_#{op_name}_#{sibling_name}
167
+ assert_renders "$foo #{op} $bar #{op} $baz"
168
+
169
+ assert_renders "$foo #{op} ($bar #{op} $baz)"
170
+ assert_equal "$foo #{op} $bar #{op} $baz",
171
+ render("($foo #{op} $bar) #{op} $baz")
172
+
173
+ assert_renders "$foo #{op} ($bar #{sibling} $baz)"
174
+ assert_equal "$foo #{sibling} $bar #{op} $baz",
175
+ render("($foo #{sibling} $bar) #{op} $baz")
176
+ end
177
+ RUBY
178
+ end
179
+
180
+ test_precedence :or, :and
181
+ test_precedence :and, :eq
182
+ test_precedence :and, :neq
183
+ test_precedence :eq, :gt
184
+ test_precedence :eq, :gte
185
+ test_precedence :eq, :lt
186
+ test_precedence :eq, :lte
187
+ test_precedence :gt, :plus
188
+ test_precedence :gt, :minus
189
+ test_precedence :plus, :times
190
+ test_precedence :plus, :div
191
+ test_precedence :plus, :mod
192
+
193
+ assert_associative :plus, :minus
194
+ assert_associative :times, :div
195
+ assert_associative :times, :mod
196
+
197
+ assert_non_associative :minus, :plus
198
+ assert_non_associative :div, :times
199
+ assert_non_associative :mod, :times
200
+ assert_non_associative :gt, :gte
201
+ assert_non_associative :gte, :lt
202
+ assert_non_associative :lt, :lte
203
+ assert_non_associative :lte, :gt
204
+
205
+ def test_comma_precedence
206
+ assert_renders "$foo, $bar, $baz"
207
+
208
+ assert_renders "$foo ($bar, $baz)"
209
+ assert_renders "($foo, $bar) $baz"
210
+
211
+ assert_equal "$foo, $bar $baz", render("$foo, ($bar $baz)")
212
+ assert_equal "$foo $bar, $baz", render("($foo $bar), $baz")
213
+
214
+ assert_equal "$foo, ($bar, $baz)", render("$foo, ($bar, $baz)")
215
+ assert_equal "($foo, $bar), $baz", render("($foo, $bar), $baz")
216
+ end
217
+
218
+ def test_space_precedence
219
+ assert_renders "$foo $bar $baz"
220
+
221
+ assert_renders "$foo or ($bar $baz)"
222
+ assert_renders "($foo $bar) or $baz"
223
+
224
+ assert_equal "$foo $bar or $baz", render("$foo ($bar or $baz)")
225
+ assert_equal "$foo or $bar $baz", render("($foo or $bar) $baz")
226
+
227
+ assert_equal "$foo ($bar $baz)", render("$foo ($bar $baz)")
228
+ assert_equal "($foo $bar) $baz", render("($foo $bar) $baz")
229
+ end
230
+
231
+ def test_unary_op
232
+ assert_renders "-12px"
233
+ assert_renders '/"foo"'
234
+ assert_renders 'not true'
235
+
236
+ assert_renders "-(foo(12px))"
237
+ assert_renders "-(-foo(12px))"
238
+ assert_renders "-(_foo(12px))"
239
+ assert_renders "-(\xC3\xBFoo(12px))"
240
+ assert_renders "-(blue)"
241
+
242
+ assert_equal 'not true or false', render('(not true) or false')
243
+ assert_equal 'not (true or false)', render('not (true or false)')
244
+ end
245
+
246
+ def test_interpolation
247
+ assert_renders "$foo\#{$bar}$baz"
248
+ assert_renders "$foo\#{$bar} $baz"
249
+ assert_renders "$foo \#{$bar}$baz"
250
+ assert_renders "$foo \#{$bar} $baz"
251
+ assert_renders "$foo \#{$bar}\#{$bang} $baz"
252
+ assert_renders "$foo \#{$bar} \#{$bang} $baz"
253
+ assert_renders "\#{$bar}$baz"
254
+ assert_renders "$foo\#{$bar}"
255
+ assert_renders "\#{$bar}"
256
+ end
257
+
258
+ def test_interpolation_in_function
259
+ assert_renders 'flabnabbit(#{1 + "foo"})'
260
+ assert_renders 'flabnabbit($foo #{1 + "foo"}$baz)'
261
+ assert_renders 'flabnabbit($foo #{1 + "foo"}#{2 + "bar"} $baz)'
262
+ end
263
+
264
+ def test_interpolation_near_operators
265
+ assert_renders '#{1 + 2} , #{3 + 4}'
266
+ assert_renders '#{1 + 2}, #{3 + 4}'
267
+ assert_renders '#{1 + 2} ,#{3 + 4}'
268
+ assert_renders '#{1 + 2},#{3 + 4}'
269
+ assert_renders '#{1 + 2}, #{3 + 4}, #{5 + 6}'
270
+ assert_renders '3, #{3 + 4}, 11'
271
+
272
+ assert_renders '3 / #{3 + 4}'
273
+ assert_renders '3 /#{3 + 4}'
274
+ assert_renders '3/ #{3 + 4}'
275
+ assert_renders '3/#{3 + 4}'
276
+
277
+ assert_renders '#{1 + 2} * 7'
278
+ assert_renders '#{1 + 2}* 7'
279
+ assert_renders '#{1 + 2} *7'
280
+ assert_renders '#{1 + 2}*7'
281
+
282
+ assert_renders '-#{1 + 2}'
283
+ assert_renders '- #{1 + 2}'
284
+
285
+ assert_renders '5 + #{1 + 2} * #{3 + 4}'
286
+ assert_renders '5 +#{1 + 2} * #{3 + 4}'
287
+ assert_renders '5+#{1 + 2} * #{3 + 4}'
288
+ assert_renders '#{1 + 2} * #{3 + 4} + 5'
289
+ assert_renders '#{1 + 2} * #{3 + 4}+ 5'
290
+ assert_renders '#{1 + 2} * #{3 + 4}+5'
291
+
292
+ assert_equal '5 / #{1 + 2} + #{3 + 4}', render('5 / (#{1 + 2} + #{3 + 4})')
293
+ assert_equal '5 / #{1 + 2} + #{3 + 4}', render('5 /(#{1 + 2} + #{3 + 4})')
294
+ assert_equal '5 / #{1 + 2} + #{3 + 4}', render('5 /( #{1 + 2} + #{3 + 4} )')
295
+ assert_equal '#{1 + 2} + #{3 + 4} / 5', render('(#{1 + 2} + #{3 + 4}) / 5')
296
+ assert_equal '#{1 + 2} + #{3 + 4} / 5', render('(#{1 + 2} + #{3 + 4})/ 5')
297
+ assert_equal '#{1 + 2} + #{3 + 4} / 5', render('( #{1 + 2} + #{3 + 4} )/ 5')
298
+
299
+ assert_renders '#{1 + 2} + 2 + 3'
300
+ assert_renders '#{1 + 2} +2 + 3'
301
+ end
302
+
303
+ def test_string_interpolation
304
+ assert_renders '"foo#{$bar}baz"'
305
+ assert_renders '"foo #{$bar}baz"'
306
+ assert_renders '"foo#{$bar} baz"'
307
+ assert_renders '"foo #{$bar} baz"'
308
+ assert_renders '"foo #{$bar}#{$bang} baz"'
309
+ assert_renders '"foo #{$bar} #{$bang} baz"'
310
+ assert_renders '"#{$bar}baz"'
311
+ assert_renders '"foo#{$bar}"'
312
+ assert_equal '#{$bar}', render('"#{$bar}"')
313
+
314
+ assert_equal '"foo#{$bar}baz"', render("'foo\#{$bar}baz'")
315
+ end
316
+
317
+ private
318
+
319
+ def assert_renders(script, options = {})
320
+ assert_equal(script, render(script, options))
321
+ end
322
+
323
+ def render(script, options = {})
324
+ munge_filename(options)
325
+ node = Sass::Script.parse(script, 1, 0, options)
326
+ node.to_sass
327
+ end
328
+ end
@@ -0,0 +1,1054 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+ require File.dirname(__FILE__) + '/../test_helper'
4
+ require 'sass/engine'
5
+
6
+ module Sass::Script::Functions::UserFunctions
7
+ def assert_options(val)
8
+ val.options[:foo]
9
+ Sass::Script::Value::String.new("Options defined!")
10
+ end
11
+
12
+ def arg_error
13
+ assert_options
14
+ end
15
+ end
16
+
17
+ module Sass::Script::Functions
18
+ include Sass::Script::Functions::UserFunctions
19
+ end
20
+
21
+ class SassScriptTest < MiniTest::Test
22
+ include Sass::Script
23
+
24
+ def test_color_clamps_input
25
+ assert_equal 0, Sass::Script::Value::Color.new([1, 2, -1]).blue
26
+ assert_equal 255, Sass::Script::Value::Color.new([256, 2, 3]).red
27
+ end
28
+
29
+ def test_color_clamps_rgba_input
30
+ assert_equal 1, Sass::Script::Value::Color.new([1, 2, 3, 1.1]).alpha
31
+ assert_equal 0, Sass::Script::Value::Color.new([1, 2, 3, -0.1]).alpha
32
+ end
33
+
34
+ def test_string_escapes
35
+ assert_equal "'", resolve("\"'\"")
36
+ assert_equal '"', resolve("\"\\\"\"")
37
+ assert_equal "\\", resolve("\"\\\\\"")
38
+ assert_equal "☃", resolve("\"\\2603\"")
39
+ assert_equal "☃f", resolve("\"\\2603 f\"")
40
+ assert_equal "☃x", resolve("\"\\2603x\"")
41
+ assert_equal "\\2603", resolve("\"\\\\2603\"")
42
+
43
+ # U+FFFD is the replacement character, "�".
44
+ assert_equal [0xFFFD].pack("U"), resolve("\"\\0\"")
45
+ assert_equal [0xFFFD].pack("U"), resolve("\"\\FFFFFF\"")
46
+ assert_equal [0xFFFD].pack("U"), resolve("\"\\D800\"")
47
+ assert_equal [0xD7FF].pack("U"), resolve("\"\\D7FF\"")
48
+ assert_equal [0xFFFD].pack("U"), resolve("\"\\DFFF\"")
49
+ assert_equal [0xE000].pack("U"), resolve("\"\\E000\"")
50
+ end
51
+
52
+ def test_string_escapes_are_resolved_before_operators
53
+ assert_equal "true", resolve('"abc" == "\61\62\63"')
54
+ end
55
+
56
+ def test_string_quote
57
+ assert_equal '"foo"', resolve_quoted('"foo"')
58
+ assert_equal "'f\"oo'", resolve_quoted('"f\"oo"')
59
+ assert_equal "\"f'oo\"", resolve_quoted("'f\\'oo'")
60
+ assert_equal "\"f'o\\\"o\"", resolve_quoted("'f\\'o\"o'")
61
+ assert_equal '"foo bar"', resolve_quoted('"foo\20 bar"')
62
+ assert_equal '"foo\a bar"', resolve_quoted('"foo\a bar"')
63
+ assert_equal '"x\ay"', resolve_quoted('"x\a y"')
64
+ assert_equal '"\a "', resolve_quoted('"\a\20"')
65
+ assert_equal '"\a abcdef"', resolve_quoted('"\a abcdef"')
66
+ assert_equal '"☃abcdef"', resolve_quoted('"\2603 abcdef"')
67
+ assert_equal '"\\\\"', resolve_quoted('"\\\\"')
68
+ assert_equal '"foobar"', resolve_quoted("\"foo\\\nbar\"")
69
+ end
70
+
71
+ def test_color_names
72
+ assert_equal "white", resolve("white")
73
+ assert_equal "#ffffff", resolve("#ffffff")
74
+ assert_equal "#fffffe", resolve("white - #000001")
75
+ assert_equal "transparent", resolve("transparent")
76
+ assert_equal "transparent", resolve("rgba(0, 0, 0, 0)")
77
+ end
78
+
79
+ def test_rgba_color_literals
80
+ assert_equal Sass::Script::Value::Color.new([1, 2, 3, 0.75]), eval("rgba(1, 2, 3, 0.75)")
81
+ assert_equal "rgba(1, 2, 3, 0.75)", resolve("rgba(1, 2, 3, 0.75)")
82
+
83
+ assert_equal Sass::Script::Value::Color.new([1, 2, 3, 0]), eval("rgba(1, 2, 3, 0)")
84
+ assert_equal "rgba(1, 2, 3, 0)", resolve("rgba(1, 2, 3, 0)")
85
+
86
+ assert_equal Sass::Script::Value::Color.new([1, 2, 3]), eval("rgba(1, 2, 3, 1)")
87
+ assert_equal Sass::Script::Value::Color.new([1, 2, 3, 1]), eval("rgba(1, 2, 3, 1)")
88
+ assert_equal "#010203", resolve("rgba(1, 2, 3, 1)")
89
+ assert_equal "white", resolve("rgba(255, 255, 255, 1)")
90
+ end
91
+
92
+ def test_rgba_color_math
93
+ assert_equal "rgba(50, 50, 100, 0.35)", resolve("rgba(1, 1, 2, 0.35) * rgba(50, 50, 50, 0.35)")
94
+ assert_equal "rgba(52, 52, 52, 0.25)", resolve("rgba(2, 2, 2, 0.25) + rgba(50, 50, 50, 0.25)")
95
+
96
+ assert_raise_message(Sass::SyntaxError, "Alpha channels must be equal: rgba(1, 2, 3, 0.15) + rgba(50, 50, 50, 0.75)") do
97
+ resolve("rgba(1, 2, 3, 0.15) + rgba(50, 50, 50, 0.75)")
98
+ end
99
+ assert_raise_message(Sass::SyntaxError, "Alpha channels must be equal: #123456 * rgba(50, 50, 50, 0.75)") do
100
+ resolve("#123456 * rgba(50, 50, 50, 0.75)")
101
+ end
102
+ assert_raise_message(Sass::SyntaxError, "Alpha channels must be equal: rgba(50, 50, 50, 0.75) / #123456") do
103
+ resolve("rgba(50, 50, 50, 0.75) / #123456")
104
+ end
105
+ end
106
+
107
+ def test_rgba_number_math
108
+ assert_equal "rgba(49, 49, 49, 0.75)", resolve("rgba(50, 50, 50, 0.75) - 1")
109
+ assert_equal "rgba(100, 100, 100, 0.75)", resolve("rgba(50, 50, 50, 0.75) * 2")
110
+ end
111
+
112
+ def test_rgba_rounding
113
+ assert_equal "rgba(10, 1, 0, 0.12346)", resolve("rgba(10.0, 1.23456789, 0.0, 0.1234567)")
114
+ end
115
+
116
+ def test_compressed_colors
117
+ assert_equal "#123456", resolve("#123456", :style => :compressed)
118
+ assert_equal "rgba(1,2,3,0.5)", resolve("rgba(1, 2, 3, 0.5)", :style => :compressed)
119
+ assert_equal "#123", resolve("#112233", :style => :compressed)
120
+ assert_equal "#000", resolve("black", :style => :compressed)
121
+ assert_equal "red", resolve("#f00", :style => :compressed)
122
+ assert_equal "blue", resolve("blue", :style => :compressed)
123
+ assert_equal "navy", resolve("#000080", :style => :compressed)
124
+ assert_equal "navy #fff", resolve("#000080 white", :style => :compressed)
125
+ assert_equal "This color is #fff", resolve('"This color is #{ white }"', :style => :compressed)
126
+ assert_equal "transparent", resolve("rgba(0, 0, 0, 0)", :style => :compressed)
127
+ end
128
+
129
+ def test_compressed_comma
130
+ # assert_equal "foo,bar,baz", resolve("foo, bar, baz", :style => :compressed)
131
+ # assert_equal "foo,#baf,baz", resolve("foo, #baf, baz", :style => :compressed)
132
+ assert_equal "foo,#baf,red", resolve("foo, #baf, #f00", :style => :compressed)
133
+ end
134
+
135
+ def test_implicit_strings
136
+ assert_equal Sass::Script::Value::String.new("foo"), eval("foo")
137
+ assert_equal Sass::Script::Value::String.new("foo/bar"), eval("foo/bar")
138
+ end
139
+
140
+ def test_basic_interpolation
141
+ assert_equal "foo3bar", resolve("foo\#{1 + 2}bar")
142
+ assert_equal "foo3 bar", resolve("foo\#{1 + 2} bar")
143
+ assert_equal "foo 3bar", resolve("foo \#{1 + 2}bar")
144
+ assert_equal "foo 3 bar", resolve("foo \#{1 + 2} bar")
145
+ assert_equal "foo 35 bar", resolve("foo \#{1 + 2}\#{2 + 3} bar")
146
+ assert_equal "foo 3 5 bar", resolve("foo \#{1 + 2} \#{2 + 3} bar")
147
+ assert_equal "3bar", resolve("\#{1 + 2}bar")
148
+ assert_equal "foo3", resolve("foo\#{1 + 2}")
149
+ assert_equal "3", resolve("\#{1 + 2}")
150
+ end
151
+
152
+ def test_interpolation_in_function
153
+ assert_equal 'flabnabbit(1foo)', resolve('flabnabbit(#{1 + "foo"})')
154
+ assert_equal 'flabnabbit(foo 1foobaz)', resolve('flabnabbit(foo #{1 + "foo"}baz)')
155
+ assert_equal('flabnabbit(foo 1foo2bar baz)',
156
+ resolve('flabnabbit(foo #{1 + "foo"}#{2 + "bar"} baz)'))
157
+ end
158
+
159
+ def test_interpolation_near_operators
160
+ assert_equal '3 , 7', resolve('#{1 + 2} , #{3 + 4}')
161
+ assert_equal '3, 7', resolve('#{1 + 2}, #{3 + 4}')
162
+ assert_equal '3 ,7', resolve('#{1 + 2} ,#{3 + 4}')
163
+ assert_equal '3,7', resolve('#{1 + 2},#{3 + 4}')
164
+ assert_equal '3, 7, 11', resolve('#{1 + 2}, #{3 + 4}, #{5 + 6}')
165
+ assert_equal '3, 7, 11', resolve('3, #{3 + 4}, 11')
166
+ assert_equal '3, 7, 11', resolve('3, 7, #{5 + 6}')
167
+
168
+ assert_equal '3 / 7', resolve('3 / #{3 + 4}')
169
+ assert_equal '3 /7', resolve('3 /#{3 + 4}')
170
+ assert_equal '3/ 7', resolve('3/ #{3 + 4}')
171
+ assert_equal '3/7', resolve('3/#{3 + 4}')
172
+
173
+ assert_equal '3 * 7', resolve('#{1 + 2} * 7')
174
+ assert_equal '3* 7', resolve('#{1 + 2}* 7')
175
+ assert_equal '3 *7', resolve('#{1 + 2} *7')
176
+ assert_equal '3*7', resolve('#{1 + 2}*7')
177
+
178
+ assert_equal '-3', resolve('-#{1 + 2}')
179
+ assert_equal '- 3', resolve('- #{1 + 2}')
180
+
181
+ assert_equal '5 + 3 * 7', resolve('5 + #{1 + 2} * #{3 + 4}')
182
+ assert_equal '5 +3 * 7', resolve('5 +#{1 + 2} * #{3 + 4}')
183
+ assert_equal '5+3 * 7', resolve('5+#{1 + 2} * #{3 + 4}')
184
+ assert_equal '3 * 7 + 5', resolve('#{1 + 2} * #{3 + 4} + 5')
185
+ assert_equal '3 * 7+ 5', resolve('#{1 + 2} * #{3 + 4}+ 5')
186
+ assert_equal '3 * 7+5', resolve('#{1 + 2} * #{3 + 4}+5')
187
+
188
+ assert_equal '5/3 + 7', resolve('5 / (#{1 + 2} + #{3 + 4})')
189
+ assert_equal '5/3 + 7', resolve('5 /(#{1 + 2} + #{3 + 4})')
190
+ assert_equal '5/3 + 7', resolve('5 /( #{1 + 2} + #{3 + 4} )')
191
+ assert_equal '3 + 7/5', resolve('(#{1 + 2} + #{3 + 4}) / 5')
192
+ assert_equal '3 + 7/5', resolve('(#{1 + 2} + #{3 + 4})/ 5')
193
+ assert_equal '3 + 7/5', resolve('( #{1 + 2} + #{3 + 4} )/ 5')
194
+
195
+ assert_equal '3 + 5', resolve('#{1 + 2} + 2 + 3')
196
+ assert_equal '3 +5', resolve('#{1 + 2} +2 + 3')
197
+ end
198
+
199
+ def test_string_interpolation
200
+ assert_equal "foo bar, baz bang", resolve('"foo #{"bar"}, #{"baz"} bang"')
201
+ assert_equal "foo bar baz bang", resolve('"foo #{"#{"ba" + "r"} baz"} bang"')
202
+ assert_equal 'foo #{bar baz} bang', resolve('"foo \#{#{"ba" + "r"} baz} bang"')
203
+ assert_equal 'foo #{baz bang', resolve('"foo #{"\#{" + "baz"} bang"')
204
+ assert_equal "foo2bar", resolve('\'foo#{1 + 1}bar\'')
205
+ assert_equal "foo2bar", resolve('"foo#{1 + 1}bar"')
206
+ assert_equal "foo1bar5baz4bang", resolve('\'foo#{1 + "bar#{2 + 3}baz" + 4}bang\'')
207
+ end
208
+
209
+ def test_interpolation_with_newline
210
+ assert_equal "\nbang", resolve('"#{"\a "}bang"')
211
+ assert_equal "\n\nbang", resolve('"#{"\a "}\a bang"')
212
+ end
213
+
214
+ def test_rule_interpolation
215
+ assert_equal(<<CSS, render(<<SASS))
216
+ foo bar baz bang {
217
+ a: b; }
218
+ CSS
219
+ foo \#{"\#{"ba" + "r"} baz"} bang
220
+ a: b
221
+ SASS
222
+ assert_equal(<<CSS, render(<<SASS))
223
+ foo [bar="\#{bar baz}"] bang {
224
+ a: b; }
225
+ CSS
226
+ foo [bar="\\\#{\#{"ba" + "r"} baz}"] bang
227
+ a: b
228
+ SASS
229
+ assert_equal(<<CSS, render(<<SASS))
230
+ foo [bar="\#{baz"] bang {
231
+ a: b; }
232
+ CSS
233
+ foo [bar="\#{"\\\#{" + "baz"}"] bang
234
+ a: b
235
+ SASS
236
+ end
237
+
238
+ def test_inaccessible_functions
239
+ assert_equal "send(to_s)", resolve("send(to_s)", :line => 2)
240
+ assert_equal "public_instance_methods()", resolve("public_instance_methods()")
241
+ end
242
+
243
+ def test_adding_functions_directly_to_functions_module
244
+ assert !Functions.callable?('nonexistant')
245
+ Functions.class_eval { def nonexistant; end }
246
+ assert Functions.callable?('nonexistant')
247
+ Functions.send :remove_method, :nonexistant
248
+ end
249
+
250
+ def test_default_functions
251
+ assert_equal "url(12)", resolve("url(12)")
252
+ assert_equal 'blam("foo")', resolve('blam("foo")')
253
+ end
254
+
255
+ def test_function_results_have_options
256
+ assert_equal "Options defined!", resolve("assert_options(abs(1))")
257
+ assert_equal "Options defined!", resolve("assert_options(round(1.2))")
258
+ end
259
+
260
+ def test_funcall_requires_no_whitespace_before_lparen
261
+ assert_equal "no-repeat 15px", resolve("no-repeat (7px + 8px)")
262
+ assert_equal "no-repeat(15px)", resolve("no-repeat(7px + 8px)")
263
+ end
264
+
265
+ def test_dynamic_url
266
+ assert_equal "url(foo-bar)", resolve("url($foo)", {}, env('foo' => Sass::Script::Value::String.new("foo-bar")))
267
+ assert_equal "url(foo-bar baz)", resolve("url($foo $bar)", {}, env('foo' => Sass::Script::Value::String.new("foo-bar"), 'bar' => Sass::Script::Value::String.new("baz")))
268
+ assert_equal "url(foo baz)", resolve("url(foo $bar)", {}, env('bar' => Sass::Script::Value::String.new("baz")))
269
+ assert_equal "url(foo bar)", resolve("url(foo bar)")
270
+ end
271
+
272
+ def test_url_with_interpolation
273
+ assert_equal "url(http://sass-lang.com/images/foo-bar)", resolve("url(http://sass-lang.com/images/\#{foo-bar})")
274
+ assert_equal 'url("http://sass-lang.com/images/foo-bar")', resolve("url('http://sass-lang.com/images/\#{foo-bar}')")
275
+ assert_equal 'url("http://sass-lang.com/images/foo-bar")', resolve('url("http://sass-lang.com/images/#{foo-bar}")')
276
+ assert_unquoted "url(http://sass-lang.com/images/\#{foo-bar})"
277
+ end
278
+
279
+ def test_hyphenated_variables
280
+ assert_equal("a-b", resolve("$a-b", {}, env("a-b" => Sass::Script::Value::String.new("a-b"))))
281
+ end
282
+
283
+ def test_ruby_equality
284
+ assert_equal eval('"foo"'), eval('"foo"')
285
+ assert_equal eval('1'), eval('1.0')
286
+ assert_equal eval('1 2 3.0'), eval('1 2 3')
287
+ assert_equal eval('1, 2, 3.0'), eval('1, 2, 3')
288
+ assert_equal eval('(1 2), (3, 4), (5 6)'), eval('(1 2), (3, 4), (5 6)')
289
+ refute_equal eval('1, 2, 3'), eval('1 2 3')
290
+ refute_equal eval('1'), eval('"1"')
291
+ end
292
+
293
+ def test_booleans
294
+ assert_equal "true", resolve("true")
295
+ assert_equal "false", resolve("false")
296
+ end
297
+
298
+ def test_null
299
+ assert_equal "", resolve("null")
300
+ end
301
+
302
+ def test_boolean_ops
303
+ assert_equal "true", resolve("true and true")
304
+ assert_equal "true", resolve("false or true")
305
+ assert_equal "true", resolve("true or false")
306
+ assert_equal "true", resolve("true or true")
307
+ assert_equal "false", resolve("false or false")
308
+ assert_equal "false", resolve("false and true")
309
+ assert_equal "false", resolve("true and false")
310
+ assert_equal "false", resolve("false and false")
311
+
312
+ assert_equal "true", resolve("not false")
313
+ assert_equal "false", resolve("not true")
314
+ assert_equal "true", resolve("not not true")
315
+
316
+ assert_equal "1", resolve("false or 1")
317
+ assert_equal "false", resolve("false and 1")
318
+ assert_equal "2", resolve("2 or 3")
319
+ assert_equal "3", resolve("2 and 3")
320
+
321
+ assert_equal "true", resolve("null or true")
322
+ assert_equal "true", resolve("true or null")
323
+ assert_equal "", resolve("null or null")
324
+ assert_equal "", resolve("null and true")
325
+ assert_equal "", resolve("true and null")
326
+ assert_equal "", resolve("null and null")
327
+
328
+ assert_equal "true", resolve("not null")
329
+
330
+ assert_equal "1", resolve("null or 1")
331
+ assert_equal "", resolve("null and 1")
332
+ end
333
+
334
+ def test_arithmetic_ops
335
+ assert_equal "2", resolve("1 + 1")
336
+ assert_equal "0", resolve("1 - 1")
337
+ assert_equal "8", resolve("2 * 4")
338
+ assert_equal "0.5", resolve("(2 / 4)")
339
+ assert_equal "2", resolve("(4 / 2)")
340
+
341
+ assert_equal "-1", resolve("-1")
342
+ end
343
+
344
+ def test_string_ops
345
+ assert_equal '"foo" "bar"', resolve('"foo" "bar"')
346
+ assert_equal "true 1", resolve('true 1')
347
+ assert_equal '"foo", "bar"', resolve("'foo' , 'bar'")
348
+ assert_equal "true, 1", resolve('true , 1')
349
+ assert_equal "foobar", resolve('"foo" + "bar"')
350
+ assert_equal "\nfoo\nxyz", resolve('"\a foo" + "\axyz"')
351
+ assert_equal "true1", resolve('true + 1')
352
+ assert_equal '"foo"-"bar"', resolve("'foo' - 'bar'")
353
+ assert_equal "true-1", resolve('true - 1')
354
+ assert_equal '"foo"/"bar"', resolve('"foo" / "bar"')
355
+ assert_equal "true/1", resolve('true / 1')
356
+
357
+ assert_equal '-"bar"', resolve("- 'bar'")
358
+ assert_equal "-true", resolve('- true')
359
+ assert_equal '/"bar"', resolve('/ "bar"')
360
+ assert_equal "/true", resolve('/ true')
361
+ end
362
+
363
+ def test_relational_ops
364
+ assert_equal "false", resolve("1 > 2")
365
+ assert_equal "false", resolve("2 > 2")
366
+ assert_equal "true", resolve("3 > 2")
367
+ assert_equal "false", resolve("1 >= 2")
368
+ assert_equal "true", resolve("2 >= 2")
369
+ assert_equal "true", resolve("3 >= 2")
370
+ assert_equal "true", resolve("1 < 2")
371
+ assert_equal "false", resolve("2 < 2")
372
+ assert_equal "false", resolve("3 < 2")
373
+ assert_equal "true", resolve("1 <= 2")
374
+ assert_equal "true", resolve("2 <= 2")
375
+ assert_equal "false", resolve("3 <= 2")
376
+ end
377
+
378
+ def test_null_ops
379
+ assert_raise_message(Sass::SyntaxError,
380
+ 'Invalid null operation: "null plus 1".') {eval("null + 1")}
381
+ assert_raise_message(Sass::SyntaxError,
382
+ 'Invalid null operation: "null minus 1".') {eval("null - 1")}
383
+ assert_raise_message(Sass::SyntaxError,
384
+ 'Invalid null operation: "null times 1".') {eval("null * 1")}
385
+ assert_raise_message(Sass::SyntaxError,
386
+ 'Invalid null operation: "null div 1".') {eval("null / 1")}
387
+ assert_raise_message(Sass::SyntaxError,
388
+ 'Invalid null operation: "null mod 1".') {eval("null % 1")}
389
+ assert_raise_message(Sass::SyntaxError,
390
+ 'Invalid null operation: "1 plus null".') {eval("1 + null")}
391
+ assert_raise_message(Sass::SyntaxError,
392
+ 'Invalid null operation: "1 minus null".') {eval("1 - null")}
393
+ assert_raise_message(Sass::SyntaxError,
394
+ 'Invalid null operation: "1 times null".') {eval("1 * null")}
395
+ assert_raise_message(Sass::SyntaxError,
396
+ 'Invalid null operation: "1 div null".') {eval("1 / null")}
397
+ assert_raise_message(Sass::SyntaxError,
398
+ 'Invalid null operation: "1 mod null".') {eval("1 % null")}
399
+ assert_raise_message(Sass::SyntaxError,
400
+ 'Invalid null operation: "1 gt null".') {eval("1 > null")}
401
+ assert_raise_message(Sass::SyntaxError,
402
+ 'Invalid null operation: "null lt 1".') {eval("null < 1")}
403
+ assert_raise_message(Sass::SyntaxError,
404
+ 'Invalid null operation: "null plus null".') {eval("null + null")}
405
+ assert_raise_message(Sass::SyntaxError,
406
+ 'Invalid null operation: ""foo" plus null".') {eval("foo + null")}
407
+ end
408
+
409
+ def test_equals
410
+ assert_equal("true", resolve('"foo" == $foo', {},
411
+ env("foo" => Sass::Script::Value::String.new("foo"))))
412
+ assert_equal "true", resolve("1 == 1.0")
413
+ assert_equal "true", resolve("false != true")
414
+ assert_equal "false", resolve("1em == 1px")
415
+ assert_equal "false", resolve("12 != 12")
416
+ assert_equal "true", resolve("(foo bar baz) == (foo bar baz)")
417
+ assert_equal "true", resolve("(foo, bar, baz) == (foo, bar, baz)")
418
+ assert_equal "true", resolve('((1 2), (3, 4), (5 6)) == ((1 2), (3, 4), (5 6))')
419
+ assert_equal "true", resolve('((1 2), (3 4)) == (1 2, 3 4)')
420
+ assert_equal "false", resolve('((1 2) 3) == (1 2 3)')
421
+ assert_equal "false", resolve('(1 (2 3)) == (1 2 3)')
422
+ assert_equal "false", resolve('((1, 2) (3, 4)) == (1, 2 3, 4)')
423
+ assert_equal "false", resolve('(1 2 3) == (1, 2, 3)')
424
+
425
+ assert_equal "true", resolve('null == null')
426
+ assert_equal "false", resolve('"null" == null')
427
+ assert_equal "false", resolve('0 == null')
428
+ assert_equal "false", resolve('() == null')
429
+
430
+ assert_equal "false", resolve('null != null')
431
+ assert_equal "true", resolve('"null" != null')
432
+ assert_equal "true", resolve('0 != null')
433
+ assert_equal "true", resolve('() != null')
434
+ end
435
+
436
+ def test_mod
437
+ assert_equal "5", resolve("29 % 12")
438
+ assert_equal "5px", resolve("29px % 12")
439
+ assert_equal "5px", resolve("29px % 12px")
440
+ end
441
+
442
+ def test_operation_precedence
443
+ assert_equal "false true", resolve("true and false false or true")
444
+ assert_equal "true", resolve("false and true or true and true")
445
+ assert_equal "true", resolve("1 == 2 or 3 == 3")
446
+ assert_equal "true", resolve("1 < 2 == 3 >= 3")
447
+ assert_equal "true", resolve("1 + 3 > 4 - 2")
448
+ assert_equal "11", resolve("1 + 2 * 3 + 4")
449
+ end
450
+
451
+ def test_functions
452
+ assert_equal "#80ff80", resolve("hsl(120, 100%, 75%)")
453
+ assert_equal "#81ff81", resolve("hsl(120, 100%, 75%) + #010001")
454
+ end
455
+
456
+ def test_operator_unit_conversion
457
+ assert_equal "1.1cm", resolve("1cm + 1mm")
458
+ assert_equal "2in", resolve("1in + 96px")
459
+ assert_equal "true", resolve("2mm < 1cm")
460
+ assert_equal "true", resolve("10mm == 1cm")
461
+ assert_equal "true", resolve("1 == 1cm")
462
+ assert_equal "true", resolve("1.1cm == 11mm")
463
+ end
464
+
465
+ def test_length_units
466
+ assert_equal "2.54", resolve("(1in/1cm)")
467
+ assert_equal "2.3622", resolve("(1cm/1pc)")
468
+ assert_equal "4.23333", resolve("(1pc/1mm)")
469
+ assert_equal "2.83465", resolve("(1mm/1pt)")
470
+ assert_equal "1.33333", resolve("(1pt/1px)")
471
+ assert_equal "0.01042", resolve("(1px/1in)")
472
+ end
473
+
474
+ def test_angle_units
475
+ assert_equal "1.11111", resolve("(1deg/1grad)")
476
+ assert_equal "0.01571", resolve("(1grad/1rad)")
477
+ assert_equal "0.15915", resolve("(1rad/1turn)")
478
+ assert_equal "360", resolve("(1turn/1deg)")
479
+ end
480
+
481
+ def test_time_units
482
+ assert_equal "1000", resolve("(1s/1ms)")
483
+ end
484
+
485
+ def test_frequency_units
486
+ assert_equal "0.001", resolve("(1Hz/1kHz)")
487
+ end
488
+
489
+ def test_resolution_units
490
+ assert_equal "2.54", resolve("(1dpi/1dpcm)")
491
+ assert_equal "37.79528", resolve("(1dpcm/1dppx)")
492
+ assert_equal "0.01042", resolve("(1dppx/1dpi)")
493
+ end
494
+
495
+ def test_operations_have_options
496
+ assert_equal "Options defined!", resolve("assert_options(1 + 1)")
497
+ assert_equal "Options defined!", resolve("assert_options('bar' + 'baz')")
498
+ end
499
+
500
+ def test_slash_compiles_literally_when_left_alone
501
+ assert_equal "1px/2px", resolve("1px/2px")
502
+ assert_equal "1px/2px/3px/4px", resolve("1px/2px/3px/4px")
503
+
504
+ assert_equal "1px/2px redpx bluepx", resolve("1px/2px redpx bluepx")
505
+ assert_equal "foo 1px/2px/3px bar", resolve("foo 1px/2px/3px bar")
506
+ end
507
+
508
+ def test_slash_divides_with_parens
509
+ assert_equal "0.5", resolve("(1px/2px)")
510
+ assert_equal "0.5", resolve("(1px)/2px")
511
+ assert_equal "0.5", resolve("1px/(2px)")
512
+ end
513
+
514
+ def test_slash_divides_with_other_arithmetic
515
+ assert_equal "0.5px", resolve("1px*1px/2px")
516
+ assert_equal "0.5px", resolve("1px/2px*1px")
517
+ assert_equal "0.5", resolve("0+1px/2px")
518
+ assert_equal "0.5", resolve("1px/2px+0")
519
+ end
520
+
521
+ def test_slash_divides_with_variable
522
+ assert_equal "0.5", resolve("$var/2px", {}, env("var" => eval("1px")))
523
+ assert_equal "0.5", resolve("1px/$var", {}, env("var" => eval("2px")))
524
+ assert_equal "0.5", resolve("$var", {}, env("var" => eval("1px/2px")))
525
+ end
526
+
527
+ def test_non_ident_colors_with_wrong_number_of_digits
528
+ assert_raise_message(Sass::SyntaxError,
529
+ 'Invalid CSS after "": expected expression (e.g. 1px, bold), was "#1"') {eval("#1")}
530
+ assert_raise_message(Sass::SyntaxError,
531
+ 'Invalid CSS after "": expected expression (e.g. 1px, bold), was "#12"') {eval("#12")}
532
+ assert_raise_message(Sass::SyntaxError,
533
+ 'Invalid CSS after "": expected expression (e.g. 1px, bold), was "#1234"') {eval("#1234")}
534
+ assert_raise_message(Sass::SyntaxError,
535
+ 'Invalid CSS after "": expected expression (e.g. 1px, bold), was "#12345"') {eval("#12345")}
536
+ assert_raise_message(Sass::SyntaxError, 'Invalid CSS after "": expected expression (e.g. ' \
537
+ '1px, bold), was "#1234567"') {eval("#1234567")}
538
+ end
539
+
540
+ def test_case_insensitive_color_names
541
+ assert_equal "BLUE", resolve("BLUE")
542
+ assert_equal "rEd", resolve("rEd")
543
+ assert_equal "#7f4000", resolve("mix(GrEeN, ReD)")
544
+ end
545
+
546
+ def test_empty_list
547
+ assert_equal "1 2 3", resolve("1 2 () 3")
548
+ assert_equal "1 2 3", resolve("1 2 3 ()")
549
+ assert_equal "1 2 3", resolve("() 1 2 3")
550
+ assert_raise_message(Sass::SyntaxError, "() isn't a valid CSS value.") {resolve("()")}
551
+ assert_raise_message(Sass::SyntaxError, "() isn't a valid CSS value.") {resolve("nth(append((), ()), 1)")}
552
+ end
553
+
554
+ def test_list_with_nulls
555
+ assert_equal "1, 2, 3", resolve("1, 2, null, 3")
556
+ assert_equal "1 2 3", resolve("1 2 null 3")
557
+ assert_equal "1, 2, 3", resolve("1, 2, 3, null")
558
+ assert_equal "1 2 3", resolve("1 2 3 null")
559
+ assert_equal "1, 2, 3", resolve("null, 1, 2, 3")
560
+ assert_equal "1 2 3", resolve("null 1 2 3")
561
+ end
562
+
563
+ def test_map_can_have_trailing_comma
564
+ assert_equal("(foo: 1, bar: 2)", eval("(foo: 1, bar: 2,)").to_sass)
565
+ end
566
+
567
+ def test_list_can_have_trailing_comma
568
+ assert_equal("1, 2, 3", resolve("1, 2, 3,"))
569
+ end
570
+
571
+ def test_trailing_comma_defines_singleton_list
572
+ assert_equal("1 2 3", resolve("nth((1 2 3,), 1)"))
573
+ end
574
+
575
+ def test_map_cannot_have_duplicate_keys
576
+ assert_raise_message(Sass::SyntaxError, 'Duplicate key "foo" in map (foo: bar, foo: baz).') do
577
+ eval("(foo: bar, foo: baz)")
578
+ end
579
+ assert_raise_message(Sass::SyntaxError, 'Duplicate key "foo" in map (foo: bar, fo + o: baz).') do
580
+ eval("(foo: bar, fo + o: baz)")
581
+ end
582
+ assert_raise_message(Sass::SyntaxError, 'Duplicate key "foo" in map (foo: bar, "foo": baz).') do
583
+ eval("(foo: bar, 'foo': baz)")
584
+ end
585
+ assert_raise_message(Sass::SyntaxError, 'Duplicate key 2px in map (2px: bar, 1px + 1px: baz).') do
586
+ eval("(2px: bar, 1px + 1px: baz)")
587
+ end
588
+ assert_raise_message(Sass::SyntaxError, 'Duplicate key #0000ff in map (blue: bar, #00f: baz).') do
589
+ eval("(blue: bar, #00f: baz)")
590
+ end
591
+ end
592
+
593
+ def test_non_duplicate_map_keys
594
+ # These shouldn't throw errors
595
+ eval("(foo: foo, bar: bar)")
596
+ eval("(2px: foo, 2: bar)")
597
+ eval("(2px: foo, 2em: bar)")
598
+ eval("('2px': foo, 2px: bar)")
599
+ end
600
+
601
+ def test_map_syntax_errors
602
+ assert_raise_message(Sass::SyntaxError, 'Invalid CSS after "(foo:": expected expression (e.g. 1px, bold), was ")"') do
603
+ eval("(foo:)")
604
+ end
605
+ assert_raise_message(Sass::SyntaxError, 'Invalid CSS after "(": expected ")", was ":bar)"') do
606
+ eval("(:bar)")
607
+ end
608
+ assert_raise_message(Sass::SyntaxError, 'Invalid CSS after "(foo, bar": expected ")", was ": baz)"') do
609
+ eval("(foo, bar: baz)")
610
+ end
611
+ assert_raise_message(Sass::SyntaxError, 'Invalid CSS after "(foo: bar, baz": expected ":", was ")"') do
612
+ eval("(foo: bar, baz)")
613
+ end
614
+ end
615
+
616
+ def test_deep_argument_error_not_unwrapped
617
+ # JRuby (as of 1.6.7.2) offers no way of distinguishing between
618
+ # argument errors caused by programming errors in a function and
619
+ # argument errors explicitly thrown within that function.
620
+ return if RUBY_PLATFORM =~ /java/
621
+
622
+ # Don't validate the message; it's different on Rubinius.
623
+ assert_raises(ArgumentError) {resolve("arg-error()")}
624
+ end
625
+
626
+ def test_shallow_argument_error_unwrapped
627
+ assert_raise_message(Sass::SyntaxError, "wrong number of arguments (1 for 0) for `arg-error'") {resolve("arg-error(1)")}
628
+ end
629
+
630
+ def test_boolean_ops_short_circuit
631
+ assert_equal "false", resolve("$ie and $ie <= 7", {}, env('ie' => Sass::Script::Value::Bool.new(false)))
632
+ assert_equal "true", resolve("$ie or $undef", {}, env('ie' => Sass::Script::Value::Bool.new(true)))
633
+ end
634
+
635
+ def test_selector
636
+ env = Sass::Environment.new
637
+ assert_equal "true", resolve("& == null", {}, env)
638
+
639
+ env.selector = selector('.foo.bar .baz.bang, .bip.bop')
640
+ assert_equal ".foo.bar .baz.bang, .bip.bop", resolve("&", {}, env)
641
+ assert_equal ".foo.bar .baz.bang", resolve("nth(&, 1)", {}, env)
642
+ assert_equal ".bip.bop", resolve("nth(&, 2)", {}, env)
643
+ assert_equal ".foo.bar", resolve("nth(nth(&, 1), 1)", {}, env)
644
+ assert_equal ".baz.bang", resolve("nth(nth(&, 1), 2)", {}, env)
645
+ assert_equal ".bip.bop", resolve("nth(nth(&, 2), 1)", {}, env)
646
+ assert_equal "string", resolve("type-of(nth(nth(&, 1), 1))", {}, env)
647
+
648
+ env.selector = selector('.foo > .bar')
649
+ assert_equal ".foo > .bar", resolve("&", {}, env)
650
+ assert_equal ".foo > .bar", resolve("nth(&, 1)", {}, env)
651
+ assert_equal ".foo", resolve("nth(nth(&, 1), 1)", {}, env)
652
+ assert_equal ">", resolve("nth(nth(&, 1), 2)", {}, env)
653
+ assert_equal ".bar", resolve("nth(nth(&, 1), 3)", {}, env)
654
+ end
655
+
656
+ def test_selector_with_newlines
657
+ env = Sass::Environment.new
658
+ env.selector = selector(".foo.bar\n.baz.bang,\n\n.bip.bop")
659
+ assert_equal ".foo.bar .baz.bang, .bip.bop", resolve("&", {}, env)
660
+ assert_equal ".foo.bar .baz.bang", resolve("nth(&, 1)", {}, env)
661
+ assert_equal ".bip.bop", resolve("nth(&, 2)", {}, env)
662
+ assert_equal ".foo.bar", resolve("nth(nth(&, 1), 1)", {}, env)
663
+ assert_equal ".baz.bang", resolve("nth(nth(&, 1), 2)", {}, env)
664
+ assert_equal ".bip.bop", resolve("nth(nth(&, 2), 1)", {}, env)
665
+ assert_equal "string", resolve("type-of(nth(nth(&, 1), 1))", {}, env)
666
+ end
667
+
668
+ def test_setting_global_variable_globally
669
+ assert_no_warning {assert_equal(<<CSS, render(<<SCSS, :syntax => :scss))}
670
+ .foo {
671
+ a: 1; }
672
+
673
+ .bar {
674
+ b: 2; }
675
+ CSS
676
+ $var: 1;
677
+
678
+ .foo {
679
+ a: $var;
680
+ }
681
+
682
+ $var: 2;
683
+
684
+ .bar {
685
+ b: $var;
686
+ }
687
+ SCSS
688
+ end
689
+
690
+ def test_setting_global_variable_locally
691
+ assert_no_warning {assert_equal(<<CSS, render(<<SCSS, :syntax => :scss))}
692
+ .bar {
693
+ a: x;
694
+ b: y;
695
+ c: z; }
696
+ CSS
697
+ $var1: 1;
698
+ $var3: 3;
699
+
700
+ .foo {
701
+ $var1: x !global;
702
+ $var2: y !global;
703
+ @each $var3 in _ {
704
+ $var3: z !global;
705
+ }
706
+ }
707
+
708
+ .bar {
709
+ a: $var1;
710
+ b: $var2;
711
+ c: $var3;
712
+ }
713
+ SCSS
714
+ end
715
+
716
+ def test_setting_global_variable_locally_with_default
717
+ assert_equal(<<CSS, render(<<SCSS, :syntax => :scss))
718
+ .bar {
719
+ a: 1;
720
+ b: y;
721
+ c: z; }
722
+ CSS
723
+ $var1: 1;
724
+
725
+ .foo {
726
+ $var1: x !global !default;
727
+ $var2: y !global !default;
728
+ @each $var3 in _ {
729
+ $var3: z !global !default;
730
+ }
731
+ }
732
+
733
+ .bar {
734
+ a: $var1;
735
+ b: $var2;
736
+ c: $var3;
737
+ }
738
+ SCSS
739
+ end
740
+
741
+ def test_setting_local_variable
742
+ assert_equal(<<CSS, render(<<SCSS, :syntax => :scss))
743
+ .a {
744
+ value: inside; }
745
+
746
+ .b {
747
+ value: outside; }
748
+ CSS
749
+ $var: outside;
750
+
751
+ .a {
752
+ $var: inside;
753
+ value: $var;
754
+ }
755
+
756
+ .b {
757
+ value: $var;
758
+ }
759
+ SCSS
760
+ end
761
+
762
+ def test_setting_local_variable_from_inner_scope
763
+ assert_equal(<<CSS, render(<<SCSS, :syntax => :scss))
764
+ .a .b {
765
+ value: inside; }
766
+ .a .c {
767
+ value: inside; }
768
+ CSS
769
+ .a {
770
+ $var: outside;
771
+
772
+ .b {
773
+ $var: inside;
774
+ value: $var;
775
+ }
776
+
777
+ .c {
778
+ value: $var;
779
+ }
780
+ }
781
+ SCSS
782
+ end
783
+
784
+ def test_color_format_is_preserved_by_default
785
+ assert_equal "blue", resolve("blue")
786
+ assert_equal "bLuE", resolve("bLuE")
787
+ assert_equal "#00f", resolve("#00f")
788
+ assert_equal "blue #00F", resolve("blue #00F")
789
+ assert_equal "blue", resolve("nth(blue #00F, 1)")
790
+ assert_equal "#00F", resolve("nth(blue #00F, 2)")
791
+ end
792
+
793
+ def test_color_format_isnt_always_preserved_in_compressed_style
794
+ assert_equal "red", resolve("red", :style => :compressed)
795
+ assert_equal "red", resolve("#f00", :style => :compressed)
796
+ assert_equal "red red", resolve("red #f00", :style => :compressed)
797
+ assert_equal "red", resolve("nth(red #f00, 2)", :style => :compressed)
798
+ end
799
+
800
+ def test_color_format_is_sometimes_preserved_in_compressed_style
801
+ assert_equal "ReD", resolve("ReD", :style => :compressed)
802
+ assert_equal "blue", resolve("blue", :style => :compressed)
803
+ assert_equal "#00f", resolve("#00f", :style => :compressed)
804
+ end
805
+
806
+ def test_color_format_isnt_preserved_when_modified
807
+ assert_equal "magenta", resolve("#f00 + #00f")
808
+ end
809
+
810
+ def test_ids
811
+ assert_equal "#foo", resolve("#foo")
812
+ assert_equal "#abcd", resolve("#abcd")
813
+ assert_equal "#abc-def", resolve("#abc-def")
814
+ assert_equal "#abc_def", resolve("#abc_def")
815
+ assert_equal "#uvw-xyz", resolve("#uvw-xyz")
816
+ assert_equal "#uvw_xyz", resolve("#uvw_xyz")
817
+ assert_equal "#uvwxyz", resolve("#uvw + xyz")
818
+ end
819
+
820
+ def test_scientific_notation
821
+ assert_equal "2000", resolve("2e3")
822
+ assert_equal "2000", resolve("2E3")
823
+ assert_equal "2000", resolve("2e+3")
824
+ assert_equal "2000em", resolve("2e3em")
825
+ assert_equal "25000000000", resolve("2.5e10")
826
+ assert_equal "0.1234", resolve("1234e-4")
827
+ assert_equal "12.34", resolve("1.234e1")
828
+ end
829
+
830
+ def test_identifier_units
831
+ assert_equal "5-foo", resolve("2-foo + 3-foo")
832
+ assert_equal "5-foo-", resolve("2-foo- + 3-foo-")
833
+ assert_equal "5-\\u2603", resolve("2-\\u2603 + 3-\\u2603")
834
+ end
835
+
836
+ def test_backslash_newline_in_string
837
+ assert_equal 'foobar', resolve("\"foo\\\nbar\"")
838
+ assert_equal 'foobar', resolve("'foo\\\nbar'")
839
+ end
840
+
841
+ def test_unclosed_special_fun
842
+ assert_raise_message(Sass::SyntaxError, 'Invalid CSS after "calc(foo()": expected ")", was ""') do
843
+ resolve("calc(foo()")
844
+ end
845
+ assert_raise_message(Sass::SyntaxError, 'Invalid CSS after "calc(#{\')\'}": expected ")", was ""') do
846
+ resolve("calc(\#{')'}")
847
+ end
848
+ assert_raise_message(Sass::SyntaxError, 'Invalid CSS after "calc(#{foo": expected "}", was ""') do
849
+ resolve("calc(\#{foo")
850
+ end
851
+ end
852
+
853
+ def test_special_fun_with_interpolation
854
+ assert_equal "calc())", resolve("calc(\#{')'})")
855
+ assert_equal "calc(# {foo})", resolve("calc(# {foo})")
856
+ end
857
+
858
+ # Regression Tests
859
+
860
+ def test_repeatedly_modified_color
861
+ assert_equal(<<CSS, render(<<SASS))
862
+ a {
863
+ link-color: #161C14;
864
+ link-color-hover: black;
865
+ link-color-tap: rgba(22, 28, 20, 0.3); }
866
+ CSS
867
+ $green: #161C14
868
+ $link-color: $green
869
+ $link-color-hover: darken($link-color, 10%)
870
+ $link-color-tap: rgba($green, 0.3)
871
+
872
+ a
873
+ link-color: $link-color
874
+ link-color-hover: $link-color-hover
875
+ link-color-tap: $link-color-tap
876
+ SASS
877
+ end
878
+
879
+ def test_inspect_divided_numbers
880
+ assert_equal "1px/2px", resolve("inspect(1px/2px)")
881
+ assert_equal "0.5", resolve("inspect((1px/2px))")
882
+ end
883
+
884
+ def test_minus_without_whitespace
885
+ assert_equal "5px", resolve("15px-10px")
886
+ assert_equal "5px-", resolve("15px--10px-")
887
+ end
888
+
889
+ def test_minus_preceded_by_comment
890
+ assert_equal "15px -10px", resolve("15px/**/-10px")
891
+ end
892
+
893
+ def test_user_defined_function_forces_division
894
+ assert_equal(<<CSS, render(<<SASS))
895
+ a {
896
+ b: 10px; }
897
+ CSS
898
+ @function foo()
899
+ @return 20px
900
+
901
+ a
902
+ b: (foo() / 2)
903
+ SASS
904
+
905
+ assert_equal(<<CSS, render(<<SASS))
906
+ a {
907
+ b: 10px; }
908
+ CSS
909
+ @function foo()
910
+ @return 20px
911
+
912
+ a
913
+ b: foo() / 2
914
+ SASS
915
+ end
916
+
917
+ def test_funcall_has_higher_precedence_than_color_name
918
+ assert_equal "teal(12)", resolve("teal(12)")
919
+ assert_equal "tealbang(12)", resolve("tealbang(12)")
920
+ assert_equal "teal-bang(12)", resolve("teal-bang(12)")
921
+ assert_equal "teal\\+bang(12)", resolve("teal\\+bang(12)")
922
+ end
923
+
924
+ def test_funcall_has_higher_precedence_than_true_false_null
925
+ assert_equal "teal(12)", resolve("teal(12)")
926
+ assert_equal "tealbang(12)", resolve("tealbang(12)")
927
+ assert_equal "teal-bang(12)", resolve("teal-bang(12)")
928
+ assert_equal "teal\\+bang(12)", resolve("teal\\+bang(12)")
929
+ end
930
+
931
+ def test_and_or_not_disallowed_as_function_names
932
+ %w[and or not].each do |name|
933
+ assert_raise_message(Sass::SyntaxError, "Invalid function name \"#{name}\".") do
934
+ render(<<SASS)
935
+ @function #{name}()
936
+ @return null
937
+ SASS
938
+ end
939
+ end
940
+ end
941
+
942
+ def test_interpolation_after_hash
943
+ assert_equal "#2", resolve('"##{1 + 1}"')
944
+ end
945
+
946
+ def test_misplaced_comma_in_funcall
947
+ assert_raise_message(Sass::SyntaxError,
948
+ 'Invalid CSS after "foo(bar, ": expected function argument, was ")"') {eval('foo(bar, )')}
949
+ end
950
+
951
+ def test_color_prefixed_identifier
952
+ assert_equal "tealbang", resolve("tealbang")
953
+ assert_equal "teal-bang", resolve("teal-bang")
954
+ end
955
+
956
+ def test_op_prefixed_identifier
957
+ assert_equal "notbang", resolve("notbang")
958
+ assert_equal "not-bang", resolve("not-bang")
959
+ assert_equal "or-bang", resolve("or-bang")
960
+ assert_equal "and-bang", resolve("and-bang")
961
+ end
962
+
963
+ def test_number_initialization
964
+ assert_equal Sass::Script::Value::Number.new(10, ["px"]), Sass::Script::Value::Number.new(10, "px")
965
+ assert_equal Sass::Script::Value::Number.new(10, ["px"], ["em"]), Sass::Script::Value::Number.new(10, "px", "em")
966
+ end
967
+
968
+ def test_is_unit
969
+ assert Sass::Script::Value::Number.new(10, "px").is_unit?("px")
970
+ assert Sass::Script::Value::Number.new(10).is_unit?(nil)
971
+ assert !Sass::Script::Value::Number.new(10, "px", "em").is_unit?("px")
972
+ assert !Sass::Script::Value::Number.new(10, [], "em").is_unit?("em")
973
+ assert !Sass::Script::Value::Number.new(10, ["px", "em"]).is_unit?("px")
974
+ end
975
+
976
+ def test_rename_redirect
977
+ assert_no_warning do
978
+ assert_equal Sass::Script::Value::Base, Sass::Script::Literal
979
+ assert_equal Sass::Script::Tree::Node, Sass::Script::Node
980
+ assert_equal Sass::Script::Tree::Operation, Sass::Script::Operation
981
+ assert_equal Sass::Script::Value::String, Sass::Script::String
982
+ end
983
+ end
984
+
985
+ def test_number_printing
986
+ assert_equal "1", resolve("1")
987
+ assert_equal "1", resolve("1.0")
988
+ assert_equal "1000000000", resolve("1000000000")
989
+ assert_equal "0.00001", resolve("0.00001")
990
+ assert_equal "1.12121", resolve("1.121214")
991
+ assert_equal "1.12122", resolve("1.121215")
992
+ assert_equal "Infinity", resolve("(1.0/0.0)")
993
+ assert_equal "-Infinity", resolve("(-1.0/0.0)")
994
+ assert_equal "NaN", resolve("(0.0/0.0)")
995
+ end
996
+
997
+ private
998
+
999
+ def resolve(str, opts = {}, environment = env)
1000
+ munge_filename opts
1001
+ val = eval(str, opts, environment)
1002
+ assert_kind_of Sass::Script::Value::Base, val
1003
+ val.is_a?(Sass::Script::Value::String) ? val.value : val.to_s
1004
+ end
1005
+
1006
+ def resolve_quoted(str, opts = {}, environment = env)
1007
+ munge_filename opts
1008
+ val = eval(str, opts, environment)
1009
+ assert_kind_of Sass::Script::Value::Base, val
1010
+ val.to_s
1011
+ end
1012
+
1013
+ def assert_unquoted(str, opts = {}, environment = env)
1014
+ munge_filename opts
1015
+ val = eval(str, opts, environment)
1016
+ assert_kind_of Sass::Script::Value::String, val
1017
+ assert_equal :identifier, val.type
1018
+ end
1019
+
1020
+ def assert_quoted(str, opts = {}, environment = env)
1021
+ munge_filename opts
1022
+ val = eval(str, opts, environment)
1023
+ assert_kind_of Sass::Script::Value::String, val
1024
+ assert_equal :string, val.type
1025
+ end
1026
+
1027
+ def eval(str, opts = {}, environment = env)
1028
+ munge_filename opts
1029
+ Sass::Script.parse(str, opts.delete(:line) || 1,
1030
+ opts.delete(:offset) || 0, opts).
1031
+ perform(Sass::Environment.new(environment, opts))
1032
+ end
1033
+
1034
+ def render(sass, options = {})
1035
+ munge_filename options
1036
+ Sass::Engine.new(sass, options).render
1037
+ end
1038
+
1039
+ def env(hash = {})
1040
+ env = Sass::Environment.new
1041
+ hash.each {|k, v| env.set_var(k, v)}
1042
+ env
1043
+ end
1044
+
1045
+ def selector(str)
1046
+ parser = Sass::SCSS::StaticParser.new(
1047
+ str, filename_for_test, Sass::Importers::Filesystem.new('.'))
1048
+ parser.parse_selector
1049
+ end
1050
+
1051
+ def test_null_is_a_singleton
1052
+ assert_same Sass::Script::Value::Null.new, Sass::Script::Value::Null.new
1053
+ end
1054
+ end