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