haml-edge 2.3.179 → 2.3.180

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. data/EDGE_GEM_VERSION +1 -1
  2. data/README.md +88 -149
  3. data/VERSION +1 -1
  4. data/bin/css2sass +7 -1
  5. data/bin/sass-convert +7 -0
  6. data/lib/haml/exec.rb +95 -22
  7. data/lib/haml/template.rb +1 -1
  8. data/lib/haml/util.rb +50 -0
  9. data/lib/sass.rb +1 -1
  10. data/lib/sass/css.rb +38 -210
  11. data/lib/sass/engine.rb +121 -47
  12. data/lib/sass/files.rb +28 -19
  13. data/lib/sass/plugin.rb +32 -43
  14. data/lib/sass/repl.rb +1 -1
  15. data/lib/sass/script.rb +25 -6
  16. data/lib/sass/script/bool.rb +1 -0
  17. data/lib/sass/script/color.rb +2 -2
  18. data/lib/sass/script/css_lexer.rb +22 -0
  19. data/lib/sass/script/css_parser.rb +28 -0
  20. data/lib/sass/script/funcall.rb +17 -9
  21. data/lib/sass/script/functions.rb +46 -1
  22. data/lib/sass/script/interpolation.rb +42 -0
  23. data/lib/sass/script/lexer.rb +142 -34
  24. data/lib/sass/script/literal.rb +28 -12
  25. data/lib/sass/script/node.rb +57 -1
  26. data/lib/sass/script/number.rb +18 -3
  27. data/lib/sass/script/operation.rb +44 -8
  28. data/lib/sass/script/parser.rb +149 -24
  29. data/lib/sass/script/string.rb +50 -2
  30. data/lib/sass/script/unary_operation.rb +25 -10
  31. data/lib/sass/script/variable.rb +20 -11
  32. data/lib/sass/scss.rb +14 -0
  33. data/lib/sass/scss/css_parser.rb +39 -0
  34. data/lib/sass/scss/parser.rb +683 -0
  35. data/lib/sass/scss/rx.rb +112 -0
  36. data/lib/sass/scss/script_lexer.rb +13 -0
  37. data/lib/sass/scss/script_parser.rb +25 -0
  38. data/lib/sass/tree/comment_node.rb +58 -16
  39. data/lib/sass/tree/debug_node.rb +7 -2
  40. data/lib/sass/tree/directive_node.rb +38 -34
  41. data/lib/sass/tree/for_node.rb +6 -0
  42. data/lib/sass/tree/if_node.rb +13 -0
  43. data/lib/sass/tree/import_node.rb +26 -7
  44. data/lib/sass/tree/mixin_def_node.rb +18 -0
  45. data/lib/sass/tree/mixin_node.rb +16 -1
  46. data/lib/sass/tree/node.rb +98 -27
  47. data/lib/sass/tree/prop_node.rb +97 -20
  48. data/lib/sass/tree/root_node.rb +37 -0
  49. data/lib/sass/tree/rule_node.rb +88 -60
  50. data/lib/sass/tree/variable_node.rb +9 -5
  51. data/lib/sass/tree/while_node.rb +4 -0
  52. data/test/haml/results/filters.xhtml +1 -1
  53. data/test/haml/util_test.rb +28 -0
  54. data/test/sass/conversion_test.rb +884 -0
  55. data/test/sass/css2sass_test.rb +46 -21
  56. data/test/sass/engine_test.rb +680 -160
  57. data/test/sass/functions_test.rb +27 -0
  58. data/test/sass/more_results/more_import.css +1 -1
  59. data/test/sass/more_templates/more_import.sass +3 -3
  60. data/test/sass/plugin_test.rb +28 -8
  61. data/test/sass/results/compact.css +1 -1
  62. data/test/sass/results/complex.css +5 -5
  63. data/test/sass/results/compressed.css +1 -1
  64. data/test/sass/results/expanded.css +1 -1
  65. data/test/sass/results/import.css +3 -1
  66. data/test/sass/results/mixins.css +12 -12
  67. data/test/sass/results/nested.css +1 -1
  68. data/test/sass/results/parent_ref.css +4 -4
  69. data/test/sass/results/script.css +3 -3
  70. data/test/sass/results/scss_import.css +15 -0
  71. data/test/sass/results/scss_importee.css +2 -0
  72. data/test/sass/script_conversion_test.rb +153 -0
  73. data/test/sass/script_test.rb +44 -54
  74. data/test/sass/scss/css_test.rb +811 -0
  75. data/test/sass/scss/rx_test.rb +156 -0
  76. data/test/sass/scss/scss_test.rb +871 -0
  77. data/test/sass/scss/test_helper.rb +37 -0
  78. data/test/sass/templates/alt.sass +2 -2
  79. data/test/sass/templates/bork1.sass +1 -1
  80. data/test/sass/templates/import.sass +4 -4
  81. data/test/sass/templates/importee.sass +3 -3
  82. data/test/sass/templates/line_numbers.sass +1 -1
  83. data/test/sass/templates/mixins.sass +2 -2
  84. data/test/sass/templates/nested_mixin_bork.sass +1 -1
  85. data/test/sass/templates/options.sass +1 -1
  86. data/test/sass/templates/parent_ref.sass +2 -2
  87. data/test/sass/templates/script.sass +69 -69
  88. data/test/sass/templates/scss_import.scss +10 -0
  89. data/test/sass/templates/scss_importee.scss +1 -0
  90. data/test/sass/templates/units.sass +10 -10
  91. data/test/test_helper.rb +4 -4
  92. metadata +27 -2
