sass 3.7.4 → 4.0.0.alpha.1

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