haml-edge 2.3.179 → 2.3.180

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 (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