honkster-haml 2.1.0

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 (176) hide show
  1. data/FAQ +138 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +332 -0
  4. data/Rakefile +198 -0
  5. data/VERSION.yml +4 -0
  6. data/bin/css2sass +7 -0
  7. data/bin/haml +9 -0
  8. data/bin/html2haml +7 -0
  9. data/bin/sass +8 -0
  10. data/extra/haml-mode.el +596 -0
  11. data/extra/sass-mode.el +137 -0
  12. data/init.rb +8 -0
  13. data/lib/haml/buffer.rb +255 -0
  14. data/lib/haml/engine.rb +268 -0
  15. data/lib/haml/error.rb +22 -0
  16. data/lib/haml/exec.rb +395 -0
  17. data/lib/haml/filters.rb +276 -0
  18. data/lib/haml/helpers/action_view_extensions.rb +45 -0
  19. data/lib/haml/helpers/action_view_mods.rb +181 -0
  20. data/lib/haml/helpers.rb +468 -0
  21. data/lib/haml/html.rb +218 -0
  22. data/lib/haml/precompiler.rb +901 -0
  23. data/lib/haml/shared.rb +45 -0
  24. data/lib/haml/template/patch.rb +58 -0
  25. data/lib/haml/template/plugin.rb +72 -0
  26. data/lib/haml/template.rb +51 -0
  27. data/lib/haml/util.rb +77 -0
  28. data/lib/haml/version.rb +25 -0
  29. data/lib/haml.rb +1043 -0
  30. data/lib/sass/css.rb +388 -0
  31. data/lib/sass/engine.rb +499 -0
  32. data/lib/sass/environment.rb +33 -0
  33. data/lib/sass/error.rb +35 -0
  34. data/lib/sass/plugin/merb.rb +56 -0
  35. data/lib/sass/plugin/rails.rb +24 -0
  36. data/lib/sass/plugin.rb +203 -0
  37. data/lib/sass/repl.rb +51 -0
  38. data/lib/sass/script/bool.rb +13 -0
  39. data/lib/sass/script/color.rb +97 -0
  40. data/lib/sass/script/funcall.rb +28 -0
  41. data/lib/sass/script/functions.rb +122 -0
  42. data/lib/sass/script/lexer.rb +152 -0
  43. data/lib/sass/script/literal.rb +80 -0
  44. data/lib/sass/script/number.rb +231 -0
  45. data/lib/sass/script/operation.rb +30 -0
  46. data/lib/sass/script/parser.rb +142 -0
  47. data/lib/sass/script/string.rb +18 -0
  48. data/lib/sass/script/unary_operation.rb +21 -0
  49. data/lib/sass/script/variable.rb +20 -0
  50. data/lib/sass/script.rb +38 -0
  51. data/lib/sass/tree/attr_node.rb +64 -0
  52. data/lib/sass/tree/comment_node.rb +34 -0
  53. data/lib/sass/tree/debug_node.rb +22 -0
  54. data/lib/sass/tree/directive_node.rb +50 -0
  55. data/lib/sass/tree/file_node.rb +27 -0
  56. data/lib/sass/tree/for_node.rb +29 -0
  57. data/lib/sass/tree/if_node.rb +27 -0
  58. data/lib/sass/tree/mixin_def_node.rb +18 -0
  59. data/lib/sass/tree/mixin_node.rb +34 -0
  60. data/lib/sass/tree/node.rb +99 -0
  61. data/lib/sass/tree/rule_node.rb +120 -0
  62. data/lib/sass/tree/variable_node.rb +24 -0
  63. data/lib/sass/tree/while_node.rb +20 -0
  64. data/lib/sass.rb +1062 -0
  65. data/rails/init.rb +1 -0
  66. data/test/benchmark.rb +99 -0
  67. data/test/haml/engine_test.rb +782 -0
  68. data/test/haml/helper_test.rb +224 -0
  69. data/test/haml/html2haml_test.rb +92 -0
  70. data/test/haml/markaby/standard.mab +52 -0
  71. data/test/haml/mocks/article.rb +6 -0
  72. data/test/haml/results/content_for_layout.xhtml +15 -0
  73. data/test/haml/results/eval_suppressed.xhtml +9 -0
  74. data/test/haml/results/filters.xhtml +62 -0
  75. data/test/haml/results/helpers.xhtml +93 -0
  76. data/test/haml/results/helpful.xhtml +10 -0
  77. data/test/haml/results/just_stuff.xhtml +68 -0
  78. data/test/haml/results/list.xhtml +12 -0
  79. data/test/haml/results/nuke_inner_whitespace.xhtml +40 -0
  80. data/test/haml/results/nuke_outer_whitespace.xhtml +148 -0
  81. data/test/haml/results/original_engine.xhtml +20 -0
  82. data/test/haml/results/partial_layout.xhtml +5 -0
  83. data/test/haml/results/partials.xhtml +21 -0
  84. data/test/haml/results/render_layout.xhtml +3 -0
  85. data/test/haml/results/silent_script.xhtml +74 -0
  86. data/test/haml/results/standard.xhtml +42 -0
  87. data/test/haml/results/tag_parsing.xhtml +23 -0
  88. data/test/haml/results/very_basic.xhtml +5 -0
  89. data/test/haml/results/whitespace_handling.xhtml +89 -0
  90. data/test/haml/rhtml/_av_partial_1.rhtml +12 -0
  91. data/test/haml/rhtml/_av_partial_2.rhtml +8 -0
  92. data/test/haml/rhtml/action_view.rhtml +62 -0
  93. data/test/haml/rhtml/standard.rhtml +54 -0
  94. data/test/haml/template_test.rb +204 -0
  95. data/test/haml/templates/_av_partial_1.haml +9 -0
  96. data/test/haml/templates/_av_partial_1_ugly.haml +9 -0
  97. data/test/haml/templates/_av_partial_2.haml +5 -0
  98. data/test/haml/templates/_av_partial_2_ugly.haml +5 -0
  99. data/test/haml/templates/_layout.erb +3 -0
  100. data/test/haml/templates/_layout_for_partial.haml +3 -0
  101. data/test/haml/templates/_partial.haml +8 -0
  102. data/test/haml/templates/_text_area.haml +3 -0
  103. data/test/haml/templates/action_view.haml +47 -0
  104. data/test/haml/templates/action_view_ugly.haml +47 -0
  105. data/test/haml/templates/breakage.haml +8 -0
  106. data/test/haml/templates/content_for_layout.haml +10 -0
  107. data/test/haml/templates/eval_suppressed.haml +11 -0
  108. data/test/haml/templates/filters.haml +66 -0
  109. data/test/haml/templates/helpers.haml +95 -0
  110. data/test/haml/templates/helpful.haml +11 -0
  111. data/test/haml/templates/just_stuff.haml +83 -0
  112. data/test/haml/templates/list.haml +12 -0
  113. data/test/haml/templates/nuke_inner_whitespace.haml +32 -0
  114. data/test/haml/templates/nuke_outer_whitespace.haml +144 -0
  115. data/test/haml/templates/original_engine.haml +17 -0
  116. data/test/haml/templates/partial_layout.haml +3 -0
  117. data/test/haml/templates/partialize.haml +1 -0
  118. data/test/haml/templates/partials.haml +12 -0
  119. data/test/haml/templates/render_layout.haml +2 -0
  120. data/test/haml/templates/silent_script.haml +40 -0
  121. data/test/haml/templates/standard.haml +42 -0
  122. data/test/haml/templates/standard_ugly.haml +1 -0
  123. data/test/haml/templates/tag_parsing.haml +21 -0
  124. data/test/haml/templates/very_basic.haml +4 -0
  125. data/test/haml/templates/whitespace_handling.haml +87 -0
  126. data/test/linked_rails.rb +12 -0
  127. data/test/sass/css2sass_test.rb +193 -0
  128. data/test/sass/engine_test.rb +655 -0
  129. data/test/sass/functions_test.rb +96 -0
  130. data/test/sass/more_results/more1.css +9 -0
  131. data/test/sass/more_results/more1_with_line_comments.css +26 -0
  132. data/test/sass/more_results/more_import.css +29 -0
  133. data/test/sass/more_templates/_more_partial.sass +2 -0
  134. data/test/sass/more_templates/more1.sass +23 -0
  135. data/test/sass/more_templates/more_import.sass +11 -0
  136. data/test/sass/plugin_test.rb +208 -0
  137. data/test/sass/results/alt.css +4 -0
  138. data/test/sass/results/basic.css +9 -0
  139. data/test/sass/results/compact.css +5 -0
  140. data/test/sass/results/complex.css +87 -0
  141. data/test/sass/results/compressed.css +1 -0
  142. data/test/sass/results/expanded.css +19 -0
  143. data/test/sass/results/import.css +29 -0
  144. data/test/sass/results/line_numbers.css +49 -0
  145. data/test/sass/results/mixins.css +95 -0
  146. data/test/sass/results/multiline.css +24 -0
  147. data/test/sass/results/nested.css +22 -0
  148. data/test/sass/results/parent_ref.css +13 -0
  149. data/test/sass/results/script.css +16 -0
  150. data/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
  151. data/test/sass/results/subdir/subdir.css +3 -0
  152. data/test/sass/results/units.css +11 -0
  153. data/test/sass/script_test.rb +250 -0
  154. data/test/sass/templates/_partial.sass +2 -0
  155. data/test/sass/templates/alt.sass +16 -0
  156. data/test/sass/templates/basic.sass +23 -0
  157. data/test/sass/templates/bork.sass +2 -0
  158. data/test/sass/templates/bork2.sass +2 -0
  159. data/test/sass/templates/compact.sass +17 -0
  160. data/test/sass/templates/complex.sass +309 -0
  161. data/test/sass/templates/compressed.sass +15 -0
  162. data/test/sass/templates/expanded.sass +17 -0
  163. data/test/sass/templates/import.sass +11 -0
  164. data/test/sass/templates/importee.sass +19 -0
  165. data/test/sass/templates/line_numbers.sass +13 -0
  166. data/test/sass/templates/mixins.sass +76 -0
  167. data/test/sass/templates/multiline.sass +20 -0
  168. data/test/sass/templates/nested.sass +25 -0
  169. data/test/sass/templates/parent_ref.sass +25 -0
  170. data/test/sass/templates/script.sass +101 -0
  171. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
  172. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
  173. data/test/sass/templates/subdir/subdir.sass +6 -0
  174. data/test/sass/templates/units.sass +11 -0
  175. data/test/test_helper.rb +21 -0
  176. metadata +272 -0
