haml 2.0.10 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of haml might be problematic. Click here for more details.

Files changed (107) hide show
  1. data/.yardopts +5 -0
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +347 -0
  4. data/Rakefile +124 -19
  5. data/VERSION +1 -1
  6. data/VERSION_NAME +1 -0
  7. data/extra/haml-mode.el +397 -78
  8. data/extra/sass-mode.el +148 -36
  9. data/extra/update_watch.rb +13 -0
  10. data/lib/haml.rb +15 -993
  11. data/lib/haml/buffer.rb +131 -84
  12. data/lib/haml/engine.rb +129 -97
  13. data/lib/haml/error.rb +7 -7
  14. data/lib/haml/exec.rb +127 -42
  15. data/lib/haml/filters.rb +107 -42
  16. data/lib/haml/helpers.rb +210 -156
  17. data/lib/haml/helpers/action_view_extensions.rb +34 -39
  18. data/lib/haml/helpers/action_view_mods.rb +132 -139
  19. data/lib/haml/html.rb +77 -65
  20. data/lib/haml/precompiler.rb +404 -213
  21. data/lib/haml/shared.rb +78 -0
  22. data/lib/haml/template.rb +14 -14
  23. data/lib/haml/template/patch.rb +2 -2
  24. data/lib/haml/template/plugin.rb +2 -3
  25. data/lib/haml/util.rb +211 -6
  26. data/lib/haml/version.rb +30 -13
  27. data/lib/sass.rb +7 -856
  28. data/lib/sass/css.rb +169 -161
  29. data/lib/sass/engine.rb +344 -328
  30. data/lib/sass/environment.rb +79 -0
  31. data/lib/sass/error.rb +33 -11
  32. data/lib/sass/files.rb +139 -0
  33. data/lib/sass/plugin.rb +160 -117
  34. data/lib/sass/plugin/merb.rb +7 -6
  35. data/lib/sass/plugin/rails.rb +5 -6
  36. data/lib/sass/repl.rb +58 -0
  37. data/lib/sass/script.rb +59 -0
  38. data/lib/sass/script/bool.rb +17 -0
  39. data/lib/sass/script/color.rb +183 -0
  40. data/lib/sass/script/funcall.rb +50 -0
  41. data/lib/sass/script/functions.rb +198 -0
  42. data/lib/sass/script/lexer.rb +178 -0
  43. data/lib/sass/script/literal.rb +177 -0
  44. data/lib/sass/script/node.rb +14 -0
  45. data/lib/sass/script/number.rb +381 -0
  46. data/lib/sass/script/operation.rb +45 -0
  47. data/lib/sass/script/parser.rb +172 -0
  48. data/lib/sass/script/string.rb +12 -0
  49. data/lib/sass/script/unary_operation.rb +34 -0
  50. data/lib/sass/script/variable.rb +31 -0
  51. data/lib/sass/tree/comment_node.rb +73 -10
  52. data/lib/sass/tree/debug_node.rb +30 -0
  53. data/lib/sass/tree/directive_node.rb +42 -17
  54. data/lib/sass/tree/file_node.rb +41 -0
  55. data/lib/sass/tree/for_node.rb +48 -0
  56. data/lib/sass/tree/if_node.rb +54 -0
  57. data/lib/sass/tree/mixin_def_node.rb +29 -0
  58. data/lib/sass/tree/mixin_node.rb +48 -0
  59. data/lib/sass/tree/node.rb +214 -11
  60. data/lib/sass/tree/prop_node.rb +109 -0
  61. data/lib/sass/tree/rule_node.rb +178 -51
  62. data/lib/sass/tree/variable_node.rb +34 -0
  63. data/lib/sass/tree/while_node.rb +31 -0
  64. data/test/haml/engine_test.rb +331 -36
  65. data/test/haml/helper_test.rb +12 -1
  66. data/test/haml/results/content_for_layout.xhtml +0 -3
  67. data/test/haml/results/filters.xhtml +2 -0
  68. data/test/haml/results/list.xhtml +1 -1
  69. data/test/haml/template_test.rb +7 -2
  70. data/test/haml/templates/content_for_layout.haml +0 -2
  71. data/test/haml/templates/list.haml +1 -1
  72. data/test/haml/util_test.rb +92 -0
  73. data/test/sass/css2sass_test.rb +69 -24
  74. data/test/sass/engine_test.rb +586 -64
  75. data/test/sass/functions_test.rb +125 -0
  76. data/test/sass/more_results/more1.css +9 -0
  77. data/test/sass/more_results/more1_with_line_comments.css +26 -0
  78. data/test/sass/more_results/more_import.css +29 -0
  79. data/test/sass/more_templates/_more_partial.sass +2 -0
  80. data/test/sass/more_templates/more1.sass +23 -0
  81. data/test/sass/more_templates/more_import.sass +11 -0
  82. data/test/sass/plugin_test.rb +81 -28
  83. data/test/sass/results/line_numbers.css +49 -0
  84. data/test/sass/results/{constants.css → script.css} +4 -4
  85. data/test/sass/results/subdir/subdir.css +2 -0
  86. data/test/sass/results/units.css +11 -0
  87. data/test/sass/script_test.rb +258 -0
  88. data/test/sass/templates/import.sass +1 -1
  89. data/test/sass/templates/importee.sass +7 -2
  90. data/test/sass/templates/line_numbers.sass +13 -0
  91. data/test/sass/templates/{constants.sass → script.sass} +11 -10
  92. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
  93. data/test/sass/templates/subdir/subdir.sass +2 -2
  94. data/test/sass/templates/units.sass +11 -0
  95. data/test/test_helper.rb +14 -0
  96. metadata +77 -19
  97. data/FAQ +0 -138
  98. data/README.rdoc +0 -319
  99. data/lib/sass/constant.rb +0 -216
  100. data/lib/sass/constant/color.rb +0 -101
  101. data/lib/sass/constant/literal.rb +0 -54
  102. data/lib/sass/constant/nil.rb +0 -9
  103. data/lib/sass/constant/number.rb +0 -87
  104. data/lib/sass/constant/operation.rb +0 -30
  105. data/lib/sass/constant/string.rb +0 -22
  106. data/lib/sass/tree/attr_node.rb +0 -57
  107. data/lib/sass/tree/value_node.rb +0 -20
@@ -14,6 +14,12 @@ class HelperTest < Test::Unit::TestCase
14
14
  def setup
15
15
  @base = ActionView::Base.new
16
16
  @base.controller = ActionController::Base.new
17
+
18
+ if defined?(ActionController::Response)
19
+ # This is needed for >=3.0.0
20
+ @base.controller.response = ActionController::Response.new
21
+ end
22
+
17
23
  @base.instance_variable_set('@post', Post.new("Foo bar\nbaz"))