@@ -92,11 +92,9 @@ class SassScriptTest < Test::Unit::TestCase
92
92
  end
93
93
 
94
94
  def test_implicit_strings
95
- silence_warnings do
96
- assert_equal Sass::Script::String.new("foo"), eval("foo")
97
- assert_equal Sass::Script::String.new("foo bar"), eval("foo bar")
98
- assert_equal Sass::Script::String.new("foo/bar"), eval("foo/bar")
99
- end
95
+ assert_equal Sass::Script::String.new("foo"), eval("foo")
96
+ assert_equal Sass::Script::String.new("foo bar"), eval("foo bar")
97
+ assert_equal Sass::Script::String.new("foo/bar"), eval("foo/bar")
100
98
  end
101
99
 
102
100
  def test_interpolation
@@ -130,50 +128,14 @@ foo \#{"\\\#{" + "baz"} bang
130
128
  SASS
131
129
  end
132
130
 
133
- def test_implicit_string_warning
134
- assert_warning(<<WARN) {eval("foo")}
135
- DEPRECATION WARNING:
136
- On line 1, character 1 of 'test_implicit_string_warning_inline.sass'
137
- Implicit strings have been deprecated and will be removed in version 3.0.
138
- 'foo' was not quoted. Please add double quotes (e.g. "foo").
139
- WARN
140
- assert_warning(<<WARN) {eval("1 + foo")}
141
- DEPRECATION WARNING:
142
- On line 1, character 5 of 'test_implicit_string_warning_inline.sass'
143
- Implicit strings have been deprecated and will be removed in version 3.0.
144
- 'foo' was not quoted. Please add double quotes (e.g. "foo").
145
- WARN
146
- assert_warning(<<WARN) {render("@if 1 + foo")}
147
- DEPRECATION WARNING:
148
- On line 1, character 9 of 'test_implicit_string_warning_inline.sass'
149
- Implicit strings have been deprecated and will be removed in version 3.0.
150
- 'foo' was not quoted. Please add double quotes (e.g. "foo").
151
- WARN
152
-
153
- # Regression
154
- assert_warning(<<WARN) {render("@if if")}
155
- DEPRECATION WARNING:
156
- On line 1, character 5 of 'test_implicit_string_warning_inline.sass'
157
- Implicit strings have been deprecated and will be removed in version 3.0.
158
- 'if' was not quoted. Please add double quotes (e.g. "if").
159
- WARN
160
- end
161
-
162
131
  def test_inaccessible_functions
163
- assert_warning <<WARN do
164
- DEPRECATION WARNING:
165
- On line 2, character 6 of 'test_inaccessible_functions_inline.sass'
166
- Implicit strings have been deprecated and will be removed in version 3.0.
167
- 'to_s' was not quoted. Please add double quotes (e.g. "to_s").
168
- WARN
169
- assert_equal "send(to_s)", resolve("send(to_s)", :line => 2)
170
- end
132
+ assert_equal "send(to_s)", resolve("send(to_s)", :line => 2)
171
133
  assert_equal "public_instance_methods()", resolve("public_instance_methods()")
172
134
  end
173
135
 
174
136
  def test_default_functions
175
137
  assert_equal "url(12)", resolve("url(12)")
176
- assert_equal 'blam(foo)', resolve('blam("foo")')
138
+ assert_equal 'blam("foo")', resolve('blam("foo")')
177
139
  end
178
140
 
179
141
  def test_function_results_have_options
@@ -182,7 +144,7 @@ WARN
182
144
  end
183
145
 
184
146
  def test_hyphenated_variables
185
- assert_equal("a-b", resolve("!a-b", {}, env("a-b" => Sass::Script::String.new("a-b"))))
147
+ assert_equal("a-b", resolve("$a-b", {}, env("a-b" => Sass::Script::String.new("a-b"))))
186
148
  end
187
149
 
188
150
  def test_ruby_equality
@@ -220,27 +182,27 @@ WARN
220
182
  assert_equal "2", resolve("1 + 1")
221
183
  assert_equal "0", resolve("1 - 1")
222
184
  assert_equal "8", resolve("2 * 4")
223
- assert_equal "0.5", resolve("2 / 4")
224
- assert_equal "2", resolve("4 / 2")
185
+ assert_equal "0.5", resolve("(2 / 4)")
186
+ assert_equal "2", resolve("(4 / 2)")
225
187
 
226
188
  assert_equal "-1", resolve("-1")
227
189
  end
228
190
 
229
191
  def test_string_ops
230
- assert_equal "foo bar", resolve('"foo" "bar"')
192
+ assert_equal '"foo" "bar"', resolve('"foo" "bar"')
231
193
  assert_equal "true 1", resolve('true 1')
