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,1281 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+ require File.dirname(__FILE__) + '/test_helper'
4
+ require 'sass/scss/css_parser'
5
+
6
+ # These tests just test the parsing of CSS
7
+ # (both standard and any hacks we intend to support).
8
+ # Tests of SCSS-specific behavior go in scss_test.rb.
9
+ class ScssCssTest < MiniTest::Test
10
+ include ScssTestHelper
11
+
12
+ def test_basic_scss
13
+ assert_parses <<SCSS
14
+ selector {
15
+ property: value;
16
+ property2: value; }
17
+ SCSS
18
+
19
+ assert_equal <<CSS, render('sel{p:v}')
20
+ sel {
21
+ p: v; }
22
+ CSS
23
+ end
24
+
25
+ def test_empty_rule
26
+ assert_equal "", render("#foo .bar {}")
27
+ assert_equal "", render(<<SCSS)
28
+ #foo .bar {
29
+ }
30
+ SCSS
31
+ end
32
+
33
+ def test_cdo_and_cdc_ignored_at_toplevel
34
+ assert_equal <<CSS, render(<<SCSS)
35
+ foo {
36
+ bar: baz; }
37
+
38
+ bar {
39
+ bar: baz; }
40
+
41
+ baz {
42
+ bar: baz; }
43
+ CSS
44
+ foo {bar: baz}
45
+ <!--
46
+ bar {bar: baz}
47
+ -->
48
+ baz {bar: baz}
49
+ SCSS
50
+ end
51
+
52
+ if Sass::Util.ruby1_8?
53
+ def test_unicode
54
+ assert_parses <<SCSS
55
+ @charset "UTF-8";
56
+ foo {
57
+ bar: föö bâr; }
58
+ SCSS
59
+ assert_parses <<SCSS
60
+ foo {
61
+ bar: föö bâr; }
62
+ SCSS
63
+ end
64
+ else
65
+ def test_unicode
66
+ assert_parses <<SCSS
67
+ @charset "UTF-8";
68
+ foo {
69
+ bar: föö bâr; }
70
+ SCSS
71
+ assert_equal <<CSS, render(<<SCSS)
72
+ @charset "UTF-8";
73
+ foo {
74
+ bar: föö bâr; }
75
+ CSS
76
+ foo {
77
+ bar: föö bâr; }
78
+ SCSS
79
+ end
80
+ end
81
+
82
+ def test_invisible_comments
83
+ assert_equal <<CSS, render(<<SCSS)
84
+ foo {
85
+ a: d; }
86
+ CSS
87
+ foo {a: /* b; c: */ d}
88
+ SCSS
89
+ assert_equal <<CSS, render(<<SCSS)
90
+ foo {
91
+ a: d; }
92
+ CSS
93
+ foo {a /*: b; c */: d}
94
+ SCSS
95
+ end
96
+
97
+ def test_crazy_comments
98
+ # http://www.w3.org/Style/CSS/Test/CSS2.1/current/xhtml1/t040109-c17-comments-00-b.xht
99
+ assert_equal <<CSS, render(<<SCSS)
100
+ /* This is a CSS comment. */
101
+ .one {
102
+ color: green; }
103
+
104
+ /* Another comment */
105
+ /* The following should not be used:
106
+ .two {color: red;} */
107
+ .three {
108
+ color: green;
109
+ /* color: red; */ }
110
+
111
+ /**
112
+ .four {color: red;} */
113
+ .five {
114
+ color: green; }
115
+
116
+ /**/
117
+ .six {
118
+ color: green; }
119
+
120
+ /*********/
121
+ .seven {
122
+ color: green; }
123
+
124
+ /* a comment **/
125
+ .eight {
126
+ color: green; }
127
+ CSS
128
+ /* This is a CSS comment. */
129
+ .one {color: green;} /* Another comment */
130
+ /* The following should not be used:
131
+ .two {color: red;} */
132
+ .three {color: green; /* color: red; */}
133
+ /**
134
+ .four {color: red;} */
135
+ .five {color: green;}
136
+ /**/
137
+ .six {color: green;}
138
+ /*********/
139
+ .seven {color: green;}
140
+ /* a comment **/
141
+ .eight {color: green;}
142
+ SCSS
143
+ end
144
+
145
+ def test_rule_comments
146
+ assert_parses <<SCSS
147
+ /* Foo */
148
+ .foo {
149
+ a: b; }
150
+ SCSS
151
+ assert_equal <<CSS, render(<<SCSS)
152
+ /* Foo
153
+ * Bar */
154
+ .foo {
155
+ a: b; }
156
+ CSS
157
+ /* Foo
158
+ * Bar */.foo {
159
+ a: b; }
160
+ SCSS
161
+ end
162
+
163
+ def test_property_comments
164
+ assert_parses <<SCSS
165
+ .foo {
166
+ /* Foo */
167
+ a: b; }
168
+ SCSS
169
+ assert_equal <<CSS, render(<<SCSS)
170
+ .foo {
171
+ /* Foo
172
+ * Bar */
173
+ a: b; }
174
+ CSS
175
+ .foo {
176
+ /* Foo
177
+ * Bar */a: b; }
178
+ SCSS
179
+ end
180
+
181
+ def test_selector_comments
182
+ assert_equal <<CSS, render(<<SCSS)
183
+ .foo #bar:baz(bip) {
184
+ a: b; }
185
+ CSS
186
+ .foo /* .a #foo */ #bar:baz(/* bang )*/ bip) {
187
+ a: b; }
188
+ SCSS
189
+ end
190
+
191
+ def test_lonely_comments
192
+ assert_parses <<SCSS
193
+ /* Foo
194
+ * Bar */
195
+ SCSS
196
+ assert_parses <<SCSS
197
+ .foo {
198
+ /* Foo
199
+ * Bar */ }
200
+ SCSS
201
+ end
202
+
203
+ def test_multiple_comments
204
+ assert_parses <<SCSS
205
+ /* Foo
206
+ * Bar */
207
+ /* Baz
208
+ * Bang */
209
+ SCSS
210
+ assert_parses <<SCSS
211
+ .foo {
212
+ /* Foo
213
+ * Bar */
214
+ /* Baz
215
+ * Bang */ }
216
+ SCSS
217
+ assert_equal <<CSS, render(<<SCSS)
218
+ .foo {
219
+ /* Foo Bar */
220
+ /* Baz Bang */ }
221
+ CSS
222
+ .foo {
223
+ /* Foo Bar *//* Baz Bang */ }
224
+ SCSS
225
+ end
226
+
227
+ def test_bizarrely_formatted_comments
228
+ assert_parses <<SCSS
229
+ .foo {
230
+ /* Foo
231
+ Bar
232
+ Baz */
233
+ a: b; }
234
+ SCSS
235
+ assert_parses <<SCSS
236
+ .foo {
237
+ /* Foo
238
+ Bar
239
+ Baz */
240
+ a: b; }
241
+ SCSS
242
+ assert_equal <<CSS, render(<<SCSS)
243
+ .foo {
244
+ /* Foo
245
+ Bar */
246
+ a: b; }
247
+ CSS
248
+ .foo {/* Foo
249
+ Bar */
250
+ a: b; }
251
+ SCSS
252
+ assert_equal <<CSS, render(<<SCSS)
253
+ .foo {
254
+ /* Foo
255
+ Bar
256
+ Baz */
257
+ a: b; }
258
+ CSS
259
+ .foo {/* Foo
260
+ Bar
261
+ Baz */
262
+ a: b; }
263
+ SCSS
264
+ end
265
+
266
+ ## Declarations
267
+
268
+ def test_vendor_properties
269
+ assert_parses <<SCSS
270
+ foo {
271
+ -moz-foo-bar: blat;
272
+ -o-flat-blang: wibble; }
273
+ SCSS
274
+ end
275
+
276
+ def test_empty_declarations
277
+ assert_equal <<CSS, render(<<SCSS)
278
+ foo {
279
+ bar: baz; }
280
+ CSS
281
+ foo {;;;;
282
+ bar: baz;;;;
283
+ ;;}
284
+ SCSS
285
+ end
286
+
287
+ def test_basic_property_types
288
+ assert_parses <<SCSS
289
+ foo {
290
+ a: 2;
291
+ b: 2.3em;
292
+ c: 50%;
293
+ d: "fraz bran";
294
+ e: flanny-blanny-blan;
295
+ f: url(http://sass-lang.com);
296
+ g: U+ffa?;
297
+ h: #aabbcc; }
298
+ SCSS
299
+ end
300
+
301
+ def test_functions
302
+ assert_parses <<SCSS
303
+ foo {
304
+ a: foo-bar(12);
305
+ b: -foo-bar-baz(13, 14 15); }
306
+ SCSS
307
+ end
308
+
309
+ def test_unary_minus
310
+ assert_parses <<SCSS
311
+ foo {
312
+ a: -2;
313
+ b: -2.3em;
314
+ c: -50%;
315
+ d: -foo(bar baz); }
316
+ SCSS
317
+ end
318
+
319
+ def test_operators
320
+ assert_parses <<SCSS
321
+ foo {
322
+ a: foo bar baz;
323
+ b: foo, #aabbcc, -12;
324
+ c: 1px/2px/-3px;
325
+ d: foo bar, baz/bang; }
326
+ SCSS
327
+ end
328
+
329
+ def test_important
330
+ assert_parses <<SCSS
331
+ foo {
332
+ a: foo !important;
333
+ b: foo bar !important;
334
+ b: foo, bar !important; }
335
+ SCSS
336
+ end
337
+
338
+ def test_initial_hyphen
339
+ assert_parses <<SCSS
340
+ foo {
341
+ a: -moz-bar-baz;
342
+ b: foo -o-bar-baz; }
343
+ SCSS
344
+ end
345
+
346
+ def test_ms_long_filter_syntax
347
+ assert_equal <<CSS, render(<<SCSS)
348
+ foo {
349
+ filter: progid:DXImageTransform.Microsoft.gradient(GradientType=1, startColorstr=#c0ff3300, endColorstr=#ff000000);
350
+ filter: progid:DXImageTransform.Microsoft.gradient(GradientType=1, startColorstr=#c0ff3300, endColorstr=#ff000000); }
351
+ CSS
352
+ foo {
353
+ filter: progid:DXImageTransform.Microsoft.gradient(GradientType=1, startColorstr=#c0ff3300, endColorstr=#ff000000);
354
+ filter:progid:DXImageTransform.Microsoft.gradient(GradientType=1, startColorstr=#c0ff3300, endColorstr=#ff000000); }
355
+ SCSS
356
+ end
357
+
358
+ def test_ms_short_filter_syntax
359
+ assert_parses <<SCSS
360
+ foo {
361
+ filter: alpha(opacity=20);
362
+ filter: alpha(opacity=20, enabled=true);
363
+ filter: blaznicate(foo=bar, baz=bang bip, bart=#fa4600); }
364
+ SCSS
365
+ end
366
+
367
+ def test_declaration_hacks
368
+ assert_parses <<SCSS
369
+ foo {
370
+ _name: val;
371
+ *name: val;
372
+ :name: val;
373
+ .name: val;
374
+ #name: val;
375
+ name/**/: val;
376
+ name/*\\**/: val;
377
+ name: val; }
378
+ SCSS
379
+ end
380
+
381
+ def test_trailing_hash_hack
382
+ assert_parses <<SCSS
383
+ foo {
384
+ foo: bar;
385
+ #baz: bang;
386
+ #bip: bop; }
387
+ SCSS
388
+ end
389
+
390
+ def test_zero_arg_functions
391
+ assert_parses <<SCSS
392
+ foo {
393
+ a: foo();
394
+ b: bar baz-bang() bip; }
395
+ SCSS
396
+ end
397
+
398
+ def test_expression_function
399
+ assert_parses <<SCSS
400
+ foo {
401
+ a: 12px expression(1 + (3 / Foo.bar("baz" + "bang") + function() {return 12;}) % 12); }
402
+ SCSS
403
+ end
404
+
405
+ def test_calc_function
406
+ assert_parses <<SCSS
407
+ foo {
408
+ a: 12px calc(100%/3 - 2*1em - 2*1px);
409
+ b: 12px -moz-calc(100%/3 - 2*1em - 2*1px);
410
+ b: 12px -webkit-calc(100%/3 - 2*1em - 2*1px);
411
+ b: 12px -foobar-calc(100%/3 - 2*1em - 2*1px); }
412
+ SCSS
413
+ end
414
+
415
+ def test_element_function
416
+ assert_parses <<SCSS
417
+ foo {
418
+ a: -moz-element(#foo);
419
+ b: -webkit-element(#foo);
420
+ b: -foobar-element(#foo); }
421
+ SCSS
422
+ end
423
+
424
+ def test_unary_ops
425
+ assert_equal <<CSS, render(<<SCSS)
426
+ foo {
427
+ a: -0.5em;
428
+ b: +0.5em;
429
+ c: -foo(12px);
430
+ d: +foo(12px); }
431
+ CSS
432
+ foo {
433
+ a: -0.5em;
434
+ b: +0.5em;
435
+ c: -foo(12px);
436
+ d: +foo(12px); }
437
+ SCSS
438
+ end
439
+
440
+ def test_css_string_escapes
441
+ assert_parses <<SCSS
442
+ foo {
443
+ a: "\\foo bar";
444
+ b: "foo\\ bar";
445
+ c: "\\2022 \\0020";
446
+ d: "foo\\\\bar";
447
+ e: "foo\\"'bar"; }
448
+ SCSS
449
+ end
450
+
451
+ def test_css_ident_escapes
452
+ assert_parses <<SCSS
453
+ foo {
454
+ a: \\foo bar;
455
+ b: foo\\ bar;
456
+ c: \\2022 \\0020;
457
+ d: foo\\\\bar;
458
+ e: foo\\"\\'bar; }
459
+ SCSS
460
+ end
461
+
462
+ ## Directives
463
+
464
+ def test_namespace_directive
465
+ assert_parses '@namespace "http://www.w3.org/Profiles/xhtml1-strict";'
466
+ assert_parses '@namespace url(http://www.w3.org/Profiles/xhtml1-strict);'
467
+ assert_parses '@namespace html url("http://www.w3.org/Profiles/xhtml1-strict");'
468
+ end
469
+
470
+ def test_media_directive
471
+ assert_parses <<SCSS
472
+ @media all {
473
+ rule1 {
474
+ prop: val; }
475
+
476
+ rule2 {
477
+ prop: val; } }
478
+ SCSS
479
+ assert_parses <<SCSS
480
+ @media screen, print {
481
+ rule1 {
482
+ prop: val; }
483
+
484
+ rule2 {
485
+ prop: val; } }
486
+ SCSS
487
+ end
488
+
489
+ def test_media_directive_with_keywords
490
+ assert_parses <<SCSS
491
+ @media screen and (-webkit-min-device-pixel-ratio: 0) {
492
+ a: b; }
493
+ SCSS
494
+ assert_parses <<SCSS
495
+ @media only screen, print and (foo: 0px) and (bar: flam(12px solid)) {
496
+ a: b; }
497
+ SCSS
498
+ end
499
+
500
+ def test_import_directive
501
+ assert_parses '@import "foo.css";'
502
+ assert_parses '@import url("foo.css");'
503
+ assert_parses "@import url('foo.css');"
504
+ assert_parses '@import url(foo.css);'
505
+
506
+ assert_equal <<CSS, render(<<SCSS)
507
+ @import "foo.css";
508
+ CSS
509
+ @import 'foo.css';
510
+ SCSS
511
+ end
512
+
513
+ def test_import_directive_with_backslash_newline
514
+ assert_equal <<CSS, render(<<SCSS)
515
+ @import "foobar.css";
516
+ CSS
517
+ @import "foo\\
518
+ bar.css";
519
+ SCSS
520
+ end
521
+
522
+ def test_string_import_directive_with_media
523
+ assert_parses '@import "foo.css" screen;'
524
+ assert_parses '@import "foo.css" screen, print;'
525
+ assert_parses '@import "foo.css" screen, print and (foo: 0);'
526
+ assert_parses '@import "foo.css" screen, only print, screen and (foo: 0);'
527
+ end
528
+
529
+ def test_url_import_directive_with_media
530
+ assert_parses '@import url("foo.css") screen;'
531
+ assert_parses '@import url("foo.css") screen, print;'
532
+ assert_parses '@import url("foo.css") screen, print and (foo: 0);'
533
+ assert_parses '@import url("foo.css") screen, only print, screen and (foo: 0);'
534
+ end
535
+
536
+ def test_page_directive
537
+ assert_parses <<SCSS
538
+ @page {
539
+ prop1: val;
540
+ prop2: val; }
541
+ SCSS
542
+ assert_parses <<SCSS
543
+ @page flap {
544
+ prop1: val;
545
+ prop2: val; }
546
+ SCSS
547
+ assert_parses <<SCSS
548
+ @page :first {
549
+ prop1: val;
550
+ prop2: val; }
551
+ SCSS
552
+ assert_parses <<SCSS
553
+ @page flap:first {
554
+ prop1: val;
555
+ prop2: val; }
556
+ SCSS
557
+ end
558
+
559
+ def test_blockless_directive_without_semicolon
560
+ assert_equal "@foo \"bar\";\n", render('@foo "bar"')
561
+ end
562
+
563
+ def test_directive_with_lots_of_whitespace
564
+ assert_equal "@foo \"bar\";\n", render('@foo "bar" ;')
565
+ end
566
+
567
+ def test_empty_blockless_directive
568
+ assert_parses "@foo;"
569
+ end
570
+
571
+ def test_multiple_blockless_directives
572
+ assert_parses <<SCSS
573
+ @foo bar;
574
+ @bar baz;
575
+ SCSS
576
+ end
577
+
578
+ def test_empty_block_directive
579
+ assert_parses "@foo {}"
580
+ assert_equal "@foo {}\n", render(<<SCSS)
581
+ @foo {
582
+ }
583
+ SCSS
584
+ end
585
+
586
+ def test_multiple_block_directives
587
+ assert_parses <<SCSS
588
+ @foo bar {
589
+ a: b; }
590
+ @bar baz {
591
+ c: d; }
592
+ SCSS
593
+ end
594
+
595
+ def test_block_directive_with_rule_and_property
596
+ assert_parses <<SCSS
597
+ @foo {
598
+ rule {
599
+ a: b; }
600
+
601
+ a: b; }
602
+ SCSS
603
+ end
604
+
605
+ def test_block_directive_with_semicolon
606
+ assert_equal <<CSS, render(<<SCSS)
607
+ @foo {
608
+ a: b; }
609
+ @bar {
610
+ a: b; }
611
+ CSS
612
+ @foo {a:b};
613
+ @bar {a:b};
614
+ SCSS
615
+ end
616
+
617
+ def test_moz_document_directive
618
+ assert_equal <<CSS, render(<<SCSS)
619
+ @-moz-document url(http://www.w3.org/),
620
+ url-prefix(http://www.w3.org/Style/),
621
+ domain(mozilla.org),
622
+ regexp("^https:.*") {
623
+ .foo {
624
+ a: b; } }
625
+ CSS
626
+ @-moz-document url(http://www.w3.org/),
627
+ url-prefix(http://www.w3.org/Style/),
628
+ domain(mozilla.org),
629
+ regexp("^https:.*") {
630
+ .foo {a: b}
631
+ }
632
+ SCSS
633
+ end
634
+
635
+ def test_supports
636
+ assert_equal <<CSS, render(<<SCSS)
637
+ @supports (((a: b) and (c: d)) or (not (d: e))) and ((not (f: g)) or (not ((h: i) and (j: k)))) {
638
+ .foo {
639
+ a: b; } }
640
+ @supports (a: b) {
641
+ .foo {
642
+ a: b; } }
643
+ CSS
644
+ @supports (((a: b) and (c: d)) or (not (d: e))) and ((not (f: g)) or (not ((h: i) and (j: k)))) {
645
+ .foo {
646
+ a: b;
647
+ }
648
+ }
649
+
650
+ @supports (a: b) {
651
+ .foo {
652
+ a: b;
653
+ }
654
+ }
655
+ SCSS
656
+ end
657
+
658
+ def test_supports_with_prefix
659
+ assert_equal <<CSS, render(<<SCSS)
660
+ @-prefix-supports (((a: b) and (c: d)) or (not (d: e))) and ((not (f: g)) or (not ((h: i) and (j: k)))) {
661
+ .foo {
662
+ a: b; } }
663
+ CSS
664
+ @-prefix-supports (((a: b) and (c: d)) or (not (d: e))) and ((not (f: g)) or (not ((h: i) and (j: k)))) {
665
+ .foo {
666
+ a: b;
667
+ }
668
+ }
669
+ SCSS
670
+ end
671
+
672
+ def test_supports_allows_similar_operators_without_parens
673
+ assert_equal <<CSS, render(<<SCSS)
674
+ @supports (a: b) and (c: d) and (e: f) {
675
+ .foo {
676
+ a: b; } }
677
+ @supports (a: b) or (c: d) or (e: f) {
678
+ .foo {
679
+ a: b; } }
680
+ CSS
681
+ @supports (a: b) and (c: d) and (e: f) {
682
+ .foo {
683
+ a: b;
684
+ }
685
+ }
686
+
687
+ @supports (a: b) or (c: d) or (e: f) {
688
+ .foo {
689
+ a: b;
690
+ }
691
+ }
692
+ SCSS
693
+ end
694
+
695
+ def test_keyframes
696
+ assert_equal <<CSS, render(<<SCSS)
697
+ @keyframes identifier {
698
+ 0% {
699
+ top: 0;
700
+ left: 0; }
701
+ 30% {
702
+ top: 50px; }
703
+ 68%, 72% {
704
+ left: 50px; }
705
+ 100% {
706
+ top: 100px;
707
+ left: 100%; } }
708
+ CSS
709
+ @keyframes identifier {
710
+ 0% {top: 0; left: 0}
711
+ 30% {top: 50px}
712
+ 68%, 72% {left: 50px}
713
+ 100% {top: 100px; left: 100%}
714
+ }
715
+ SCSS
716
+ end
717
+
718
+ def test_keyframes_with_empty_rules
719
+ assert_equal <<CSS, render(<<SCSS)
720
+ @keyframes identifier {
721
+ 50% {
722
+ background-color: black; } }
723
+ CSS
724
+ @keyframes identifier {
725
+ 0% {}
726
+ 50% {background-color: black}
727
+ 100% {}
728
+ }
729
+ SCSS
730
+ end
731
+
732
+ def test_keyframes_with_custom_identifiers
733
+ assert_equal <<CSS, render(<<SCSS)
734
+ @-skrollr-keyframes identifier {
735
+ center-top {
736
+ left: 100%; }
737
+ top-bottom {
738
+ left: 0%; } }
739
+ CSS
740
+ @-skrollr-keyframes identifier {
741
+ center-top {
742
+ left: 100%;
743
+ }
744
+
745
+ top-bottom {
746
+ left: 0%;
747
+ }
748
+ }
749
+
750
+ SCSS
751
+ end
752
+
753
+ ## Selectors
754
+
755
+ # Taken from http://dev.w3.org/csswg/selectors4/#overview
756
+ def test_summarized_selectors_with_element
757
+ assert_selector_parses('*')
758
+ assert_selector_parses('E')
759
+ assert_selector_parses('E:not(s)')
760
+ assert_selector_parses('E:not(s1, s2)')
761
+ assert_selector_parses('E:matches(s1, s2)')
762
+ assert_selector_parses('E:has(s1, s2)')
763
+ assert_selector_parses('E:has(> s1, > s2)')
764
+ assert_selector_parses('E.warning')
765
+ assert_selector_parses('E#myid')
766
+ assert_selector_parses('E[foo]')
767
+ assert_selector_parses('E[foo="bar"]')
768
+ assert_selector_parses('E[foo="bar" i]')
769
+ assert_selector_parses('E[foo~="bar"]')
770
+ assert_selector_parses('E[foo^="bar"]')
771
+ assert_selector_parses('E[foo$="bar"]')
772
+ assert_selector_parses('E[foo*="bar"]')
773
+ assert_selector_parses('E[foo|="en"]')
774
+ assert_selector_parses('E:dir(ltr)')
775
+ assert_selector_parses('E:lang(fr)')
776
+ assert_selector_parses('E:lang(zh, *-hant)')
777
+ assert_selector_parses('E:any-link')
778
+ assert_selector_parses('E:link')
779
+ assert_selector_parses('E:visited')
780
+ assert_selector_parses('E:local-link')
781
+ assert_selector_parses('E:local-link(0)')
782
+ assert_selector_parses('E:target')
783
+ assert_selector_parses('E:scope')
784
+ assert_selector_parses('E:current')
785
+ assert_selector_parses('E:current(s)')
786
+ assert_selector_parses('E:past')
787
+ assert_selector_parses('E:future')
788
+ assert_selector_parses('E:active')
789
+ assert_selector_parses('E:hover')
790
+ assert_selector_parses('E:focus')
791
+ assert_selector_parses('E:enabled')
792
+ assert_selector_parses('E:disabled')
793
+ assert_selector_parses('E:checked')
794
+ assert_selector_parses('E:indeterminate')
795
+ assert_selector_parses('E:default')
796
+ assert_selector_parses('E:in-range')
797
+ assert_selector_parses('E:out-of-range')
798
+ assert_selector_parses('E:required')
799
+ assert_selector_parses('E:optional')
800
+ assert_selector_parses('E:read-only')
801
+ assert_selector_parses('E:read-write')
802
+ assert_selector_parses('E:root')
803
+ assert_selector_parses('E:empty')
804
+ assert_selector_parses('E:first-child')
805
+ assert_selector_parses('E:nth-child(n)')
806
+ assert_selector_parses('E:last-child')
807
+ assert_selector_parses('E:nth-last-child(n)')
808
+ assert_selector_parses('E:only-child')
809
+ assert_selector_parses('E:first-of-type')
810
+ assert_selector_parses('E:nth-of-type(n)')
811
+ assert_selector_parses('E:last-of-type')
812
+ assert_selector_parses('E:nth-last-of-type(n)')
813
+ assert_selector_parses('E:only-of-type')
814
+ assert_selector_parses('E:nth-child(n of selector)')
815
+ assert_selector_parses('E:nth-last-child(n of selector)')
816
+ assert_selector_parses('E:nth-child(n)')
817
+ assert_selector_parses('E:nth-last-child(n)')
818
+ assert_selector_parses('E F')
819
+ assert_selector_parses('E > F')
820
+ assert_selector_parses('E + F')
821
+ assert_selector_parses('E ~ F')
822
+ assert_selector_parses('E /foo/ F')
823
+ silence_warnings {assert_selector_parses('E! > F')}
824
+
825
+ assert_selector_parses('E /ns|foo/ F')
826
+
827
+ # From http://dev.w3.org/csswg/css-scoping-1/
828
+ assert_selector_parses('E:host(s)')
829
+ assert_selector_parses('E:host-context(s)')
830
+ end
831
+
832
+ # Taken from http://dev.w3.org/csswg/selectors4/#overview, but without element
833
+ # names.
834
+ def test_more_summarized_selectors
835
+ assert_selector_parses(':not(s)')
836
+ assert_selector_parses(':not(s1, s2)')
837
+ assert_selector_parses(':matches(s1, s2)')
838
+ assert_selector_parses(':has(s1, s2)')
839
+ assert_selector_parses(':has(> s1, > s2)')
840
+ assert_selector_parses('.warning')
841
+ assert_selector_parses('#myid')
842
+ assert_selector_parses('[foo]')
843
+ assert_selector_parses('[foo="bar"]')
844
+ assert_selector_parses('[foo="bar" i]')
845
+ assert_selector_parses('[foo~="bar"]')
846
+ assert_selector_parses('[foo^="bar"]')
847
+ assert_selector_parses('[foo$="bar"]')
848
+ assert_selector_parses('[foo*="bar"]')
849
+ assert_selector_parses('[foo|="en"]')
850
+ assert_selector_parses(':dir(ltr)')
851
+ assert_selector_parses(':lang(fr)')
852
+ assert_selector_parses(':lang(zh, *-hant)')
853
+ assert_selector_parses(':any-link')
854
+ assert_selector_parses(':link')
855
+ assert_selector_parses(':visited')
856
+ assert_selector_parses(':local-link')
857
+ assert_selector_parses(':local-link(0)')
858
+ assert_selector_parses(':target')
859
+ assert_selector_parses(':scope')
860
+ assert_selector_parses(':current')
861
+ assert_selector_parses(':current(s)')
862
+ assert_selector_parses(':past')
863
+ assert_selector_parses(':future')
864
+ assert_selector_parses(':active')
865
+ assert_selector_parses(':hover')
866
+ assert_selector_parses(':focus')
867
+ assert_selector_parses(':enabled')
868
+ assert_selector_parses(':disabled')
869
+ assert_selector_parses(':checked')
870
+ assert_selector_parses(':indeterminate')
871
+ assert_selector_parses(':default')
872
+ assert_selector_parses(':in-range')
873
+ assert_selector_parses(':out-of-range')
874
+ assert_selector_parses(':required')
875
+ assert_selector_parses(':optional')
876
+ assert_selector_parses(':read-only')
877
+ assert_selector_parses(':read-write')
878
+ assert_selector_parses(':root')
879
+ assert_selector_parses(':empty')
880
+ assert_selector_parses(':first-child')
881
+ assert_selector_parses(':nth-child(n)')
882
+ assert_selector_parses(':last-child')
883
+ assert_selector_parses(':nth-last-child(n)')
884
+ assert_selector_parses(':only-child')
885
+ assert_selector_parses(':first-of-type')
886
+ assert_selector_parses(':nth-of-type(n)')
887
+ assert_selector_parses(':last-of-type')
888
+ assert_selector_parses(':nth-last-of-type(n)')
889
+ assert_selector_parses(':only-of-type')
890
+ assert_selector_parses(':nth-child(n of selector)')
891
+ assert_selector_parses(':nth-last-child(n of selector)')
892
+ assert_selector_parses(':nth-child(n)')
893
+ assert_selector_parses(':nth-last-child(n)')
894
+
895
+ # From http://dev.w3.org/csswg/css-scoping-1/
896
+ assert_selector_parses(':host(s)')
897
+ assert_selector_parses(':host-context(s)')
898
+ end
899
+
900
+ def test_attribute_selectors_with_identifiers
901
+ assert_selector_parses('[foo~=bar]')
902
+ assert_selector_parses('[foo^=bar]')
903
+ assert_selector_parses('[foo$=bar]')
904
+ assert_selector_parses('[foo*=bar]')
905
+ assert_selector_parses('[foo|=en]')
906
+ end
907
+
908
+ def test_nth_selectors
909
+ assert_selector_parses(':nth-child(-n)')
910
+ assert_selector_parses(':nth-child(+n)')
911
+
912
+ assert_selector_parses(':nth-child(even)')
913
+ assert_selector_parses(':nth-child(odd)')
914
+
915
+ assert_selector_parses(':nth-child(50)')
916
+ assert_selector_parses(':nth-child(-50)')
917
+ assert_selector_parses(':nth-child(+50)')
918
+
919
+ assert_selector_parses(':nth-child(2n+3)')
920
+ assert_selector_parses(':nth-child(2n-3)')
921
+ assert_selector_parses(':nth-child(+2n-3)')
922
+ assert_selector_parses(':nth-child(-2n+3)')
923
+ assert_selector_parses(':nth-child(-2n+ 3)')
924
+
925
+ assert_equal(<<CSS, render(<<SCSS))
926
+ :nth-child(2n + 3) {
927
+ a: b; }
928
+ CSS
929
+ :nth-child( 2n + 3 ) {
930
+ a: b; }
931
+ SCSS
932
+ end
933
+
934
+ def test_selectors_containing_selectors
935
+ assert_selector_can_contain_selectors(':not(<sel>)')
936
+ assert_selector_can_contain_selectors(':current(<sel>)')
937
+ assert_selector_can_contain_selectors(':nth-child(n of <sel>)')
938
+ assert_selector_can_contain_selectors(':nth-last-child(n of <sel>)')
939
+ assert_selector_can_contain_selectors(':-moz-any(<sel>)')
940
+ assert_selector_can_contain_selectors(':has(<sel>)')
941
+ assert_selector_can_contain_selectors(':has(+ <sel>)')
942
+ assert_selector_can_contain_selectors(':host(<sel>)')
943
+ assert_selector_can_contain_selectors(':host-context(<sel>)')
944
+ end
945
+
946
+ def assert_selector_can_contain_selectors(sel)
947
+ try = lambda {|subsel| assert_selector_parses(sel.gsub('<sel>', subsel))}
948
+
949
+ try['foo|bar']
950
+ try['*|bar']
951
+
952
+ try['foo|*']
953
+ try['*|*']
954
+
955
+ try['#blah']
956
+ try['.blah']
957
+
958
+ try['[foo]']
959
+ try['[foo^="bar"]']
960
+ try['[baz|foo~="bar"]']
961
+
962
+ try[':hover']
963
+ try[':nth-child(2n + 3)']
964
+
965
+ try['h1, h2, h3']
966
+ try['#foo, bar, [baz]']
967
+
968
+ # Not technically allowed for most selectors, but what the heck
969
+ try[':not(#foo)']
970
+ try['a#foo.bar']
971
+ try['#foo .bar > baz']
972
+ end
973
+
974
+ def test_namespaced_selectors
975
+ assert_selector_parses('foo|E')
976
+ assert_selector_parses('*|E')
977
+ assert_selector_parses('foo|*')
978
+ assert_selector_parses('*|*')
979
+ end
980
+
981
+ def test_namespaced_attribute_selectors
982
+ assert_selector_parses('[foo|bar=baz]')
983
+ assert_selector_parses('[*|bar=baz]')
984
+ assert_selector_parses('[foo|bar|=baz]')
985
+ end
986
+
987
+ def test_comma_selectors
988
+ assert_selector_parses('E, F')
989
+ assert_selector_parses('E F, G H')
990
+ assert_selector_parses('E > F, G > H')
991
+ end
992
+
993
+ def test_selectors_with_newlines
994
+ assert_selector_parses("E,\nF")
995
+ assert_selector_parses("E\nF")
996
+ assert_selector_parses("E, F\nG, H")
997
+ end
998
+
999
+ def test_expression_fallback_selectors
1000
+ assert_directive_parses('0%')
1001
+ assert_directive_parses('60%')
1002
+ assert_directive_parses('100%')
1003
+ assert_directive_parses('12px')
1004
+ assert_directive_parses('"foo"')
1005
+ end
1006
+
1007
+ def test_functional_pseudo_selectors
1008
+ assert_selector_parses(':foo("bar")')
1009
+ assert_selector_parses(':foo(bar)')
1010
+ assert_selector_parses(':foo(12px)')
1011
+ assert_selector_parses(':foo(+)')
1012
+ assert_selector_parses(':foo(-)')
1013
+ assert_selector_parses(':foo(+"bar")')
1014
+ assert_selector_parses(':foo(-++--baz-"bar"12px)')
1015
+ end
1016
+
1017
+ def test_selector_hacks
1018
+ assert_selector_parses('> E')
1019
+ assert_selector_parses('+ E')
1020
+ assert_selector_parses('~ E')
1021
+ assert_selector_parses('> > E')
1022
+ assert_equal <<CSS, render(<<SCSS)
1023
+ > > E {
1024
+ a: b; }
1025
+ CSS
1026
+ >> E {
1027
+ a: b; }
1028
+ SCSS
1029
+
1030
+ assert_selector_parses('E*')
1031
+ assert_selector_parses('E*.foo')
1032
+ assert_selector_parses('E*:hover')
1033
+ end
1034
+
1035
+ def test_spaceless_combo_selectors
1036
+ assert_equal "E > F {\n a: b; }\n", render("E>F { a: b;} ")
1037
+ assert_equal "E ~ F {\n a: b; }\n", render("E~F { a: b;} ")
1038
+ assert_equal "E + F {\n a: b; }\n", render("E+F { a: b;} ")
1039
+ end
1040
+
1041
+ def test_escapes_in_selectors
1042
+ assert_selector_parses('.\!foo')
1043
+ assert_selector_parses('.\66 foo')
1044
+ assert_selector_parses('.\21 foo')
1045
+ end
1046
+
1047
+ def test_subject_selector_deprecation
1048
+ assert_warning(<<WARNING) {render(".foo .bar! .baz {a: b}")}
1049
+ DEPRECATION WARNING on line 1, column 1:
1050
+ The subject selector operator "!" is deprecated and will be removed in a future release.
1051
+ This operator has been replaced by ":has()" in the CSS spec.
1052
+ For example: .foo .bar:has(.baz)
1053
+ WARNING
1054
+
1055
+ assert_warning(<<WARNING) {render(".foo .bar! > .baz {a: b}")}
1056
+ DEPRECATION WARNING on line 1, column 1:
1057
+ The subject selector operator "!" is deprecated and will be removed in a future release.
1058
+ This operator has been replaced by ":has()" in the CSS spec.
1059
+ For example: .foo .bar:has(> .baz)
1060
+ WARNING
1061
+
1062
+ assert_warning(<<WARNING) {render(".foo .bar! {a: b}")}
1063
+ DEPRECATION WARNING on line 1, column 1:
1064
+ The subject selector operator "!" is deprecated and will be removed in a future release.
1065
+ This operator has been replaced by ":has()" in the CSS spec.
1066
+ For example: .foo .bar
1067
+ WARNING
1068
+ end
1069
+
1070
+ ## Errors
1071
+
1072
+ def test_invalid_directives
1073
+ assert_not_parses("identifier", '@<err> import "foo";')
1074
+ assert_not_parses("identifier", '@<err>12 "foo";')
1075
+ end
1076
+
1077
+ def test_invalid_classes
1078
+ assert_not_parses("class name", 'p.<err> foo {a: b}')
1079
+ assert_not_parses("class name", 'p.<err>1foo {a: b}')
1080
+ end
1081
+
1082
+ def test_invalid_ids
1083
+ assert_not_parses("id name", 'p#<err> foo {a: b}')
1084
+ end
1085
+
1086
+ def test_no_properties_at_toplevel
1087
+ assert_not_parses('pseudoclass or pseudoelement', 'a:<err> b;')
1088
+ end
1089
+
1090
+ def test_no_scss_directives
1091
+ assert_parses('@import "foo.sass";')
1092
+ assert_parses <<SCSS
1093
+ @mixin foo {
1094
+ a: b; }
1095
+ SCSS
1096
+ end
1097
+
1098
+ def test_no_variables
1099
+ assert_not_parses("selector or at-rule", "<err>$var = 12;")
1100
+ assert_not_parses('"}"', "foo { <err>!var = 12; }")
1101
+ end
1102
+
1103
+ def test_no_parent_selectors
1104
+ assert_not_parses('"{"', "foo <err>&.bar {a: b}")
1105
+ end
1106
+
1107
+ def test_no_selector_interpolation
1108
+ assert_not_parses('"{"', 'foo <err>#{"bar"}.baz {a: b}')
1109
+ end
1110
+
1111
+ def test_no_prop_name_interpolation
1112
+ assert_not_parses('":"', 'foo {a<err>#{"bar"}baz: b}')
1113
+ end
1114
+
1115
+ def test_no_prop_val_interpolation
1116
+ assert_not_parses('"}"', 'foo {a: b <err>#{"bar"} c}')
1117
+ end
1118
+
1119
+ def test_no_string_interpolation
1120
+ assert_parses <<SCSS
1121
+ foo {
1122
+ a: "bang \#{1 + " bar "} bip"; }
1123
+ SCSS
1124
+ end
1125
+
1126
+ def test_no_sass_script_values
1127
+ assert_not_parses('"}"', 'foo {a: b <err>* c}')
1128
+ end
1129
+
1130
+ def test_no_nested_rules
1131
+ assert_not_parses('":"', 'foo {bar <err>{a: b}}')
1132
+ assert_not_parses('"}"', 'foo {<err>[bar=baz] {a: b}}')
1133
+ end
1134
+
1135
+ def test_no_nested_properties
1136
+ assert_not_parses('expression (e.g. 1px, bold)', 'foo {bar: <err>{a: b}}')
1137
+ assert_not_parses('expression (e.g. 1px, bold)', 'foo {bar: bang <err>{a: b}}')
1138
+ end
1139
+
1140
+ def test_no_nested_directives
1141
+ assert_not_parses('"}"', 'foo {<err>@bar {a: b}}')
1142
+ end
1143
+
1144
+ def test_error_with_windows_newlines
1145
+ render <<SCSS
1146
+ foo {bar}\r
1147
+ baz {a: b}
1148
+ SCSS
1149
+ assert(false, "Expected syntax error")
1150
+ rescue Sass::SyntaxError => e
1151
+ assert_equal 'Invalid CSS after "foo {bar": expected ":", was "}"', e.message
1152
+ assert_equal 1, e.sass_line
1153
+ end
1154
+
1155
+ def test_newline_in_property_value
1156
+ assert_equal(<<CSS, render(<<SCSS))
1157
+ .foo {
1158
+ bar: "bazbang"; }
1159
+ CSS
1160
+ .foo {
1161
+ bar: "baz\\
1162
+ bang";
1163
+ }
1164
+ SCSS
1165
+ end
1166
+
1167
+ ## Regressions
1168
+
1169
+ def test_very_long_comment_doesnt_take_forever
1170
+ string = 'asdf' * (100000)
1171
+ assert_equal(<<CSS, render(<<SCSS))
1172
+ /*
1173
+ #{string}
1174
+ */
1175
+ CSS
1176
+ /*
1177
+ #{string}
1178
+ */
1179
+ SCSS
1180
+ end
1181
+
1182
+ def test_long_unclosed_comment_doesnt_take_forever
1183
+ assert_raise_message(Sass::SyntaxError,
1184
+ 'Invalid CSS after "/*": expected "/", was "//*************..."') {render(<<SCSS)}
1185
+ /*
1186
+ //**************************************************************************
1187
+ SCSS
1188
+ end
1189
+
1190
+ def test_double_space_string
1191
+ assert_equal(<<CSS, render(<<SCSS))
1192
+ .a {
1193
+ content: " a"; }
1194
+ CSS
1195
+ .a {
1196
+ content: " a";
1197
+ }
1198
+ SCSS
1199
+ end
1200
+
1201
+ def test_very_long_number_with_important_doesnt_take_forever
1202
+ assert_equal(<<CSS, render(<<SCSS))
1203
+ .foo {
1204
+ width: 97.916666666666666666666666666667% !important; }
1205
+ CSS
1206
+ .foo {
1207
+ width: 97.916666666666666666666666666667% !important;
1208
+ }
1209
+ SCSS
1210
+ end
1211
+
1212
+ def test_selector_without_closing_bracket
1213
+ assert_not_parses('"]"', "foo[bar <err>{a: b}")
1214
+ end
1215
+
1216
+ def test_closing_line_comment_end_with_compact_output
1217
+ assert_equal(<<CSS, render(<<SCSS, :style => :compact))
1218
+ /* foo */
1219
+ bar { baz: bang; }
1220
+ CSS
1221
+ /*
1222
+ * foo
1223
+ */
1224
+ bar {baz: bang}
1225
+ SCSS
1226
+ end
1227
+
1228
+ def test_single_line_comment_within_multiline_comment
1229
+ assert_equal(<<CSS, render(<<SCSS))
1230
+ body {
1231
+ /*
1232
+ //comment here
1233
+ */ }
1234
+ CSS
1235
+ body {
1236
+ /*
1237
+ //comment here
1238
+ */
1239
+ }
1240
+ SCSS
1241
+ end
1242
+
1243
+ def test_malformed_media
1244
+ render <<SCSS
1245
+ @media {
1246
+ margin: 0;
1247
+ }
1248
+ SCSS
1249
+ assert(false, "Expected syntax error")
1250
+ rescue Sass::SyntaxError => e
1251
+ assert_equal 'Invalid CSS after "@media ": expected media query (e.g. print, screen, print and screen), was "{"', e.message
1252
+ assert_equal 1, e.sass_line
1253
+ end
1254
+
1255
+ private
1256
+
1257
+ def assert_selector_parses(selector)
1258
+ assert_parses <<SCSS
1259
+ #{selector} {
1260
+ a: b; }
1261
+ SCSS
1262
+
1263
+ assert_parses <<SCSS
1264
+ :not(#{selector}) {
1265
+ a: b; }
1266
+ SCSS
1267
+ end
1268
+
1269
+ def assert_directive_parses(param)
1270
+ assert_parses <<SCSS
1271
+ @unknown #{param} {
1272
+ a: b; }
1273
+ SCSS
1274
+ end
1275
+
1276
+ def render(scss, options = {})
1277
+ tree = Sass::SCSS::CssParser.new(scss, options[:filename], nil).parse
1278
+ tree.options = Sass::Engine::DEFAULT_OPTIONS.merge(options)
1279
+ tree.render
1280
+ end
1281
+ end