18
24
  end
19
25
 
@@ -61,7 +67,7 @@ class HelperTest < Test::Unit::TestCase
61
67
 
62
68
  begin
63
69
  ActionView::Base.new.render(:inline => "<%= flatten('Foo\\nBar') %>")
64
- rescue NoMethodError
70
+ rescue NoMethodError, ActionView::TemplateError
65
71
  proper_behavior = true
66
72
  end
67
73
  assert(proper_behavior)
@@ -182,6 +188,11 @@ HAML
182
188
  render("= find_and_preserve do\n %pre\n Foo\n Bar\n Foo\n Bar"))
183
189
  end
184
190
 
191
+ def test_find_and_preserve_with_block_and_tags
192
+ assert_equal("<pre>Foo\nBar</pre>\nFoo\nBar\n",
193
+ render("= find_and_preserve([]) do\n %pre\n Foo\n Bar\n Foo\n Bar"))
194
+ end
195
+
185
196
  def test_preserve_with_block
186
197
  assert_equal("<pre>Foo&#x000A;Bar</pre>&#x000A;Foo&#x000A;Bar\n",
187
198
  render("= preserve do\n %pre\n Foo\n Bar\n Foo\n Bar"))
@@ -2,9 +2,6 @@
2
2
  <html>
3
3
  <head></head>
4
4
  <body>
5
- <div id='content'>
6
- Lorem ipsum dolor sit amet
7
- </div>
8
5
  <div id='yieldy'>
9
6
  Lorem ipsum dolor sit amet
10
7
  </div>
@@ -1,6 +1,8 @@
1
1
  <style>