232
- assert_equal "foo, bar", resolve("'foo' , 'bar'")
194
+ assert_equal '"foo", "bar"', resolve("'foo' , 'bar'")
233
195
  assert_equal "true, 1", resolve('true , 1')
234
196
  assert_equal "foobar", resolve('"foo" + "bar"')
235
197
  assert_equal "true1", resolve('true + 1')
236
- assert_equal "foo-bar", resolve("'foo' - 'bar'")
198
+ assert_equal '"foo"-"bar"', resolve("'foo' - 'bar'")
237
199
  assert_equal "true-1", resolve('true - 1')
238
- assert_equal "foo/bar", resolve('"foo" / "bar"')
200
+ assert_equal '"foo"/"bar"', resolve('"foo" / "bar"')
239
201
  assert_equal "true/1", resolve('true / 1')
240
202
 
241
- assert_equal "-bar", resolve("- 'bar'")
203
+ assert_equal '-"bar"', resolve("- 'bar'")
242
204
  assert_equal "-true", resolve('- true')
243
- assert_equal "/bar", resolve('/ "bar"')
205
+ assert_equal '/"bar"', resolve('/ "bar"')
244
206
  assert_equal "/true", resolve('/ true')
245
207
  end
246
208
 
@@ -260,7 +222,7 @@ WARN
260
222
  end
261
223
 
262
224
  def test_equals
