sass 3.7.4 → 4.0.0.alpha.1

Sign up to get free protection for your applications and to get access to all the features.
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