2
+ /* line 1 */
2
3
  p { border-style: dotted; border-width: 22px; border-color: #ff00ff; }
3
4
 
5
+ /* line 6 */
4
6
  h1 { font-weight: normal; }
5
7
  </style>
6
8
  TESTING HAHAHAHA!
@@ -1,4 +1,4 @@
1
- ! Not a Doctype !
1
+ !Not a Doctype!
2
2
  <ul>
3
3
  <li>a</li>
4
4
  <li>b</li>
@@ -35,6 +35,10 @@ class DummyController
35
35
  def self.controller_path
36
36
  ''
37
37
  end
38
+
39
+ def controller_path
40
+ ''
41
+ end
38
42
  end
39
43
 
40
44
  class TemplateTest < Test::Unit::TestCase
@@ -91,7 +95,8 @@ class TemplateTest < Test::Unit::TestCase
91
95
  end
92
96
 
93
97
  def assert_renders_correctly(name, &render_method)
94
- if ActionPack::VERSION::MAJOR < 2 || ActionPack::VERSION::MINOR < 2
98
+ if ActionPack::VERSION::MAJOR < 2 ||
99
+ (ActionPack::VERSION::MAJOR == 2 && ActionPack::VERSION::MINOR < 2)
95
100
  render_method ||= proc { |name| @base.render(name) }
96
101
  else
97
102
  render_method ||= proc { |name| @base.render(:file => name) }
@@ -135,7 +140,7 @@ class TemplateTest < Test::Unit::TestCase
135
140
  end
136
141
 
137
142
  def test_action_view_templates_render_correctly
138
- @base.instance_variable_set("@content_for_layout", 'Lorem ipsum dolor sit amet')
143
+ @base.content_for(:layout) {'Lorem ipsum dolor sit amet'}
139
144
  assert_renders_correctly 'content_for_layout'
140
145
  end
141
146
 
@@ -2,8 +2,6 @@
2
2
  %html
3
3
  %head
4
4
  %body
5
- #content
6
- = @content_for_layout
7
5
  #yieldy
8
6
  = yield :layout
9
7
  #nosym
@@ -1,4 +1,4 @@
1
- ! Not a Doctype !
1
+ !Not a Doctype!
2
2
  %ul
3
3
  %li a
4
4
  %li b
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../test_helper'
3
+ require 'pathname'
4
+
5
+ class UtilTest < Test::Unit::TestCase
6
+ include Haml::Util
7
+
8
+ def test_scope
9
+ assert(File.exist?(scope("Rakefile")))
10
+ end
11
+
12
+ def test_to_hash
13
+ assert_equal({
14
+ :foo => 1,
15
+ :bar => 2,
16
+ :baz => 3
17
+ }, to_hash([[:foo, 1], [:bar, 2], [:baz, 3]]))
18
+ end
19
+
20
+ def test_map_keys
21
+ assert_equal({
22
+ "foo" => 1,
23
+ "bar" => 2,
24
+ "baz" => 3
25
+ }, map_keys({:foo => 1, :bar => 2, :baz => 3}) {|k| k.to_s})
26
+ end
27
+
28
+ def test_map_vals
29
+ assert_equal({
30
+ :foo => "1",
31
+ :bar => "2",
32
+ :baz => "3"
33
+ }, map_vals({:foo => 1, :bar => 2, :baz => 3}) {|k| k.to_s})
34
+ end
35
+
36
+ def test_map_hash
37
+ assert_equal({
38
+ "foo" => "1",
39
+ "bar" => "2",
40
+ "baz" => "3"
41
+ }, map_hash({:foo => 1, :bar => 2, :baz => 3}) {|k, v| [k.to_s, v.to_s]})
42
+ end
43
+
44
+ def test_powerset
45
+ return unless Set[Set[]] == Set[Set[]] # There's a bug in Ruby 1.8.6 that breaks nested set equality
46
+ assert_equal([[].to_set].to_set,
47
+ powerset([]))
48
+ assert_equal([[].to_set, [1].to_set].to_set,
49
+ powerset([1]))
50
+ assert_equal([[].to_set, [1].to_set, [2].to_set, [1, 2].to_set].to_set,
51
+ powerset([1, 2]))
52
+ assert_equal([[].to_set, [1].to_set, [2].to_set, [3].to_set,
53
+ [1, 2].to_set, [2, 3].to_set, [1, 3].to_set, [1, 2, 3].to_set].to_set,
54
+ powerset([1, 2, 3]))
55
+ end
56
+
57
+ def test_has
58
+ assert(has?(:instance_method, String, :chomp!))
59
+ assert(has?(:private_instance_method, Haml::Engine, :set_locals))
60
+ end
61
+
62
+ def test_enum_with_index
63
+ assert_equal(%w[foo0 bar1 baz2],
64
+ enum_with_index(%w[foo bar baz]).map {|s, i| "#{s}#{i}"})
65
+ end
66
+
67
+ def test_def_static_method
68
+ klass = Class.new
69
+ def_static_method(klass, :static_method, [:arg1, :arg2],
70
+ :sarg1, :sarg2, <<RUBY)
71
+ s = "Always " + arg1
72
+ s << " <% if sarg1 %>and<% else %>but never<% end %> " << arg2
73
+
74
+ <% if sarg2 %>
75
+ s << "."
76
+ <% end %>
77
+ RUBY
78
+ c = klass.new
79
+ assert_equal("Always brush your teeth and comb your hair.",
80
+ c.send(static_method_name(:static_method, true, true),
81
+ "brush your teeth", "comb your hair"))
82
+ assert_equal("Always brush your teeth and comb your hair",
83
+ c.send(static_method_name(:static_method, true, false),
84
+ "brush your teeth", "comb your hair"))
85
+ assert_equal("Always brush your teeth but never play with fire.",
86
+ c.send(static_method_name(:static_method, false, true),
87
+ "brush your teeth", "play with fire"))
88
+ assert_equal("Always brush your teeth but never play with fire",
89
+ c.send(static_method_name(:static_method, false, false),
90
+ "brush your teeth", "play with fire"))
91
+ end
92
+ end
@@ -11,27 +11,27 @@ h1 {
11
11
  CSS
12
12
  assert_equal(<<SASS, css2sass(css))
13
13
  h1
14
- :color red
14
+ color: red
15
15
  SASS
16
- assert_equal(<<SASS, css2sass(css, :alternate => true))
16
+ assert_equal(<<SASS, css2sass(css, :old => true))
17
17
  h1
18
- color: red
18
+ :color red
19
19
  SASS
20
20
  end
21
21
 
22
22
  def test_nesting
23
23
  assert_equal(<<SASS, css2sass(<<CSS))
24
24
  li
25
- :display none
25
+ display: none
26
26
 
27
27
  a
28
- :text-decoration none
28
+ text-decoration: none
29
29
 
30
30
  span
31
- :color yellow
31
+ color: yellow
32
32
 
33
33
  &:hover
34
- :text-decoration underline
34
+ text-decoration: underline
35
35
  SASS
36
36
  li {
37
37
  display: none;
@@ -51,6 +51,28 @@ li a:hover {
51
51
  CSS
52
52
  end
53
53
 
54
+ def test_no_nesting_around_rules
55
+ assert_equal(<<SASS, css2sass(<<CSS))
56
+ div .warning
57
+ color: #d21a19
58
+
59
+
60
+ span .debug
61
+ cursor: crosshair
62
+
63
+
64
+ div .debug
65
+ cursor: default
66
+ SASS
67
+ div .warning {
68
+ color: #d21a19; }
69
+ span .debug {
70
+ cursor: crosshair;}
71
+ div .debug {
72
+ cursor: default; }
73
+ CSS
74
+ end
75
+
54
76
  def test_comments_multiline
55
77
  css = <<CSS
56
78
  /* comment */
@@ -82,24 +104,24 @@ span.turkey {
82
104
  CSS
83
105
  sass = <<SASS
84
106
  elephant.rawr
85
- :rampages excessively
107
+ rampages: excessively
86
108
 
87
109
 
88
110
  span.turkey
89
- :isdinner true
111
+ isdinner: true
90
112
 
91
113
 
92
114
  .turducken
93
- :chimera not_really
115
+ chimera: not_really
94
116
 
95
117
 
96
118
  #overhere
97
- :bored sorta
98
- :better_than thread_pools
119
+ bored: sorta
120
+ better_than: thread_pools
99
121
 
100
122
 
101
123
  #one_more
102
- :finally srsly
124
+ finally: srsly
103
125
  SASS
104
126
  assert_equal(css2sass(css), sass)
105
127
  end
@@ -108,7 +130,7 @@ SASS
108
130
  assert_equal(<<SASS, css2sass(<<CSS))
109
131
  li
110
132
  .one, .two
111
- :color red
133
+ color: red
112
134
  SASS
113
135
  li .one {
114
136
  color: red;
@@ -120,16 +142,16 @@ CSS
120
142
 
121
143
  assert_equal(<<SASS, css2sass(<<CSS))
122
144
  .one
123
- :color green
145
+ color: green
124
146
 
125
147
 
126
148
  .two
127
- :color green
128
- :color red
149
+ color: green
150
+ color: red
129
151
 
130
152
 
131
153
  .three
132
- :color red
154
+ color: red
133
155
  SASS
134
156
  .one, .two {
135
157
  color: green;
@@ -144,23 +166,23 @@ CSS
144
166
  def test_bad_formatting
145
167
  assert_equal(<<SASS, css2sass(<<CSS))
146
168
  hello
147
- :parent true
169
+ parent: true
148
170
 
149
171
  there
150
- :parent false
172
+ parent: false
151
173
 
152
174
  who
153
- :hoo false
175
+ hoo: false
154
176
 
155
177
  why
156
- :y true
178
+ y: true
157
179
 
158
180
  when
159
- :wen nao
181
+ wen: nao
160
182
 
161
183
 
162
184
  down_here
163
- :yeah true
185
+ yeah: true
164
186
  SASS
165
187
  hello {
166
188
  parent: true;
@@ -185,6 +207,29 @@ down_here { yeah: true; }
185
207
  CSS
186
208
  end
187
209
 
210
+ def test_comments_in_selectors
211
+ assert_equal(<<SASS, css2sass(<<CSS))
212
+ .js
213
+ #location-navigation-form .form-submit, #business-listing-form .form-submit, #detailTabs ul, #detailsEnhanced #addTags, #locationSearchList, #moreHoods
214
+ display: none
215
+
216
+
217
+ #navListLeft
218
+ display: none
219
+ SASS
220
+ .js #location-navigation-form .form-submit,
221
+ .js #business-listing-form .form-submit,
222
+ .js #detailTabs ul,
223
+ /* .js #addReview, */
224
+ /* .js #addTags, */
225
+ .js #detailsEnhanced #addTags,
226
+ .js #locationSearchList,
227
+ .js #moreHoods,
228
+ #navListLeft
229
+ { display: none; }
230
+ CSS
231
+ end
232
+
188
233
  private
189
234
 
190
235
  def css2sass(string, opts={})
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
  require File.dirname(__FILE__) + '/../test_helper'
3
3
  require 'sass/engine'
4
+ require 'stringio'
4
5
 
5
6
  class SassEngineTest < Test::Unit::TestCase
6
7
  # A map of erroneous Sass documents to the error messages they should produce.
@@ -8,68 +9,104 @@ class SassEngineTest < Test::Unit::TestCase
8
9
  # if so, the second element should be the line number that should be reported for the error.
9
10
  # If this isn't provided, the tests will assume the line number should be the last line of the document.
10
11
  EXCEPTION_MAP = {
11
- "!a = 1 + " => 'Constant arithmetic error: "1 +".',
12
- "!a = 1 + 2 +" => 'Constant arithmetic error: "1 + 2 +".',
13
- "!a = \"b" => 'Unterminated string: "\\"b".',
14
- "!a = #aaa - a" => 'Undefined operation: "#aaaaaa minus a".',
15
- "!a = #aaa / a" => 'Undefined operation: "#aaaaaa div a".',
16
- "!a = #aaa * a" => 'Undefined operation: "#aaaaaa times a".',
17
- "!a = #aaa % a" => 'Undefined operation: "#aaaaaa mod a".',
18
- "!a = 1 - a" => 'Undefined operation: "1 minus a".',
19
- "!a = 1 * a" => 'Undefined operation: "1 times a".',
20
- "!a = 1 / a" => 'Undefined operation: "1 div a".',
21
- "!a = 1 % a" => 'Undefined operation: "1 mod a".',
22
- ":" => 'Invalid attribute: ":".',
23
- ": a" => 'Invalid attribute: ": a".',
24
- ":= a" => 'Invalid attribute: ":= a".',
25
- "a\n :b" => 'Invalid attribute: ":b ".',
26
- "a\n :b: c" => 'Invalid attribute: ":b: c".',
27
- "a\n :b:c d" => 'Invalid attribute: ":b:c d".',
28
- "a\n :b=c d" => 'Invalid attribute: ":b=c d".',
29
- "a\n :b c;" => 'Invalid attribute: ":b c;" (This isn\'t CSS!).',
30
- "a\n b : c" => 'Invalid attribute: "b : c".',
31
- "a\n b=c: d" => 'Invalid attribute: "b=c: d".',
32
- ":a" => 'Attributes aren\'t allowed at the root of a document.',
33
- "!" => 'Invalid constant: "!".',
34
- "!a" => 'Invalid constant: "!a".',
35
- "! a" => 'Invalid constant: "! a".',
36
- "!a b" => 'Invalid constant: "!a b".',
37
- "a\n\t:b c" => <<END.strip,
38
- A tab character was used for indentation. Sass must be indented using two spaces.
39
- Are you sure you have soft tabs enabled in your editor?
40
- END
41
- "a\n :b c" => "1 space was used for indentation. Sass must be indented using two spaces.",
42
- "a\n :b c" => "4 spaces were used for indentation. Sass must be indented using two spaces.",
43
- "a\n :b c\n !d = 3" => "Constants may only be declared at the root of a document.",
44
- "!a = 1b + 2c" => "Incompatible units: b and c.",
12
+ "!a = 1 + " => 'Expected expression, was end of text.',
13
+ "!a = 1 + 2 +" => 'Expected expression, was end of text.',
14
+ "!a = 1 + 2 + %" => 'Expected expression, was mod token.',
15
+ "!a = foo(\"bar\"" => 'Expected rparen token, was end of text.',
16
+ "!a = 1 }" => 'Unexpected end_interpolation token.',
17
+ "!a = 1 }foo\"" => 'Unexpected end_interpolation token.',
18
+ ":" => 'Invalid property: ":".',
19
+ ": a" => 'Invalid property: ": a".',
20
+ ":= a" => 'Invalid property: ":= a".',
21
+ "a\n :b" => 'Invalid property: ":b " (no value).',
22
+ "a\n b:" => 'Invalid property: "b: " (no value).',
23
+ "a\n :b: c" => 'Invalid property: ":b: c".',
24
+ "a\n :b:c d" => 'Invalid property: ":b:c d".',
25
+ "a\n :b=c d" => 'Invalid property: ":b=c d".',
26
+ "a\n :b c;" => 'Invalid property: ":b c;" (no ";" required at end-of-line).',
27
+ "a\n b: c;" => 'Invalid property: "b: c;" (no ";" required at end-of-line).',
28
+ "a\n b : c" => 'Invalid property: "b : c".',
29
+ "a\n b=c: d" => 'Invalid property: "b=c: d".',
30
+ ":a" => 'Properties aren\'t allowed at the root of a document.',
31
+ "!" => 'Invalid variable: "!".',
32
+ "!a" => 'Invalid variable: "!a".',
33
+ "! a" => 'Invalid variable: "! a".',
34
+ "!a b" => 'Invalid variable: "!a b".',
35
+ "!a = 1b + 2c" => "Incompatible units: 'c' and 'b'.",
36
+ "!a = 1b < 2c" => "Incompatible units: 'c' and 'b'.",
37
+ "!a = 1b > 2c" => "Incompatible units: 'c' and 'b'.",
38
+ "!a = 1b <= 2c" => "Incompatible units: 'c' and 'b'.",
39
+ "!a = 1b >= 2c" => "Incompatible units: 'c' and 'b'.",
40
+ "a\n :b= 1b * 2c" => "2b*c isn't a valid CSS value.",
41
+ "a\n :b= 1b % 2c" => "Cannot modulo by a number with units: 2c.",
42
+ "!a = 2px + #ccc" => "Cannot add a number with units (2px) to a color (#cccccc).",
43
+ "!a = #ccc + 2px" => "Cannot add a number with units (2px) to a color (#cccccc).",
45
44
  "& a\n :b c" => ["Base-level rules cannot contain the parent-selector-referencing character '&'.", 1],
46
- "a\n :b\n c" => "Illegal nesting: Only attributes may be nested beneath attributes.",
47
- "a,\n :b c" => "Rules can\'t end in commas.",
45
+ "a\n :b\n c" => "Illegal nesting: Only properties may be nested beneath properties.",
46
+ "a,\n :b c" => ["Rules can\'t end in commas.", 1],
48
47
  "a," => "Rules can\'t end in commas.",
49
- "a,\n!b = c" => "Rules can\'t end in commas.",
50
- "!a = b\n :c d\n" => "Illegal nesting: Nothing may be nested beneath constants.",
48
+ "a,\n!b = 1" => ["Rules can\'t end in commas.", 1],
49
+ "!a = b\n :c d\n" => "Illegal nesting: Nothing may be nested beneath variable declarations.",
51
50
  "@import foo.sass" => "File to import not found or unreadable: foo.sass.",
52
51
  "@import templates/basic\n foo" => "Illegal nesting: Nothing may be nested beneath import directives.",
53
52
  "foo\n @import templates/basic" => "Import directives may only be used at the root of a document.",
54
53
  "foo\n @import #{File.dirname(__FILE__)}/templates/basic" => "Import directives may only be used at the root of a document.",
55
- "!foo = bar baz !" => "Unterminated constant.",
56
- "!foo = !(foo)" => "Invalid constant.",
54
+ %Q{!foo = "bar" "baz" !} => %Q{Syntax error in '"bar" "baz" !' at character 20.},
57
55
  "=foo\n :color red\n.bar\n +bang" => "Undefined mixin 'bang'.",
58
- ".bar\n =foo\n :color red\n" => "Mixins may only be defined at the root of a document.",
56
+ ".bar\n =foo\n :color red\n" => ["Mixins may only be defined at the root of a document.", 2],
59
57
  "=foo\n :color red\n.bar\n +foo\n :color red" => "Illegal nesting: Nothing may be nested beneath mixin directives.",
60
58
  " a\n b: c" => ["Indenting at the beginning of the document is illegal.", 1],
61
59
  " \n \n\t\n a\n b: c" => ["Indenting at the beginning of the document is illegal.", 4],
60
+ "a\n b: c\n b: c" => ["Inconsistent indentation: 1 space was used for indentation, but the rest of the document was indented using 2 spaces.", 3],
61
+ "a\n b: c\na\n b: c" => ["Inconsistent indentation: 1 space was used for indentation, but the rest of the document was indented using 2 spaces.", 4],
62
+ "a\n\t\tb: c\n\tb: c" => ["Inconsistent indentation: 1 tab was used for indentation, but the rest of the document was indented using 2 tabs.", 3],
63
+ "a\n b: c\n b: c" => ["Inconsistent indentation: 3 spaces were used for indentation, but the rest of the document was indented using 2 spaces.", 3],
64
+ "a\n b: c\n a\n d: e" => ["Inconsistent indentation: 3 spaces were used for indentation, but the rest of the document was indented using 2 spaces.", 4],
65
+ "a\n b: c\na\n d: e" => ["The line was indented 2 levels deeper than the previous line.", 4],
66
+ "a\n b: c\n a\n d: e" => ["The line was indented 3 levels deeper than the previous line.", 4],
67
+ "a\n \tb: c" => ["Indentation can't use both tabs and spaces.", 2],
68
+ "=a(" => 'Invalid mixin "a(".',
69
+ "=a(b)" => 'Mixin argument "b" must begin with an exclamation point (!).',
70
+ "=a(,)" => "Mixin arguments can't be empty.",
71
+ "=a(!)" => "Mixin arguments can't be empty.",
72
+ "=a(!foo bar)" => "Invalid variable \"!foo bar\".",
73
+ "=foo\n bar: baz\n+foo" => ["Properties aren't allowed at the root of a document.", 2],
74
+ "a-\#{!b\n c: d" => ["Expected end_interpolation token, was end of text.", 1],
75
+ "=a(!b = 1, !c)" => "Required arguments must not follow optional arguments \"!c\".",
76
+ "=a(!b = 1)\n :a= !b\ndiv\n +a(1,2)" => "Mixin a takes 1 argument but 2 were passed.",
77
+ "=a(!b)\n :a= !b\ndiv\n +a" => "Mixin a is missing parameter !b.",
78
+ "@else\n a\n b: c" => ["@else must come after @if.", 1],
79
+ "@if false\n@else foo" => "Invalid else directive '@else foo': expected 'if <expr>'.",
80
+ "@if false\n@else if " => "Invalid else directive '@else if': expected 'if <expr>'.",
81
+ "a\n !b = 12\nc\n d = !b" => 'Undefined variable: "!b".',
82
+ "=foo\n !b = 12\nc\n +foo\n d = !b" => 'Undefined variable: "!b".',
83
+ '@for !a from "foo" to 1' => '"foo" is not an integer.',
84
+ '@for !a from 1 to "2"' => '"2" is not an integer.',
85
+ '@for !a from 1 to "foo"' => '"foo" is not an integer.',
86
+ '@for !a from 1 to 1.232323' => '1.232 is not an integer.',
87
+ '@for !a from 1px to 3em' => "Incompatible units: 'em' and 'px'.",
88
+ '@if' => "Invalid if directive '@if': expected expression.",
89
+ '@while' => "Invalid while directive '@while': expected expression.",
90
+ '@debug' => "Invalid debug directive '@debug': expected expression.",
62
91
 
63
92
  # Regression tests
64
- "a\n b:\n c\n d" => ["Illegal nesting: Only attributes may be nested beneath attributes.", 3],
93
+ "a\n b:\n c\n d" => ["Illegal nesting: Only properties may be nested beneath properties.", 3],
65
94
  "& foo\n bar: baz\n blat: bang" => ["Base-level rules cannot contain the parent-selector-referencing character '&'.", 1],
66
95
  "a\n b: c\n& foo\n bar: baz\n blat: bang" => ["Base-level rules cannot contain the parent-selector-referencing character '&'.", 3],
67
96
  }
68
-
97
+
98
+ def teardown
99
+ clean_up_sassc
100
+ end
101
+
69
102
  def test_basic_render
70
103
  renders_correctly "basic", { :style => :compact }
71
104
  end
72
105
 
106
+ def test_empty_render
107
+ assert_equal "", render("")
108
+ end
109
+
73
110
  def test_multiple_calls_to_render
74
111
  sass = Sass::Engine.new("a\n b: c")
75
112
  assert_equal sass.render, sass.render
@@ -82,16 +119,25 @@ END
82
119
  renders_correctly "compressed", { :style => :compressed }
83
120
  end
84
121
 
122
+ def test_flexible_tabulation
123
+ assert_equal("p {\n a: b; }\n p q {\n c: d; }\n",
124
+ render("p\n a: b\n q\n c: d\n"))
125
+ assert_equal("p {\n a: b; }\n p q {\n c: d; }\n",
126
+ render("p\n\ta: b\n\tq\n\t\tc: d\n"))
127
+ end
128
+
85
129
  def test_exceptions
86
130
  EXCEPTION_MAP.each do |key, value|
131
+ line = 10
87
132
  begin
88
- Sass::Engine.new(key).render
133
+ Sass::Engine.new(key, :filename => __FILE__, :line => line).render
89
134
  rescue Sass::SyntaxError => err
90
135
  value = [value] unless value.is_a?(Array)
91
136
 
92
137
  assert_equal(value.first, err.message, "Line: #{key}")
93
- assert_equal(value[1] || key.split("\n").length, err.sass_line, "Line: #{key}")
94
- assert_match(/\(sass\):[0-9]+/, err.backtrace[0], "Line: #{key}")
138
+ assert_equal(__FILE__, err.sass_filename)
139
+ assert_equal((value[1] || key.split("\n").length) + line - 1, err.sass_line, "Line: #{key}")
140
+ assert_match(/#{Regexp.escape(__FILE__)}:[0-9]+/, err.backtrace[0], "Line: #{key}")
95
141
  else
96
142
  assert(false, "Exception not raised for\n#{key}")
97
143
  end
@@ -101,7 +147,7 @@ END
101
147
  def test_exception_line
102
148
  to_render = <<SASS
103
149
  rule
104
- :attr val
150
+ :prop val
105
151
  // comment!
106
152
 
107
153
  :broken
@@ -115,9 +161,26 @@ SASS
115
161
  end
116
162
  end
117
163
 
164
+ def test_exception_location
165
+ to_render = <<SASS
166
+ rule
167
+ :prop val
168
+ // comment!
169
+
170
+ :broken
171
+ SASS
172
+ begin
173
+ Sass::Engine.new(to_render, :filename => __FILE__, :line => (__LINE__-7)).render
174
+ rescue Sass::SyntaxError => err
175
+ assert_equal(__FILE__, err.sass_filename)
176
+ assert_equal((__LINE__-6), err.sass_line)
177
+ else
178
+ assert(false, "Exception not raised for '#{to_render}'!")
179
+ end
180
+ end
181
+
118
182
  def test_imported_exception
119
- [1, 2].each do |i|
120
- i = nil if i == 1
183
+ [nil, 2].each do |i|
121
184
  begin
122
185
  Sass::Engine.new("@import bork#{i}", :load_paths => [File.dirname(__FILE__) + '/templates/']).render
123
186
  rescue Sass::SyntaxError => err
@@ -130,19 +193,44 @@ SASS
130
193
  end
131
194
 
132
195
  def test_css_import
133
- assert_equal("@import url(./fonts.css) screen;", render("@import url(./fonts.css) screen"))
134
- assert_equal("@import \"./fonts.css\" screen;", render("@import \"./fonts.css\" screen"))
196
+ assert_equal("@import url(./fonts.css) screen;\n", render("@import url(./fonts.css) screen"))
197
+ assert_equal("@import \"./fonts.css\" screen;\n", render("@import \"./fonts.css\" screen"))
135
198
  end
136
199
 
137
200
  def test_sass_import
201
+ assert !File.exists?(sassc_path("importee"))
138
202
  renders_correctly "import", { :style => :compact, :load_paths => [File.dirname(__FILE__) + "/templates"] }
203
+ assert File.exists?(sassc_path("importee"))
204
+ end
205
+
206
+ def test_no_cache
207
+ assert !File.exists?(sassc_path("importee"))
208
+ renders_correctly("import", {
209
+ :style => :compact, :cache => false,
210
+ :load_paths => [File.dirname(__FILE__) + "/templates"],
211
+ })
212
+ assert !File.exists?(sassc_path("importee"))
213
+ end
214
+
215
+ def test_units
216
+ renders_correctly "units"
139
217
  end
140
218
 
141
219
  def test_default_function
142
- assert_equal("foo {\n bar: url(foo.png); }\n", render("foo\n bar = url(foo.png)\n"));
220
+ assert_equal("foo {\n bar: url(foo.png); }\n", render(%Q{foo\n bar = url("foo.png")\n}));
143
221
  assert_equal("foo {\n bar: url(); }\n", render("foo\n bar = url()\n"));
144
222
  end
145
223
 
224
+ def test_string_minus
225
+ assert_equal("foo {\n bar: baz-boom-bat; }\n", render(%Q{foo\n bar = "baz"-"boom"-"bat"}))
226
+ assert_equal("foo {\n bar: -baz-boom; }\n", render(%Q{foo\n bar = -"baz"-"boom"}))
227
+ end
228
+
229
+ def test_string_div
230
+ assert_equal("foo {\n bar: baz/boom/bat; }\n", render(%Q{foo\n bar = "baz"/"boom"/"bat"}))
231
+ assert_equal("foo {\n bar: /baz/boom; }\n", render(%Q{foo\n bar = /"baz"/"boom"}))
232
+ end
233
+
146
234
  def test_basic_multiline_selector
147
235
  assert_equal("#foo #bar,\n#baz #boom {\n foo: bar; }\n",
148
236
  render("#foo #bar,\n#baz #boom\n :foo bar"))
@@ -163,21 +251,21 @@ SASS
163
251
 
164
252
  def test_colon_only
165
253
  begin
166
- render("a\n b: c", :attribute_syntax => :normal)
254
+ render("a\n b: c", :property_syntax => :old)
167
255
  rescue Sass::SyntaxError => e
168
- assert_equal("Illegal attribute syntax: can't use alternate syntax when :attribute_syntax => :normal is set.",
256
+ assert_equal("Illegal property syntax: can't use new syntax when :property_syntax => :old is set.",
169
257
  e.message)
170
258
  else
171
- assert(false, "SyntaxError not raised for :attribute_syntax => :normal")
259
+ assert(false, "SyntaxError not raised for :property_syntax => :old")
172
260
  end
173
261
 
174
262
  begin
175
- render("a\n :b c", :attribute_syntax => :alternate)
263
+ render("a\n :b c", :property_syntax => :new)
176
264
  rescue Sass::SyntaxError => e
177
- assert_equal("Illegal attribute syntax: can't use normal syntax when :attribute_syntax => :alternate is set.",
265
+ assert_equal("Illegal property syntax: can't use old syntax when :property_syntax => :new is set.",
178
266
  e.message)
179
267
  else
180
- assert(false, "SyntaxError not raised for :attribute_syntax => :alternate")
268
+ assert(false, "SyntaxError not raised for :property_syntax => :new")
181
269
  end
182
270
  end
183
271
 
@@ -192,7 +280,7 @@ SASS
192
280
  end
193
281
 
194
282
  def test_directive
195
- assert_equal("@a b;", render("@a b"))
283
+ assert_equal("@a b;\n", render("@a b"))
196
284
 
197
285
  assert_equal("@a {\n b: c; }\n", render("@a\n :b c"))
198
286
  assert_equal("@a { b: c; }\n", render("@a\n :b c", :style => :compact))
@@ -252,6 +340,54 @@ END
252
340
  assert_equal("@a{b:c;#d{e:f}g:h}\n", render(to_render, :style => :compressed))
253
341
  end
254
342
 
343
+ def test_line_annotations
344
+ assert_equal(<<CSS, render(<<SASS, :line_comments => true, :style => :compact))
345
+ /* line 2, test_line_annotations_inline.sass */
346
+ foo bar { foo: bar; }
347
+ /* line 5, test_line_annotations_inline.sass */
348
+ foo baz { blip: blop; }
349
+
350
+ /* line 9, test_line_annotations_inline.sass */
351
+ floodle { flop: blop; }
352
+
353
+ /* line 18, test_line_annotations_inline.sass */
354
+ bup { mix: on; }
355
+ /* line 15, test_line_annotations_inline.sass */
356
+ bup mixin { moop: mup; }
357
+
358
+ /* line 22, test_line_annotations_inline.sass */
359
+ bip hop, skip hop { a: b; }
360
+ CSS
361
+ foo
362
+ bar
363
+ foo: bar
364
+
365
+ baz
366
+ blip: blop
367
+
368
+
369
+ floodle
370
+
371
+ flop: blop
372
+
373
+ =mxn
374
+ mix: on
375
+ mixin
376
+ moop: mup
377
+
378
+ bup
379
+ +mxn
380
+
381
+ bip, skip
382
+ hop
383
+ a: b
384
+ SASS
385
+ end
386
+
387
+ def test_line_annotations_with_filename
388
+ renders_correctly "line_numbers", :line_comments => true, :load_paths => [File.dirname(__FILE__) + "/templates"]
389
+ end
390
+
255
391
  def test_empty_first_line
256
392
  assert_equal("#a {\n b: c; }\n", render("#a\n\n b: c"))
257
393
  end
@@ -266,8 +402,8 @@ END
266
402
  end
267
403
 
268
404
  def test_or_eq
269
- assert_equal("foo {\n a: b; }\n", render("!foo = b\n!foo ||= c\nfoo\n a = !foo"))
270
- assert_equal("foo {\n a: b; }\n", render("!foo ||= b\nfoo\n a = !foo"))
405
+ assert_equal("foo {\n a: b; }\n", render(%Q{!foo = "b"\n!foo ||= "c"\nfoo\n a = !foo}))
406
+ assert_equal("foo {\n a: b; }\n", render(%Q{!foo ||= "b"\nfoo\n a = !foo}))
271
407
  end
272
408
 
273
409
  def test_mixins
@@ -275,27 +411,413 @@ END
275
411
  end
276
412
 
277
413
  def test_mixins_dont_interfere_with_sibling_combinator
278
- assert_equal("foo + bar {\n a: b; }\n", render("foo\n + bar\n a: b"))
279
414
  assert_equal("foo + bar {\n a: b; }\nfoo + baz {\n c: d; }\n",
280
415
  render("foo\n +\n bar\n a: b\n baz\n c: d"))
281
416
  end
282
417
 
283
- private
418
+ def test_mixin_args
419
+ assert_equal("blat {\n baz: hi; }\n", render(<<SASS))
420
+ =foo(!bar)
421
+ baz = !bar
422
+ blat
423
+ +foo(\"hi\")
424
+ SASS
425
+ assert_equal("blat {\n baz: 3; }\n", render(<<SASS))
426
+ =foo(!a, !b)
427
+ baz = !a + !b
428
+ blat
429
+ +foo(1, 2)
430
+ SASS
431
+ assert_equal("blat {\n baz: 4;\n baz: 3;\n baz: 5;\n bang: 3; }\n", render(<<SASS))
432
+ =foo(!c = (6 + 4) / 2)
433
+ baz = !c
434
+ !c = 3
435
+ blat
436
+ +foo(!c + 1)
437
+ +foo((!c + 3)/2)
438
+ +foo
439
+ bang = !c
440
+ SASS
441
+ end
442
+
443
+ def test_default_values_for_mixin_arguments
444
+ assert_equal("white {\n color: white; }\n\nblack {\n color: black; }\n", render(<<SASS))
445
+ =foo(!a = #FFF)
446
+ :color= !a
447
+ white
448
+ +foo
449
+ black
450
+ +foo(#000)
451
+ SASS
452
+ assert_equal(<<CSS, render(<<SASS))
453
+ one {
454
+ color: white;
455
+ padding: 1px;
456
+ margin: 4px; }
457
+
458
+ two {
459
+ color: white;
460
+ padding: 2px;
461
+ margin: 5px; }
462
+
463
+ three {
464
+ color: white;
465
+ padding: 2px;
466
+ margin: 3px; }
467
+ CSS
468
+ !a = 5px
469
+ =foo(!a, !b = 1px, !c = 3px + !b)
470
+ :color= !a
471
+ :padding= !b
472
+ :margin= !c
473
+ one
474
+ +foo(#fff)
475
+ two
476
+ +foo(#fff, 2px)
477
+ three
478
+ +foo(#fff, 2px, 3px)
479
+ SASS
480
+ end
481
+
482
+ def test_interpolation
483
+ assert_equal("a-1 {\n b-2-3: c-3; }\n", render(<<SASS))
484
+ !a = 1
485
+ !b = 2
486
+ !c = 3
487
+ a-\#{!a}
488
+ b-\#{!b}-\#{!c}: c-\#{!a + !b}
489
+ SASS
490
+ end
491
+
492
+ def test_if_directive
493
+ assert_equal("a {\n b: 1; }\n", render(<<SASS))
494
+ !var = true
495
+ a
496
+ @if !var
497
+ b: 1
498
+ @if not !var
499
+ b: 2
500
+ SASS
501
+ end
502
+
503
+ def test_for
504
+ assert_equal(<<CSS, render(<<SASS))
505
+ a-0 {
506
+ 2i: 0; }
507
+
508
+ a-1 {
509
+ 2i: 2; }
510
+
511
+ a-2 {
512
+ 2i: 4; }
513
+
514
+ a-3 {
515
+ 2i: 6; }
516
+
517
+ b-1 {
518
+ j-1: 0; }
519
+
520
+ b-2 {
521
+ j-1: 1; }
522
+
523
+ b-3 {
524
+ j-1: 2; }
525
+
526
+ b-4 {
527
+ j-1: 3; }
528
+ CSS
529
+ !a = 3
530
+ @for !i from 0 to !a + 1
531
+ a-\#{!i}
532
+ 2i = 2 * !i
533
+
534
+ @for !j from 1 through 4
535
+ b-\#{!j}
536
+ j-1 = !j - 1
537
+ SASS
538
+ end
539
+
540
+ def test_while
541
+ assert_equal(<<CSS, render(<<SASS))
542
+ a-5 {
543
+ blooble: gloop; }
544
+
545
+ a-4 {
546
+ blooble: gloop; }
284
547
 
548
+ a-3 {
549
+ blooble: gloop; }
550
+
551
+ a-2 {
552
+ blooble: gloop; }
553
+
554
+ a-1 {
555
+ blooble: gloop; }
556
+ CSS
557
+ !a = 5
558
+ @while !a != 0
559
+ a-\#{!a}
560
+ blooble: gloop
561
+ !a = !a - 1
562
+ SASS
563
+ end
564
+
565
+ def test_else
566
+ assert_equal(<<CSS, render(<<SASS))
567
+ a {
568
+ t1: t;
569
+ t2: t;
570
+ t3: t;
571
+ t4: t; }
572
+ CSS
573
+ a
574
+ @if true
575
+ t1: t
576
+ @else
577
+ f1: f
578
+
579
+ @if false
580
+ f2: f
581
+ @else
582
+ t2: t
583
+
584
+ @if false
585
+ f3: f1
586
+ @else if 1 + 1 == 3
587
+ f3: f2
588
+ @else
589
+ t3: t
590
+
591
+ @if false
592
+ f4: f1
593
+ @else if 1 + 1 == 2
594
+ t4: t
595
+ @else
596
+ f4: f2
597
+
598
+ @if false
599
+ f5: f1
600
+ @else if false
601
+ f5: f2
602
+ SASS
603
+ end
604
+
605
+ def test_variable_reassignment
606
+ assert_equal(<<CSS, render(<<SASS))
607
+ a {
608
+ b: 1;
609
+ c: 2; }
610
+ CSS
611
+ !a = 1
612
+ a
613
+ b = !a
614
+ !a = 2
615
+ c = !a
616
+ SASS
617
+ end
618
+
619
+ def test_variable_scope
620
+ assert_equal(<<CSS, render(<<SASS))
621
+ a {
622
+ b-1: c;
623
+ b-2: c;
624
+ d: 12; }
625
+
626
+ b {
627
+ d: 17; }
628
+ CSS
629
+ !i = 12
630
+ a
631
+ @for !i from 1 through 2
632
+ b-\#{!i}: c
633
+ d = !i
634
+
635
+ =foo
636
+ !i = 17
637
+
638
+ b
639
+ +foo
640
+ d = !i
641
+ SASS
642
+ end
643
+
644
+ def test_argument_error
645
+ assert_raise(Sass::SyntaxError) { render("a\n b = hsl(1)") }
646
+ end
647
+
648
+ def test_comments_at_the_top_of_a_document
649
+ render(<<SASS)
650
+ //
651
+ This is a comment that
652
+ continues to the second line.
653
+ foo
654
+ bar: baz
655
+ SASS
656
+ end
657
+
658
+ def test_loud_comments_containing_a_comment_close
659
+ actual_css = render(<<SASS)
660
+ /*
661
+ This is a comment that
662
+ continues to the second line. */
663
+ foo
664
+ bar: baz
665
+ SASS
666
+ assert_equal(<<CSS, actual_css)
667
+ /* This is a comment that
668
+ * continues to the second line. */
669
+ foo {
670
+ bar: baz; }
671
+ CSS
672
+ end
673
+
674
+ def test_quoted_colon
675
+ assert_equal(<<CSS, render(<<SASS))
676
+ a b[foo="bar: baz"] {
677
+ c: d; }
678
+ CSS
679
+ a
680
+ b[foo="bar: baz"]
681
+ c: d
682
+ SASS
683
+ end
684
+
685
+ def test_quoted_comma
686
+ assert_equal(<<CSS, render(<<SASS))
687
+ a b[foo="bar, baz"] {
688
+ c: d; }
689
+ CSS
690
+ a
691
+ b[foo="bar, baz"]
692
+ c: d
693
+ SASS
694
+ end
695
+
696
+ def test_quoted_ampersand
697
+ assert_equal(<<CSS, render(<<SASS))
698
+ a b[foo="bar & baz"] {
699
+ c: d; }
700
+ CSS
701
+ a
702
+ b[foo="bar & baz"]
703
+ c: d
704
+ SASS
705
+ end
706
+
707
+ # Regression tests
708
+
709
+ def test_comment_beneath_prop
710
+ assert_equal(<<RESULT, render(<<SOURCE))
711
+ .box {
712
+ border-style: solid; }
713
+ RESULT
714
+ .box
715
+ :border
716
+ //:color black
717
+ :style solid
718
+ SOURCE
719
+
720
+ assert_equal(<<RESULT, render(<<SOURCE))
721
+ .box {
722
+ /* :color black */
723
+ border-style: solid; }
724
+ RESULT
725
+ .box
726
+ :border
727
+ /*:color black
728
+ :style solid
729
+ SOURCE
730
+
731
+ assert_equal(<<RESULT, render(<<SOURCE, :style => :compressed))
732
+ .box{border-style:solid}
733
+ RESULT
734
+ .box
735
+ :border
736
+ /*:color black
737
+ :style solid
738
+ SOURCE
739
+ end
740
+
741
+ def test_compressed_comment_beneath_directive
742
+ assert_equal(<<RESULT, render(<<SOURCE, :style => :compressed))
743
+ @foo{a:b}
744
+ RESULT
745
+ @foo
746
+ a: b
747
+ /*b: c
748
+ SOURCE
749
+ end
750
+
751
+ def test_comment_with_crazy_indentation
752
+ assert_equal(<<CSS, render(<<SASS))
753
+ /* This is a loud comment:
754
+ * Where the indentation is wonky. */
755
+ .comment {
756
+ width: 1px; }
757
+ CSS
758
+ /*
759
+ This is a loud comment:
760
+ Where the indentation is wonky.
761
+ //
762
+ This is a silent comment:
763
+ Where the indentation is wonky.
764
+ .comment
765
+ width: 1px
766
+ SASS
767
+ end
768
+
769
+ def test_plus_with_space
770
+ assert_equal(<<CSS, render(<<SASS))
771
+ a + b {
772
+ color: green; }
773
+ CSS
774
+ a
775
+ + b
776
+ color: green
777
+ SASS
778
+ end
779
+
780
+ def test_empty_line_comment
781
+ assert_equal(<<CSS, render(<<SASS))
782
+ /* Foo
783
+ *
784
+ * Bar */
785
+ CSS
786
+ /*
787
+ Foo
788
+
789
+ Bar
790
+ SASS
791
+ end
792
+
793
+ private
794
+
285
795
  def render(sass, options = {})
796
+ munge_filename options
286
797
  Sass::Engine.new(sass, options).render
287
798
  end
288
799
 
289
800
  def renders_correctly(name, options={})
290
801
  sass_file = load_file(name, "sass")
291
802
  css_file = load_file(name, "css")
803
+ options[:filename] ||= filename(name, "sass")
804
+ options[:css_filename] ||= filename(name, "css")
292
805
  css_result = Sass::Engine.new(sass_file, options).render
293
806
  assert_equal css_file, css_result
294
807
  end
295
808
 
296
809
  def load_file(name, type = "sass")
297
810
  @result = ''
298
- File.new(File.dirname(__FILE__) + "/#{type == 'sass' ? 'templates' : 'results'}/#{name}.#{type}").each_line { |l| @result += l }
811
+ File.new(filename(name, type)).each_line { |l| @result += l }
299
812
  @result
300
813
  end
814
+
815
+ def filename(name, type)
816
+ File.dirname(__FILE__) + "/#{type == 'sass' ? 'templates' : 'results'}/#{name}.#{type}"
817
+ end
818
+
819
+ def sassc_path(template)
820
+ sassc_path = File.join(File.dirname(__FILE__) + "/templates/#{template}.sass")
821
+ Sass::Files.send(:sassc_filename, sassc_path, Sass::Engine::DEFAULT_OPTIONS)
822
+ end
301
823
  end