263
- assert_equal("true", resolve('"foo" == !foo', {},
225
+ assert_equal("true", resolve('"foo" == $foo', {},
264
226
  env("foo" => Sass::Script::String.new("foo"))))
265
227
  assert_equal "true", resolve("1 == 1.0")
266
228
  assert_equal "true", resolve("false != true")
@@ -295,6 +257,33 @@ WARN
295
257
  assert_equal "Options defined!", resolve("assert_options('bar' + 'baz')")
296
258
  end
297
259
 
260
+ def test_slash_compiles_literally_when_left_alone
261
+ assert_equal "1px/2px", resolve("1px/2px")
262
+ assert_equal "1px/2px/3px/4px", resolve("1px/2px/3px/4px")
263
+
264
+ assert_equal "1px/2px redpx bluepx", resolve("1px/2px redpx bluepx")
265
+ assert_equal "foo 1px/2px/3px bar", resolve("foo 1px/2px/3px bar")
266
+ end
267
+
268
+ def test_slash_divides_with_parens
269
+ assert_equal "0.5", resolve("(1px/2px)")
270
+ assert_equal "0.5", resolve("(1px)/2px")
271
+ assert_equal "0.5", resolve("1px/(2px)")
272
+ end
273
+
274
+ def test_slash_divides_with_other_arithmetic
275
+ assert_equal "0.5px", resolve("1px*1px/2px")
276
+ assert_equal "0.5px", resolve("1px/2px*1px")
277
+ assert_equal "0.5", resolve("0+1px/2px")
278
+ assert_equal "0.5", resolve("1px/2px+0")
279
+ end
280
+
281
+ def test_slash_divides_with_variable
282
+ assert_equal "0.5", resolve("$var/2px", {}, env("var" => eval("1px")))
283
+ assert_equal "0.5", resolve("1px/$var", {}, env("var" => eval("2px")))
284
+ assert_equal "0.5", resolve("$var", {}, env("var" => eval("1px/2px")))
285
+ end
286
+
298
287
  # Regression Tests
299
288
 
300
289
  def test_funcall_has_higher_precedence_than_color_name
@@ -312,7 +301,8 @@ WARN
312
301
 
313
302
  def resolve(str, opts = {}, environment = env)
314
303
  munge_filename opts
315
- eval(str, opts, environment).to_s
304
+ val = eval(str, opts, environment)
305
+ val.is_a?(Sass::Script::String) ? val.value : val.to_s
316
306
  end
317
307
 
318
308
  def eval(str, opts = {}, environment = env)
@@ -0,0 +1,811 @@
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 < Test::Unit::TestCase
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
+ def test_unicode
53
+ assert_parses <<SCSS
54
+ foo {
55
+ bar: föö bâr; }
56
+ SCSS
57
+ end
58
+
59
+ def test_invisible_comments
60
+ assert_equal <<CSS, render(<<SCSS)
61
+ foo {
62
+ a: d; }
63
+ CSS
64
+ foo {a: /* b; c: */ d}
65
+ SCSS
66
+ assert_equal <<CSS, render(<<SCSS)
67
+ foo {
68
+ a: d; }
69
+ CSS
70
+ foo {a /*: b; c */: d}
71
+ SCSS
72
+ end
73
+
74
+ def test_crazy_comments
75
+ # http://www.w3.org/Style/CSS/Test/CSS2.1/current/xhtml1/t040109-c17-comments-00-b.xht
76
+ assert_equal <<CSS, render(<<SCSS)
77
+ /* This is a CSS comment. */
78
+ .one {
79
+ color: green; }
80
+
81
+ /* Another comment */
82
+ /* The following should not be used:
83
+ .two {color: red;} */
84
+ .three {
85
+ color: green;
86
+ /* color: red; */ }
87
+
88
+ /**
89
+ .four {color: red;} */
90
+ .five {
91
+ color: green; }
92
+
93
+ /**/
94
+ .six {
95
+ color: green; }
96
+
97
+ /*********/
98
+ .seven {
99
+ color: green; }
100
+
101
+ /* a comment **/
102
+ .eight {
103
+ color: green; }
104
+ CSS
105
+ /* This is a CSS comment. */
106
+ .one {color: green;} /* Another comment */
107
+ /* The following should not be used:
108
+ .two {color: red;} */
109
+ .three {color: green; /* color: red; */}
110
+ /**
111
+ .four {color: red;} */
112
+ .five {color: green;}
113
+ /**/
114
+ .six {color: green;}
115
+ /*********/
116
+ .seven {color: green;}
117
+ /* a comment **/
118
+ .eight {color: green;}
119
+ SCSS
120
+ end
121
+
122
+ def test_rule_comments
123
+ assert_parses <<SCSS
124
+ /* Foo */
125
+ .foo {
126
+ a: b; }
127
+ SCSS
128
+ assert_equal <<CSS, render(<<SCSS)
129
+ /* Foo
130
+ * Bar */
131
+ .foo {
132
+ a: b; }
133
+ CSS
134
+ /* Foo
135
+ * Bar */.foo {
136
+ a: b; }
137
+ SCSS
138
+ end
139
+
140
+ def test_property_comments
141
+ assert_parses <<SCSS
142
+ .foo {
143
+ /* Foo */
144
+ a: b; }
145
+ SCSS
146
+ assert_equal <<CSS, render(<<SCSS)
147
+ .foo {
148
+ /* Foo
149
+ * Bar */
150
+ a: b; }
151
+ CSS
152
+ .foo {
153
+ /* Foo
154
+ * Bar */a: b; }
155
+ SCSS
156
+ end
157
+
158
+ def test_selector_comments
159
+ assert_equal <<CSS, render(<<SCSS)
160
+ .foo #bar:baz( bip) {
161
+ a: b; }
162
+ CSS
163
+ .foo /* .a #foo */ #bar:baz(/* bang )*/ bip) {
164
+ a: b; }
165
+ SCSS
166
+ end
167
+
168
+ def test_lonely_comments
169
+ assert_parses <<SCSS
170
+ /* Foo
171
+ * Bar */
172
+ SCSS
173
+ assert_parses <<SCSS
174
+ .foo {
175
+ /* Foo
176
+ * Bar */ }
177
+ SCSS
178
+ end
179
+
180
+ def test_multiple_comments
181
+ assert_parses <<SCSS
182
+ /* Foo
183
+ * Bar */
184
+ /* Baz
185
+ * Bang */
186
+ SCSS
187
+ assert_parses <<SCSS
188
+ .foo {
189
+ /* Foo
190
+ * Bar */
191
+ /* Baz
192
+ * Bang */ }
193
+ SCSS
194
+ assert_equal <<CSS, render(<<SCSS)
195
+ .foo {
196
+ /* Foo Bar */
197
+ /* Baz Bang */ }
198
+ CSS
199
+ .foo {
200
+ /* Foo Bar *//* Baz Bang */ }
201
+ SCSS
202
+ end
203
+
204
+ def test_bizarrely_formatted_comments
205
+ assert_parses <<SCSS
206
+ .foo {
207
+ /* Foo
208
+ Bar
209
+ Baz */
210
+ a: b; }
211
+ SCSS
212
+ assert_parses <<SCSS
213
+ .foo {
214
+ /* Foo
215
+ Bar
216
+ Baz */
217
+ a: b; }
218
+ SCSS
219
+ assert_equal <<CSS, render(<<SCSS)
220
+ .foo {
221
+ /* Foo
222
+ Bar */
223
+ a: b; }
224
+ CSS
225
+ .foo {/* Foo
226
+ Bar */
227
+ a: b; }
228
+ SCSS
229
+ assert_equal <<CSS, render(<<SCSS)
230
+ .foo {
231
+ /* Foo
232
+ Bar
233
+ Baz */
234
+ a: b; }
235
+ CSS
236
+ .foo {/* Foo
237
+ Bar
238
+ Baz */
239
+ a: b; }
240
+ SCSS
241
+ end
242
+
243
+ ## Declarations
244
+
245
+ def test_vendor_properties
246
+ assert_parses <<SCSS
247
+ foo {
248
+ -moz-foo-bar: blat;
249
+ -o-flat-blang: wibble; }
250
+ SCSS
251
+ end
252
+
253
+ def test_empty_declarations
254
+ assert_equal <<CSS, render(<<SCSS)
255
+ foo {
256
+ bar: baz; }
257
+ CSS
258
+ foo {;;;;
259
+ bar: baz;;;;
260
+ ;;}
261
+ SCSS
262
+ end
263
+
264
+ def test_basic_property_types
265
+ assert_parses <<SCSS
266
+ foo {
267
+ a: 2;
268
+ b: 2.3em;
269
+ c: 50%;
270
+ d: "fraz bran";
271
+ e: flanny-blanny-blan;
272
+ f: url(http://sass-lang.com);
273
+ g: U+ffa?;
274
+ h: #aabbcc; }
275
+ SCSS
276
+ end
277
+
278
+ def test_functions
279
+ assert_parses <<SCSS
280
+ foo {
281
+ a: foo-bar(12);
282
+ b: -foo-bar-baz(13, 14 15); }
283
+ SCSS
284
+ end
285
+
286
+ def test_unary_minus
287
+ assert_parses <<SCSS
288
+ foo {
289
+ a: -2;
290
+ b: -2.3em;
291
+ c: -50%;
292
+ d: -foo(bar baz); }
293
+ SCSS
294
+ end
295
+
296
+ def test_operators
297
+ assert_parses <<SCSS
298
+ foo {
299
+ a: foo bar baz;
300
+ b: foo, #aabbcc, -12;
301
+ c: 1px/2px/-3px;
302
+ d: foo bar, baz/bang; }
303
+ SCSS
304
+ end
305
+
306
+ def test_important
307
+ assert_parses <<SCSS
308
+ foo {
309
+ a: foo !important;
310
+ b: foo bar !important;
311
+ b: foo, bar !important; }
312
+ SCSS
313
+ end
314
+
315
+ def test_initial_hyphen
316
+ assert_parses <<SCSS
317
+ foo {
318
+ a: -moz-bar-baz;
319
+ b: foo -o-bar-baz; }
320
+ SCSS
321
+ end
322
+
323
+ def test_ms_filter_syntax
324
+ assert_equal <<CSS, render(<<SCSS)
325
+ foo {
326
+ filter: progid:DXImageTransform.Microsoft.gradient(GradientType=1, startColorstr=#c0ff3300, endColorstr=#ff000000);
327
+ filter: progid:DXImageTransform.Microsoft.gradient(GradientType=1, startColorstr=#c0ff3300, endColorstr=#ff000000); }
328
+ CSS
329
+ foo {
330
+ filter: progid:DXImageTransform.Microsoft.gradient(GradientType=1, startColorstr=#c0ff3300, endColorstr=#ff000000);
331
+ filter:progid:DXImageTransform.Microsoft.gradient(GradientType=1, startColorstr=#c0ff3300, endColorstr=#ff000000); }
332
+ SCSS
333
+ end
334
+
335
+ def test_declaration_hacks
336
+ assert_parses <<SCSS
337
+ foo {
338
+ _name: val;
339
+ *name: val;
340
+ :name: val;
341
+ .name: val;
342
+ name: val; }
343
+ SCSS
344
+ end
345
+
346
+ def test_zero_arg_functions
347
+ assert_parses <<SCSS
348
+ foo {
349
+ a: foo();
350
+ b: bar baz-bang() bip; }
351
+ SCSS
352
+ end
353
+
354
+ def test_expression_function
355
+ assert_parses <<SCSS
356
+ foo {
357
+ a: 12px expression(1 + (3 / Foo.bar("baz" + "bang") + function() {return 12;}) % 12); }
358
+ SCSS
359
+ end
360
+
361
+ def test_calc_function
362
+ assert_parses <<SCSS
363
+ foo {
364
+ a: 12px calc(100%/3 - 2*1em - 2*1px); }
365
+ SCSS
366
+ end
367
+
368
+ def test_unary_ops
369
+ assert_equal <<CSS, render(<<SCSS)
370
+ foo {
371
+ a: -0.5em;
372
+ b: 0.5em;
373
+ c: -foo(12px);
374
+ d: +foo(12px); }
375
+ CSS
376
+ foo {
377
+ a: -0.5em;
378
+ b: +0.5em;
379
+ c: -foo(12px);
380
+ d: +foo(12px); }
381
+ SCSS
382
+ end
383
+
384
+ ## Directives
385
+
386
+ def test_charset_directive
387
+ assert_parses '@charset "utf-8";'
388
+ end
389
+
390
+ def test_namespace_directive
391
+ assert_parses '@namespace "http://www.w3.org/Profiles/xhtml1-strict";'
392
+ assert_parses '@namespace url(http://www.w3.org/Profiles/xhtml1-strict);'
393
+ assert_parses '@namespace html url("http://www.w3.org/Profiles/xhtml1-strict");'
394
+ end
395
+
396
+ def test_media_directive
397
+ assert_parses <<SCSS
398
+ @media all {
399
+ rule1 {
400
+ prop: val; }
401
+
402
+ rule2 {
403
+ prop: val; } }
404
+ SCSS
405
+ assert_parses <<SCSS
406
+ @media screen, print {
407
+ rule1 {
408
+ prop: val; }
409
+
410
+ rule2 {
411
+ prop: val; } }
412
+ SCSS
413
+ end
414
+
415
+ def test_media_directive_with_keywords
416
+ assert_parses <<SCSS
417
+ @media screen and (-webkit-min-device-pixel-ratio:0) {
418
+ a: b; }
419
+ SCSS
420
+ assert_parses <<SCSS
421
+ @media only screen, print and (foo: 0px) and (bar: flam(12px solid)) {
422
+ a: b; }
423
+ SCSS
424
+ end
425
+
426
+ def test_import_directive
427
+ assert_parses '@import "foo.css";'
428
+ assert_parses "@import 'foo.css';"
429
+ assert_parses '@import url("foo.css");'
430
+ assert_parses "@import url('foo.css');"
431
+ assert_parses '@import url(foo.css);'
432
+ end
433
+
434
+ def test_import_directive_with_media
435
+ assert_parses '@import "foo.css" screen;'
436
+ assert_parses '@import "foo.css" screen, print;'
437
+ assert_parses '@import "foo.css" screen, print and (foo: 0);'
438
+ assert_parses '@import "foo.css" screen, only print, screen and (foo: 0);'
439
+ end
440
+
441
+ def test_page_directive
442
+ assert_parses <<SCSS
443
+ @page {
444
+ prop1: val;
445
+ prop2: val; }
446
+ SCSS
447
+ assert_parses <<SCSS
448
+ @page flap {
449
+ prop1: val;
450
+ prop2: val; }
451
+ SCSS
452
+ assert_parses <<SCSS
453
+ @page :first {
454
+ prop1: val;
455
+ prop2: val; }
456
+ SCSS
457
+ assert_parses <<SCSS
458
+ @page flap:first {
459
+ prop1: val;
460
+ prop2: val; }
461
+ SCSS
462
+ end
463
+
464
+ def test_blockless_directive_without_semicolon
465
+ assert_equal "@charset \"utf-8\";\n", render('@charset "utf-8"')
466
+ end
467
+
468
+ def test_directive_with_lots_of_whitespace
469
+ assert_equal "@charset \"utf-16\";\n", render('@charset "utf-16" ;')
470
+ end
471
+
472
+ def test_empty_blockless_directive
473
+ assert_parses "@foo;"
474
+ end
475
+
476
+ def test_multiple_blockless_directives
477
+ assert_parses <<SCSS
478
+ @foo bar;
479
+ @bar baz;
480
+ SCSS
481
+ end
482
+
483
+ def test_empty_block_directive
484
+ assert_parses "@foo {}"
485
+ assert_equal "@foo {}\n", render(<<SCSS)
486
+ @foo {
487
+ }
488
+ SCSS
489
+ end
490
+
491
+ def test_multiple_block_directives
492
+ assert_parses <<SCSS
493
+ @foo bar {
494
+ a: b; }
495
+
496
+ @bar baz {
497
+ c: d; }
498
+ SCSS
499
+ end
500
+
501
+ def test_block_directive_with_rule_and_property
502
+ assert_parses <<SCSS
503
+ @foo {
504
+ rule {
505
+ a: b; }
506
+
507
+ a: b; }
508
+ SCSS
509
+ end
510
+
511
+ def test_block_directive_with_semicolon
512
+ assert_equal <<CSS, render(<<SCSS)
513
+ @foo {
514
+ a: b; }
515
+
516
+ @bar {
517
+ a: b; }
518
+ CSS
519
+ @foo {a:b};
520
+ @bar {a:b};
521
+ SCSS
522
+ end
523
+
524
+ ## Selectors
525
+
526
+ # Taken from http://www.w3.org/TR/css3-selectors/#selectors
527
+ def test_summarized_selectors
528
+ assert_selector_parses('*')
529
+ assert_selector_parses('E')
530
+ assert_selector_parses('E[foo]')
531
+ assert_selector_parses('E[foo="bar"]')
532
+ assert_selector_parses('E[foo~="bar"]')
533
+ assert_selector_parses('E[foo^="bar"]')
534
+ assert_selector_parses('E[foo$="bar"]')
535
+ assert_selector_parses('E[foo*="bar"]')
536
+ assert_selector_parses('E[foo|="en"]')
537
+ assert_selector_parses('E:root')
538
+ assert_selector_parses('E:nth-child(n)')
539
+ assert_selector_parses('E:nth-last-child(n)')
540
+ assert_selector_parses('E:nth-of-type(n)')
541
+ assert_selector_parses('E:nth-last-of-type(n)')
542
+ assert_selector_parses('E:first-child')
543
+ assert_selector_parses('E:last-child')
544
+ assert_selector_parses('E:first-of-type')
545
+ assert_selector_parses('E:last-of-type')
546
+ assert_selector_parses('E:only-child')
547
+ assert_selector_parses('E:only-of-type')
548
+ assert_selector_parses('E:empty')
549
+ assert_selector_parses('E:link')
550
+ assert_selector_parses('E:visited')
551
+ assert_selector_parses('E:active')
552
+ assert_selector_parses('E:hover')
553
+ assert_selector_parses('E:focus')
554
+ assert_selector_parses('E:target')
555
+ assert_selector_parses('E:lang(fr)')
556
+ assert_selector_parses('E:enabled')
557
+ assert_selector_parses('E:disabled')
558
+ assert_selector_parses('E:checked')
559
+ assert_selector_parses('E::first-line')
560
+ assert_selector_parses('E::first-letter')
561
+ assert_selector_parses('E::before')
562
+ assert_selector_parses('E::after')
563
+ assert_selector_parses('E.warning')
564
+ assert_selector_parses('E#myid')
565
+ assert_selector_parses('E:not(s)')
566
+ assert_selector_parses('E F')
567
+ assert_selector_parses('E > F')
568
+ assert_selector_parses('E + F')
569
+ assert_selector_parses('E ~ F')
570
+ end
571
+
572
+ # Taken from http://www.w3.org/TR/css3-selectors/#selectors,
573
+ # but without the element names
574
+ def test_lonely_selectors
575
+ assert_selector_parses('[foo]')
576
+ assert_selector_parses('[foo="bar"]')
577
+ assert_selector_parses('[foo~="bar"]')
578
+ assert_selector_parses('[foo^="bar"]')
579
+ assert_selector_parses('[foo$="bar"]')
580
+ assert_selector_parses('[foo*="bar"]')
581
+ assert_selector_parses('[foo|="en"]')
582
+ assert_selector_parses(':root')
583
+ assert_selector_parses(':nth-child(n)')
584
+ assert_selector_parses(':nth-last-child(n)')
585
+ assert_selector_parses(':nth-of-type(n)')
586
+ assert_selector_parses(':nth-last-of-type(n)')
587
+ assert_selector_parses(':first-child')
588
+ assert_selector_parses(':last-child')
589
+ assert_selector_parses(':first-of-type')
590
+ assert_selector_parses(':last-of-type')
591
+ assert_selector_parses(':only-child')
592
+ assert_selector_parses(':only-of-type')
593
+ assert_selector_parses(':empty')
594
+ assert_selector_parses(':link')
595
+ assert_selector_parses(':visited')
596
+ assert_selector_parses(':active')
597
+ assert_selector_parses(':hover')
598
+ assert_selector_parses(':focus')
599
+ assert_selector_parses(':target')
600
+ assert_selector_parses(':lang(fr)')
601
+ assert_selector_parses(':enabled')
602
+ assert_selector_parses(':disabled')
603
+ assert_selector_parses(':checked')
604
+ assert_selector_parses('::first-line')
605
+ assert_selector_parses('::first-letter')
606
+ assert_selector_parses('::before')
607
+ assert_selector_parses('::after')
608
+ assert_selector_parses('.warning')
609
+ assert_selector_parses('#myid')
610
+ assert_selector_parses(':not(s)')
611
+ end
612
+
613
+ def test_attribute_selectors_with_identifiers
614
+ assert_selector_parses('[foo~=bar]')
615
+ assert_selector_parses('[foo^=bar]')
616
+ assert_selector_parses('[foo$=bar]')
617
+ assert_selector_parses('[foo*=bar]')
618
+ assert_selector_parses('[foo|=en]')
619
+ end
620
+
621
+ def test_nth_selectors
622
+ assert_selector_parses(':nth-child(-n)')
623
+ assert_selector_parses(':nth-child(+n)')
624
+
625
+ assert_selector_parses(':nth-child(even)')
626
+ assert_selector_parses(':nth-child(odd)')
627
+
628
+ assert_selector_parses(':nth-child(50)')
629
+ assert_selector_parses(':nth-child(-50)')
630
+ assert_selector_parses(':nth-child(+50)')
631
+
632
+ assert_selector_parses(':nth-child(2n+3)')
633
+ assert_selector_parses(':nth-child(2n-3)')
634
+ assert_selector_parses(':nth-child(+2n-3)')
635
+ assert_selector_parses(':nth-child(-2n+3)')
636
+ assert_selector_parses(':nth-child(-2n+ 3)')
637
+ assert_selector_parses(':nth-child( 2n + 3 )')
638
+ end
639
+
640
+ def test_negation_selectors
641
+ assert_selector_parses(':not(foo|bar)')
642
+ assert_selector_parses(':not(*|bar)')
643
+
644
+ assert_selector_parses(':not(foo|*)')
645
+ assert_selector_parses(':not(*|*)')
646
+
647
+ assert_selector_parses(':not(#blah)')
648
+ assert_selector_parses(':not(.blah)')
649
+
650
+ assert_selector_parses(':not([foo])')
651
+ assert_selector_parses(':not([foo^="bar"])')
652
+ assert_selector_parses(':not([baz|foo~="bar"])')
653
+
654
+ assert_selector_parses(':not(:hover)')
655
+ assert_selector_parses(':not(:nth-child(2n + 3))')
656
+ end
657
+
658
+ def test_namespaced_selectors
659
+ assert_selector_parses('foo|E')
660
+ assert_selector_parses('*|E')
661
+ assert_selector_parses('foo|*')
662
+ assert_selector_parses('*|*')
663
+ end
664
+
665
+ def test_namespaced_attribute_selectors
666
+ assert_selector_parses('[foo|bar=baz]')
667
+ assert_selector_parses('[*|bar=baz]')
668
+ assert_selector_parses('[foo|bar|=baz]')
669
+ end
670
+
671
+ def test_comma_selectors
672
+ assert_selector_parses('E, F')
673
+ assert_selector_parses('E F, G H')
674
+ assert_selector_parses('E > F, G > H')
675
+ end
676
+
677
+ def test_selectors_with_newlines
678
+ assert_selector_parses("E,\nF")
679
+ assert_selector_parses("E\nF")
680
+ assert_selector_parses("E, F\nG, H")
681
+ end
682
+
683
+ def test_expression_fallback_selectors
684
+ assert_selector_parses('0%')
685
+ assert_selector_parses('60%')
686
+ assert_selector_parses('100%')
687
+ assert_selector_parses('12px')
688
+ assert_selector_parses('"foo"')
689
+ end
690
+
691
+ def test_functional_pseudo_selectors
692
+ assert_selector_parses(':foo("bar")')
693
+ assert_selector_parses(':foo(bar)')
694
+ assert_selector_parses(':foo(12px)')
695
+ assert_selector_parses(':foo(+)')
696
+ assert_selector_parses(':foo(-)')
697
+ assert_selector_parses(':foo(+"bar")')
698
+ assert_selector_parses(':foo(-++--baz-"bar"12px)')
699
+ end
700
+
701
+ def test_selector_hacks
702
+ assert_selector_parses('> E')
703
+ assert_selector_parses('+ E')
704
+ assert_selector_parses('~ E')
705
+ assert_selector_parses('>> E')
706
+
707
+ assert_selector_parses('E*')
708
+ assert_selector_parses('E*.foo')
709
+ assert_selector_parses('E*:hover')
710
+ end
711
+
712
+ ## Errors
713
+
714
+ def test_invalid_directives
715
+ assert_not_parses("identifier", '@<err> import "foo";')
716
+ assert_not_parses("identifier", '@<err>12 "foo";')
717
+ end
718
+
719
+ def test_invalid_classes
720
+ assert_not_parses("identifier", 'p.<err> foo {a: b}')
721
+ assert_not_parses("identifier", 'p.<err>1foo {a: b}')
722
+ end
723
+
724
+ def test_invalid_ids
725
+ assert_not_parses('"{"', 'p<err># foo {a: b}')
726
+ end
727
+
728
+ def test_no_properties_at_toplevel
729
+ assert_not_parses('pseudoclass or pseudoelement', 'a:<err> b;')
730
+ end
731
+
732
+ def test_no_scss_directives
733
+ assert_parses('@import "foo.sass";')
734
+ assert_parses <<SCSS
735
+ @mixin foo {
736
+ a: b; }
737
+ SCSS
738
+ end
739
+
740
+ def test_no_variables
741
+ assert_not_parses("selector or at-rule", "<err>$var = 12;")
742
+ assert_not_parses('"}"', "foo { <err>!var = 12; }")
743
+ end
744
+
745
+ def test_no_parent_selectors
746
+ assert_not_parses('"{"', "foo <err>&.bar {a: b}")
747
+ end
748
+
749
+ def test_no_selector_interpolation
750
+ assert_not_parses('"{"', 'foo <err>#{"bar"}.baz {a: b}')
751
+ end
752
+
753
+ def test_no_prop_name_interpolation
754
+ assert_not_parses('":"', 'foo {a<err>#{"bar"}baz: b}')
755
+ end
756
+
757
+ def test_no_prop_val_interpolation
758
+ assert_not_parses('"}"', 'foo {a: b <err>#{"bar"} c}')
759
+ end
760
+
761
+ def test_no_string_interpolation
762
+ assert_parses <<SCSS
763
+ foo {
764
+ a: "bang \#{1 + " bar "} bip"; }
765
+ SCSS
766
+ end
767
+
768
+ def test_no_sass_script_values
769
+ assert_not_parses('"}"', 'foo {a: b <err>* c}')
770
+ end
771
+
772
+ def test_no_nested_rules
773
+ assert_not_parses('":"', 'foo {bar <err>{a: b}}')
774
+ assert_not_parses('"}"', 'foo {<err>#bar {a: b}}')
775
+ end
776
+
777
+ def test_no_nested_properties
778
+ assert_not_parses('expression (e.g. 1px, bold)', 'foo {bar: <err>{a: b}}')
779
+ assert_not_parses('expression (e.g. 1px, bold)', 'foo {bar: bang <err>{a: b}}')
780
+ end
781
+
782
+ def test_no_nested_directives
783
+ assert_not_parses('"}"', 'foo {<err>@bar {a: b}}')
784
+ end
785
+
786
+ def test_error_with_windows_newlines
787
+ render <<SCSS
788
+ foo {bar}\r
789
+ baz {a: b}
790
+ SCSS
791
+ assert(false, "Expected syntax error")
792
+ rescue Sass::SyntaxError => e
793
+ assert_equal 'Invalid CSS after "foo {bar": expected ":", was "}"', e.message
794
+ assert_equal 1, e.sass_line
795
+ end
796
+
797
+ private
798
+
799
+ def assert_selector_parses(selector)
800
+ assert_parses <<SCSS
801
+ #{selector} {
802
+ a: b; }
803
+ SCSS
804
+ end
805
+
806
+ def render(scss, options = {})
807
+ tree = Sass::SCSS::CssParser.new(scss).parse
808
+ tree.options = Sass::Engine::DEFAULT_OPTIONS.merge(options)
809
+ tree.render
810
+ end
811
+ end