@@ -0,0 +1,655 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../test_helper'
3
+ require 'sass/engine'
4
+ require 'stringio'
5
+
6
+ class SassEngineTest < Test::Unit::TestCase
7
+ # A map of erroneous Sass documents to the error messages they should produce.
8
+ # The error messages may be arrays;
9
+ # if so, the second element should be the line number that should be reported for the error.
10
+ # If this isn't provided, the tests will assume the line number should be the last line of the document.
11
+ EXCEPTION_MAP = {
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 attribute: ":".',
19
+ ": a" => 'Invalid attribute: ": a".',
20
+ ":= a" => 'Invalid attribute: ":= a".',
21
+ "a\n :b" => 'Invalid attribute: ":b ".',
22
+ "a\n :b: c" => 'Invalid attribute: ":b: c".',
23
+ "a\n :b:c d" => 'Invalid attribute: ":b:c d".',
24
+ "a\n :b=c d" => 'Invalid attribute: ":b=c d".',
25
+ "a\n :b c;" => 'Invalid attribute: ":b c;" (This isn\'t CSS!).',
26
+ "a\n b : c" => 'Invalid attribute: "b : c".',
27
+ "a\n b=c: d" => 'Invalid attribute: "b=c: d".',
28
+ ":a" => 'Attributes aren\'t allowed at the root of a document.',
29
+ "!" => 'Invalid variable: "!".',
30
+ "!a" => 'Invalid variable: "!a".',
31
+ "! a" => 'Invalid variable: "! a".',
32
+ "!a b" => 'Invalid variable: "!a b".',
33
+ "!a = 1b + 2c" => "Incompatible units: 'c' and 'b'.",
34
+ "a\n :b= 1b * 2c" => "2b*c isn't a valid CSS value.",
35
+ "a\n :b= 1b % 2c" => "Cannot modulo by a number with units: 2c.",
36
+ "!a = 2px + #ccc" => "Cannot add a number with units (2px) to a color (#cccccc).",
37
+ "!a = #ccc + 2px" => "Cannot add a number with units (2px) to a color (#cccccc).",
38
+ "& a\n :b c" => ["Base-level rules cannot contain the parent-selector-referencing character '&'.", 1],
39
+ "a\n :b\n c" => "Illegal nesting: Only attributes may be nested beneath attributes.",
40
+ "a,\n :b c" => ["Rules can\'t end in commas.", 1],
41
+ "a," => "Rules can\'t end in commas.",
42
+ "a,\n!b = 1" => ["Rules can\'t end in commas.", 1],
43
+ "!a = b\n :c d\n" => "Illegal nesting: Nothing may be nested beneath variable declarations.",
44
+ "@import foo.sass" => "File to import not found or unreadable: foo.sass.",
45
+ "@import templates/basic\n foo" => "Illegal nesting: Nothing may be nested beneath import directives.",
46
+ "foo\n @import templates/basic" => "Import directives may only be used at the root of a document.",
47
+ %Q{!foo = "bar" "baz" !} => %Q{Syntax error in '"bar" "baz" !' at character 20.},
48
+ "=foo\n :color red\n.bar\n +bang" => "Undefined mixin 'bang'.",
49
+ ".bar\n =foo\n :color red\n" => ["Mixins may only be defined at the root of a document.", 2],
50
+ "=foo\n :color red\n.bar\n +foo\n :color red" => "Illegal nesting: Nothing may be nested beneath mixin directives.",
51
+ " a\n b: c" => ["Indenting at the beginning of the document is illegal.", 1],
52
+ " \n \n\t\n a\n b: c" => ["Indenting at the beginning of the document is illegal.", 4],
53
+ "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],
54
+ "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],
55
+ "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],
56
+ "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],
57
+ "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],
58
+ "a\n b: c\na\n d: e" => ["The line was indented 2 levels deeper than the previous line.", 4],
59
+ "a\n b: c\n a\n d: e" => ["The line was indented 3 levels deeper than the previous line.", 4],
60
+ "a\n \tb: c" => ["Indentation can't use both tabs and spaces.", 2],
61
+ "=a(" => 'Invalid mixin "a(".',
62
+ "=a(b)" => 'Mixin argument "b" must begin with an exclamation point (!).',
63
+ "=a(,)" => "Mixin arguments can't be empty.",
64
+ "=a(!)" => "Mixin arguments can't be empty.",
65
+ "=a(!foo bar)" => "Invalid variable \"!foo bar\".",
66
+ "=foo\n bar: baz\n+foo" => ["Attributes aren't allowed at the root of a document.", 2],
67
+ "a-\#{!b\n c: d" => ["Expected end_interpolation token, was end of text.", 1],
68
+ "=a(!b = 1, !c)" => "Required arguments must not follow optional arguments \"!c\".",
69
+ "=a(!b = 1)\n :a= !b\ndiv\n +a(1,2)" => "Mixin a takes 1 argument but 2 were passed.",
70
+ "=a(!b)\n :a= !b\ndiv\n +a" => "Mixin a is missing parameter !b.",
71
+ "@else\n a\n b: c" => ["@else must come after @if.", 1],
72
+ "@if false\n@else foo" => "Invalid else directive '@else foo': expected 'if <expr>'.",
73
+ "@if false\n@else if " => "Invalid else directive '@else if': expected 'if <expr>'.",
74
+ "a\n !b = 12\nc\n d = !b" => 'Undefined variable: "!b".',
75
+ "=foo\n !b = 12\nc\n +foo\n d = !b" => 'Undefined variable: "!b".',
76
+ '@for !a from 1 to "foo"' => '"foo" is not an integer.',
77
+ '@for !a from 1 to 1.232323' => '1.232 is not an integer.',
78
+ '@if' => "Invalid if directive '@if': expected expression.",
79
+ '@while' => "Invalid while directive '@while': expected expression.",
80
+ '@debug' => "Invalid debug directive '@debug': expected expression.",
81
+
82
+ # Regression tests
83
+ "a\n b:\n c\n d" => ["Illegal nesting: Only attributes may be nested beneath attributes.", 3],
84
+ "& foo\n bar: baz\n blat: bang" => ["Base-level rules cannot contain the parent-selector-referencing character '&'.", 1],
85
+ "a\n b: c\n& foo\n bar: baz\n blat: bang" => ["Base-level rules cannot contain the parent-selector-referencing character '&'.", 3],
86
+ }
87
+
88
+ def test_basic_render
89
+ renders_correctly "basic", { :style => :compact }
90
+ end
91
+
92
+ def test_empty_render
93
+ assert_equal "", render("")
94
+ end
95
+
96
+ def test_multiple_calls_to_render
97
+ sass = Sass::Engine.new("a\n b: c")
98
+ assert_equal sass.render, sass.render
99
+ end
100
+
101
+ def test_alternate_styles
102
+ renders_correctly "expanded", { :style => :expanded }
103
+ renders_correctly "compact", { :style => :compact }
104
+ renders_correctly "nested", { :style => :nested }
105
+ renders_correctly "compressed", { :style => :compressed }
106
+ end
107
+
108
+ def test_flexible_tabulation
109
+ assert_equal("p {\n a: b; }\n p q {\n c: d; }\n",
110
+ render("p\n a: b\n q\n c: d\n"))
111
+ assert_equal("p {\n a: b; }\n p q {\n c: d; }\n",
112
+ render("p\n\ta: b\n\tq\n\t\tc: d\n"))
113
+ end
114
+
115
+ def test_exceptions
116
+ EXCEPTION_MAP.each do |key, value|
117
+ begin
118
+ Sass::Engine.new(key).render
119
+ rescue Sass::SyntaxError => err
120
+ value = [value] unless value.is_a?(Array)
121
+
122
+ assert_equal(value.first, err.message, "Line: #{key}")
123
+ assert_equal(value[1] || key.split("\n").length, err.sass_line, "Line: #{key}")
124
+ assert_match(/\(sass\):[0-9]+/, err.backtrace[0], "Line: #{key}")
125
+ else
126
+ assert(false, "Exception not raised for\n#{key}")
127
+ end
128
+ end
129
+ end
130
+
131
+ def test_exception_line
132
+ to_render = <<SASS
133
+ rule
134
+ :attr val
135
+ // comment!
136
+
137
+ :broken
138
+ SASS
139
+ begin
140
+ Sass::Engine.new(to_render).render
141
+ rescue Sass::SyntaxError => err
142
+ assert_equal(5, err.sass_line)
143
+ else
144
+ assert(false, "Exception not raised for '#{to_render}'!")
145
+ end
146
+ end
147
+
148
+ def test_imported_exception
149
+ [nil, 2].each do |i|
150
+ begin
151
+ Sass::Engine.new("@import bork#{i}", :load_paths => [File.dirname(__FILE__) + '/templates/']).render
152
+ rescue Sass::SyntaxError => err
153
+ assert_equal(2, err.sass_line)
154
+ assert_match(/bork#{i}\.sass$/, err.sass_filename)
155
+ else
156
+ assert(false, "Exception not raised for imported template: bork#{i}")
157
+ end
158
+ end
159
+ end
160
+
161
+ def test_css_import
162
+ assert_equal("@import url(./fonts.css) screen;", render("@import url(./fonts.css) screen"))
163
+ assert_equal("@import \"./fonts.css\" screen;", render("@import \"./fonts.css\" screen"))
164
+ end
165
+
166
+ def test_sass_import
167
+ renders_correctly "import", { :style => :compact, :load_paths => [File.dirname(__FILE__) + "/templates"] }
168
+ end
169
+
170
+ def test_units
171
+ renders_correctly "units"
172
+ end
173
+
174
+ def test_default_function
175
+ assert_equal("foo {\n bar: url(foo.png); }\n", render(%Q{foo\n bar = url("foo.png")\n}));
176
+ assert_equal("foo {\n bar: url(); }\n", render("foo\n bar = url()\n"));
177
+ end
178
+
179
+ def test_string_minus
180
+ assert_equal("foo {\n bar: baz-boom-bat; }\n", render(%Q{foo\n bar = "baz"-"boom"-"bat"}))
181
+ assert_equal("foo {\n bar: -baz-boom; }\n", render(%Q{foo\n bar = -"baz"-"boom"}))
182
+ end
183
+
184
+ def test_string_div
185
+ assert_equal("foo {\n bar: baz/boom/bat; }\n", render(%Q{foo\n bar = "baz"/"boom"/"bat"}))
186
+ assert_equal("foo {\n bar: /baz/boom; }\n", render(%Q{foo\n bar = /"baz"/"boom"}))
187
+ end
188
+
189
+ def test_basic_multiline_selector
190
+ assert_equal("#foo #bar,\n#baz #boom {\n foo: bar; }\n",
191
+ render("#foo #bar,\n#baz #boom\n :foo bar"))
192
+ assert_equal("#foo #bar,\n#foo #baz {\n foo: bar; }\n",
193
+ render("#foo\n #bar,\n #baz\n :foo bar"))
194
+ assert_equal("#foo,\n#bar {\n foo: bar; }\n #foo #baz,\n #bar #baz {\n foo: bar; }\n",
195
+ render("#foo,\n#bar\n :foo bar\n #baz\n :foo bar"))
196
+ assert_equal("#foo #bar, #baz #boom { foo: bar; }\n",
197
+ render("#foo #bar,\n#baz #boom\n :foo bar", :style => :compact))
198
+
199
+ assert_equal("#foo #bar,#baz #boom{foo:bar}\n",
200
+ render("#foo #bar,\n#baz #boom\n :foo bar", :style => :compressed))
201
+ end
202
+
203
+ def test_complex_multiline_selector
204
+ renders_correctly "multiline"
205
+ end
206
+
207
+ def test_colon_only
208
+ begin
209
+ render("a\n b: c", :attribute_syntax => :normal)
210
+ rescue Sass::SyntaxError => e
211
+ assert_equal("Illegal attribute syntax: can't use alternate syntax when :attribute_syntax => :normal is set.",
212
+ e.message)
213
+ else
214
+ assert(false, "SyntaxError not raised for :attribute_syntax => :normal")
215
+ end
216
+
217
+ begin
218
+ render("a\n :b c", :attribute_syntax => :alternate)
219
+ rescue Sass::SyntaxError => e
220
+ assert_equal("Illegal attribute syntax: can't use normal syntax when :attribute_syntax => :alternate is set.",
221
+ e.message)
222
+ else
223
+ assert(false, "SyntaxError not raised for :attribute_syntax => :alternate")
224
+ end
225
+ end
226
+
227
+ def test_pseudo_elements
228
+ assert_equal(<<CSS, render(<<SASS))
229
+ ::first-line {
230
+ size: 10em; }
231
+ CSS
232
+ ::first-line
233
+ size: 10em
234
+ SASS
235
+ end
236
+
237
+ def test_directive
238
+ assert_equal("@a b;", render("@a b"))
239
+
240
+ assert_equal("@a {\n b: c; }\n", render("@a\n :b c"))
241
+ assert_equal("@a { b: c; }\n", render("@a\n :b c", :style => :compact))
242
+ assert_equal("@a {\n b: c;\n}\n", render("@a\n :b c", :style => :expanded))
243
+ assert_equal("@a{b:c}\n", render("@a\n :b c", :style => :compressed))
244
+
245
+ assert_equal("@a {\n b: c;\n d: e; }\n",
246
+ render("@a\n :b c\n :d e"))
247
+ assert_equal("@a { b: c; d: e; }\n",
248
+ render("@a\n :b c\n :d e", :style => :compact))
249
+ assert_equal("@a {\n b: c;\n d: e;\n}\n",
250
+ render("@a\n :b c\n :d e", :style => :expanded))
251
+ assert_equal("@a{b:c;d:e}\n",
252
+ render("@a\n :b c\n :d e", :style => :compressed))
253
+
254
+ assert_equal("@a {\n #b {\n c: d; } }\n",
255
+ render("@a\n #b\n :c d"))
256
+ assert_equal("@a { #b { c: d; } }\n",
257
+ render("@a\n #b\n :c d", :style => :compact))
258
+ assert_equal("@a {\n #b {\n c: d;\n }\n}\n",
259
+ render("@a\n #b\n :c d", :style => :expanded))
260
+ assert_equal("@a{#b{c:d}}\n",
261
+ render("@a\n #b\n :c d", :style => :compressed))
262
+
263
+ assert_equal("@a {\n #b {\n a: b; }\n #b #c {\n d: e; } }\n",
264
+ render("@a\n #b\n :a b\n #c\n :d e"))
265
+ assert_equal("@a { #b { a: b; }\n #b #c { d: e; } }\n",
266
+ render("@a\n #b\n :a b\n #c\n :d e", :style => :compact))
267
+ assert_equal("@a {\n #b {\n a: b;\n }\n #b #c {\n d: e;\n }\n}\n",
268
+ render("@a\n #b\n :a b\n #c\n :d e", :style => :expanded))
269
+ assert_equal("@a{#b{a:b}#b #c{d:e}}\n",
270
+ render("@a\n #b\n :a b\n #c\n :d e", :style => :compressed))
271
+
272
+ assert_equal("@a {\n #foo,\n #bar {\n b: c; } }\n",
273
+ render("@a\n #foo, \n #bar\n :b c"))
274
+ assert_equal("@a { #foo, #bar { b: c; } }\n",
275
+ render("@a\n #foo, \n #bar\n :b c", :style => :compact))
276
+ assert_equal("@a {\n #foo,\n #bar {\n b: c;\n }\n}\n",
277
+ render("@a\n #foo, \n #bar\n :b c", :style => :expanded))
278
+ assert_equal("@a{#foo,#bar{b:c}}\n",
279
+ render("@a\n #foo, \n #bar\n :b c", :style => :compressed))
280
+
281
+ to_render = <<END
282
+ @a
283
+ :b c
284
+ #d
285
+ :e f
286
+ :g h
287
+ END
288
+ rendered = <<END
289
+ @a { b: c;
290
+ #d { e: f; }
291
+ g: h; }
292
+ END
293
+ assert_equal(rendered, render(to_render, :style => :compact))
294
+
295
+ assert_equal("@a{b:c;#d{e:f}g:h}\n", render(to_render, :style => :compressed))
296
+ end
297
+
298
+ def test_line_annotations
299
+ assert_equal(<<CSS, render(<<SASS, :line_comments => true, :style => :compact))
300
+ /* line 2, test_line_annotations_inline.sass */
301
+ foo bar { foo: bar; }
302
+ /* line 5, test_line_annotations_inline.sass */
303
+ foo baz { blip: blop; }
304
+
305
+ /* line 9, test_line_annotations_inline.sass */
306
+ floodle { flop: blop; }
307
+
308
+ /* line 18, test_line_annotations_inline.sass */
309
+ bup { mix: on; }
310
+ /* line 15, test_line_annotations_inline.sass */
311
+ bup mixin { moop: mup; }
312
+
313
+ /* line 22, test_line_annotations_inline.sass */
314
+ bip hop, skip hop { a: b; }
315
+ CSS
316
+ foo
317
+ bar
318
+ foo: bar
319
+
320
+ baz
321
+ blip: blop
322
+
323
+
324
+ floodle
325
+
326
+ flop: blop
327
+
328
+ =mxn
329
+ mix: on
330
+ mixin
331
+ moop: mup
332
+
333
+ bup
334
+ +mxn
335
+
336
+ bip, skip
337
+ hop
338
+ a: b
339
+ SASS
340
+ end
341
+
342
+ def test_line_annotations_with_filename
343
+ renders_correctly "line_numbers", :line_comments => true, :load_paths => [File.dirname(__FILE__) + "/templates"]
344
+ end
345
+
346
+ def test_empty_first_line
347
+ assert_equal("#a {\n b: c; }\n", render("#a\n\n b: c"))
348
+ end
349
+
350
+ def test_escaped_rule
351
+ assert_equal(":focus {\n a: b; }\n", render("\\:focus\n a: b"))
352
+ assert_equal("a {\n b: c; }\n a :focus {\n d: e; }\n", render("\\a\n b: c\n \\:focus\n d: e"))
353
+ end
354
+
355
+ def test_cr_newline
356
+ assert_equal("foo {\n a: b;\n c: d;\n e: f; }\n", render("foo\r a: b\r\n c: d\n\r e: f"))
357
+ end
358
+
359
+ def test_or_eq
360
+ assert_equal("foo {\n a: b; }\n", render(%Q{!foo = "b"\n!foo ||= "c"\nfoo\n a = !foo}))
361
+ assert_equal("foo {\n a: b; }\n", render(%Q{!foo ||= "b"\nfoo\n a = !foo}))
362
+ end
363
+
364
+ def test_mixins
365
+ renders_correctly "mixins", { :style => :expanded }
366
+ end
367
+
368
+ def test_mixins_dont_interfere_with_sibling_combinator
369
+ assert_equal("foo + bar {\n a: b; }\nfoo + baz {\n c: d; }\n",
370
+ render("foo\n +\n bar\n a: b\n baz\n c: d"))
371
+ end
372
+
373
+ def test_mixin_args
374
+ assert_equal("blat {\n baz: hi; }\n", render(<<SASS))
375
+ =foo(!bar)
376
+ baz = !bar
377
+ blat
378
+ +foo(\"hi\")
379
+ SASS
380
+ assert_equal("blat {\n baz: 3; }\n", render(<<SASS))
381
+ =foo(!a, !b)
382
+ baz = !a + !b
383
+ blat
384
+ +foo(1, 2)
385
+ SASS
386
+ assert_equal("blat {\n baz: 4;\n baz: 3;\n baz: 5;\n bang: 3; }\n", render(<<SASS))
387
+ =foo(!c = (6 + 4) / 2)
388
+ baz = !c
389
+ !c = 3
390
+ blat
391
+ +foo(!c + 1)
392
+ +foo((!c + 3)/2)
393
+ +foo
394
+ bang = !c
395
+ SASS
396
+ end
397
+
398
+ def test_default_values_for_mixin_arguments
399
+ assert_equal("white {\n color: white; }\n\nblack {\n color: black; }\n", render(<<SASS))
400
+ =foo(!a = #FFF)
401
+ :color= !a
402
+ white
403
+ +foo
404
+ black
405
+ +foo(#000)
406
+ SASS
407
+ assert_equal(<<CSS, render(<<SASS))
408
+ one {
409
+ color: white;
410
+ padding: 1px;
411
+ margin: 4px; }
412
+
413
+ two {
414
+ color: white;
415
+ padding: 2px;
416
+ margin: 5px; }
417
+
418
+ three {
419
+ color: white;
420
+ padding: 2px;
421
+ margin: 3px; }
422
+ CSS
423
+ !a = 5px
424
+ =foo(!a, !b = 1px, !c = 3px + !b)
425
+ :color= !a
426
+ :padding= !b
427
+ :margin= !c
428
+ one
429
+ +foo(#fff)
430
+ two
431
+ +foo(#fff, 2px)
432
+ three
433
+ +foo(#fff, 2px, 3px)
434
+ SASS
435
+ end
436
+
437
+ def test_interpolation
438
+ assert_equal("a-1 {\n b-2-3: c-3; }\n", render(<<SASS))
439
+ !a = 1
440
+ !b = 2
441
+ !c = 3
442
+ a-\#{!a}
443
+ b-\#{!b}-\#{!c}: c-\#{!a + !b}
444
+ SASS
445
+ end
446
+
447
+ def test_if_directive
448
+ assert_equal("a {\n b: 1; }\n", render(<<SASS))
449
+ !var = true
450
+ a
451
+ @if !var
452
+ b: 1
453
+ @if not !var
454
+ b: 2
455
+ SASS
456
+ end
457
+
458
+ def test_for
459
+ assert_equal(<<CSS, render(<<SASS))
460
+ a-0 {
461
+ 2i: 0; }
462
+
463
+ a-1 {
464
+ 2i: 2; }
465
+
466
+ a-2 {
467
+ 2i: 4; }
468
+
469
+ a-3 {
470
+ 2i: 6; }
471
+
472
+ b-1 {
473
+ j-1: 0; }
474
+
475
+ b-2 {
476
+ j-1: 1; }
477
+
478
+ b-3 {
479
+ j-1: 2; }
480
+
481
+ b-4 {
482
+ j-1: 3; }
483
+ CSS
484
+ !a = 3
485
+ @for !i from 0 to !a + 1
486
+ a-\#{!i}
487
+ 2i = 2 * !i
488
+
489
+ @for !j from 1 through 4
490
+ b-\#{!j}
491
+ j-1 = !j - 1
492
+ SASS
493
+ end
494
+
495
+ def test_while
496
+ assert_equal(<<CSS, render(<<SASS))
497
+ a-5 {
498
+ blooble: gloop; }
499
+
500
+ a-4 {
501
+ blooble: gloop; }
502
+
503
+ a-3 {
504
+ blooble: gloop; }
505
+
506
+ a-2 {
507
+ blooble: gloop; }
508
+
509
+ a-1 {
510
+ blooble: gloop; }
511
+ CSS
512
+ !a = 5
513
+ @while !a != 0
514
+ a-\#{!a}
515
+ blooble: gloop
516
+ !a = !a - 1
517
+ SASS
518
+ end
519
+
520
+ def test_else
521
+ assert_equal(<<CSS, render(<<SASS))
522
+ a {
523
+ t1: t;
524
+ t2: t;
525
+ t3: t;
526
+ t4: t; }
527
+ CSS
528
+ a
529
+ @if true
530
+ t1: t
531
+ @else
532
+ f1: f
533
+
534
+ @if false
535
+ f2: f
536
+ @else
537
+ t2: t
538
+
539
+ @if false
540
+ f3: f1
541
+ @else if 1 + 1 == 3
542
+ f3: f2
543
+ @else
544
+ t3: t
545
+
546
+ @if false
547
+ f4: f1
548
+ @else if 1 + 1 == 2
549
+ t4: t
550
+ @else
551
+ f4: f2
552
+
553
+ @if false
554
+ f5: f1
555
+ @else if false
556
+ f5: f2
557
+ SASS
558
+ end
559
+
560
+ def test_variable_reassignment
561
+ assert_equal(<<CSS, render(<<SASS))
562
+ a {
563
+ b: 1;
564
+ c: 2; }
565
+ CSS
566
+ !a = 1
567
+ a
568
+ b = !a
569
+ !a = 2
570
+ c = !a
571
+ SASS
572
+ end
573
+
574
+ def test_variable_scope
575
+ assert_equal(<<CSS, render(<<SASS))
576
+ a {
577
+ b-1: c;
578
+ b-2: c;
579
+ d: 12; }
580
+
581
+ b {
582
+ d: 17; }
583
+ CSS
584
+ !i = 12
585
+ a
586
+ @for !i from 1 through 2
587
+ b-\#{!i}: c
588
+ d = !i
589
+
590
+ =foo
591
+ !i = 17
592
+
593
+ b
594
+ +foo
595
+ d = !i
596
+ SASS
597
+ end
598
+
599
+ def test_argument_error
600
+ assert_raise(Sass::SyntaxError) { render("a\n b = hsl(1)") }
601
+ end
602
+
603
+ def test_comments_at_the_top_of_a_document
604
+ render(<<SASS)
605
+ //
606
+ This is a comment that
607
+ continues to the second line.
608
+ foo
609
+ bar: baz
610
+ SASS
611
+ end
612
+
613
+ def test_loud_comments_containing_a_comment_close
614
+ actual_css = render(<<SASS)
615
+ /*
616
+ This is a comment that
617
+ continues to the second line. */
618
+ foo
619
+ bar: baz
620
+ SASS
621
+ assert_equal(<<CSS, actual_css)
622
+ /*
623
+ * This is a comment that
624
+ * continues to the second line. */
625
+ foo {
626
+ bar: baz; }
627
+ CSS
628
+ end
629
+
630
+ private
631
+
632
+ def render(sass, options = {})
633
+ munge_filename options
634
+ Sass::Engine.new(sass, options).render
635
+ end
636
+
637
+ def renders_correctly(name, options={})
638
+ sass_file = load_file(name, "sass")
639
+ css_file = load_file(name, "css")
640
+ options[:filename] ||= filename(name, "sass")
641
+ options[:css_filename] ||= filename(name, "css")
642
+ css_result = Sass::Engine.new(sass_file, options).render
643
+ assert_equal css_file, css_result
644
+ end
645
+
646
+ def load_file(name, type = "sass")
647
+ @result = ''
648
+ File.new(filename(name, type)).each_line { |l| @result += l }
649
+ @result
650
+ end
651
+
652
+ def filename(name, type)
653
+ File.dirname(__FILE__) + "/#{type == 'sass' ? 'templates' : 'results'}/#{name}.#{type}"
654
+ end
655
+ end