Changesets can be listed by changeset number.
The Git repository is here.
- Revision:
- 373
- Log:
Initial import of Radiant 0.9.1, which is now packaged as a gem. This is an
import of the tagged 0.9.1 source checked out from GitHub, which isn't quite
the same as the gem distribution - but it doesn't seem to be available in an
archived form and the installed gem already has modifications, so this is
the closest I can get.
- Author:
- rool
- Date:
- Mon Mar 21 13:40:05 +0000 2011
- Size:
- 25922 Bytes
- Properties:
- Property svn:executable is set to *
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 property: ":".', |
19 | ": a" => 'Invalid property: ": a".', |
20 | ":= a" => 'Invalid property: ":= a".', |
21 | "a\n :b" => <<MSG, |
22 | Invalid property: ":b" (no value). |
23 | If ":b" should be a selector, use "\\:b" instead. |
24 | MSG |
25 | "a\n b:" => 'Invalid property: "b:" (no value).', |
26 | "a\n :b: c" => 'Invalid property: ":b: c".', |
27 | "a\n :b:c d" => 'Invalid property: ":b:c d".', |
28 | "a\n :b=c d" => 'Invalid property: ":b=c d".', |
29 | "a\n :b c;" => 'Invalid property: ":b c;" (no ";" required at end-of-line).', |
30 | "a\n b: c;" => 'Invalid property: "b: c;" (no ";" required at end-of-line).', |
31 | "a\n b : c" => 'Invalid property: "b : c".', |
32 | "a\n b=c: d" => 'Invalid property: "b=c: d".', |
33 | ":a b" => 'Properties aren\'t allowed at the root of a document.', |
34 | "a:" => 'Properties aren\'t allowed at the root of a document.', |
35 | ":a" => <<MSG, |
36 | Properties aren't allowed at the root of a document. |
37 | If ":a" should be a selector, use "\\:a" instead. |
38 | MSG |
39 | "!" => 'Invalid variable: "!".', |
40 | "!a" => 'Invalid variable: "!a".', |
41 | "! a" => 'Invalid variable: "! a".', |
42 | "!a b" => 'Invalid variable: "!a b".', |
43 | "!a = 1b + 2c" => "Incompatible units: 'c' and 'b'.", |
44 | "!a = 1b < 2c" => "Incompatible units: 'c' and 'b'.", |
45 | "!a = 1b > 2c" => "Incompatible units: 'c' and 'b'.", |
46 | "!a = 1b <= 2c" => "Incompatible units: 'c' and 'b'.", |
47 | "!a = 1b >= 2c" => "Incompatible units: 'c' and 'b'.", |
48 | "a\n :b= 1b * 2c" => "2b*c isn't a valid CSS value.", |
49 | "a\n :b= 1b % 2c" => "Cannot modulo by a number with units: 2c.", |
50 | "!a = 2px + #ccc" => "Cannot add a number with units (2px) to a color (#cccccc).", |
51 | "!a = #ccc + 2px" => "Cannot add a number with units (2px) to a color (#cccccc).", |
52 | "& a\n :b c" => ["Base-level rules cannot contain the parent-selector-referencing character '&'.", 1], |
53 | "a\n :b\n c" => "Illegal nesting: Only properties may be nested beneath properties.", |
54 | "a,\n :b c" => ["Rules can\'t end in commas.", 1], |
55 | "a," => "Rules can\'t end in commas.", |
56 | "a,\n!b = 1" => ["Rules can\'t end in commas.", 1], |
57 | "!a = b\n :c d\n" => "Illegal nesting: Nothing may be nested beneath variable declarations.", |
58 | "@import foo.sass" => <<MSG, |
59 | File to import not found or unreadable: foo.sass. |
60 | Load paths: |
61 | #{File.dirname(__FILE__)} |
62 | . |
63 | MSG |
64 | "@import templates/basic\n foo" => "Illegal nesting: Nothing may be nested beneath import directives.", |
65 | "foo\n @import templates/basic" => "Import directives may only be used at the root of a document.", |
66 | "foo\n @import #{File.dirname(__FILE__)}/templates/basic" => "Import directives may only be used at the root of a document.", |
67 | %Q{!foo = "bar" "baz" !} => %Q{Syntax error in '"bar" "baz" !' at character 20.}, |
68 | "=foo\n :color red\n.bar\n +bang" => "Undefined mixin 'bang'.", |
69 | ".bar\n =foo\n :color red\n" => ["Mixins may only be defined at the root of a document.", 2], |
70 | "=foo\n :color red\n.bar\n +foo\n :color red" => "Illegal nesting: Nothing may be nested beneath mixin directives.", |
71 | " a\n b: c" => ["Indenting at the beginning of the document is illegal.", 1], |
72 | " \n \n\t\n a\n b: c" => ["Indenting at the beginning of the document is illegal.", 4], |
73 | "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], |
74 | "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], |
75 | "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], |
76 | "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], |
77 | "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], |
78 | "a\n b: c\na\n d: e" => ["The line was indented 2 levels deeper than the previous line.", 4], |
79 | "a\n b: c\n a\n d: e" => ["The line was indented 3 levels deeper than the previous line.", 4], |
80 | "a\n \tb: c" => ["Indentation can't use both tabs and spaces.", 2], |
81 | "=a(" => 'Expected rparen token, was end of text.', |
82 | "=a(b)" => 'Expected rparen token, was ident token.', |
83 | "=a(,)" => "Expected rparen token, was comma token.", |
84 | "=a(!)" => "Syntax error in '(!)' at character 4.", |
85 | "=a(!foo bar)" => "Expected rparen token, was ident token.", |
86 | "=foo\n bar: baz\n+foo" => ["Properties aren't allowed at the root of a document.", 2], |
87 | "a-\#{!b\n c: d" => ["Expected end_interpolation token, was end of text.", 1], |
88 | "=a(!b = 1, !c)" => "Required argument !c must come before any optional arguments.", |
89 | "=a(!b = 1)\n :a= !b\ndiv\n +a(1,2)" => "Mixin a takes 1 argument but 2 were passed.", |
90 | "=a(!b)\n :a= !b\ndiv\n +a" => "Mixin a is missing parameter !b.", |
91 | "@else\n a\n b: c" => ["@else must come after @if.", 1], |
92 | "@if false\n@else foo" => "Invalid else directive '@else foo': expected 'if <expr>'.", |
93 | "@if false\n@else if " => "Invalid else directive '@else if': expected 'if <expr>'.", |
94 | "a\n !b = 12\nc\n d = !b" => 'Undefined variable: "!b".', |
95 | "=foo\n !b = 12\nc\n +foo\n d = !b" => 'Undefined variable: "!b".', |
96 | '@for !a from "foo" to 1' => '"foo" is not an integer.', |
97 | '@for !a from 1 to "2"' => '"2" is not an integer.', |
98 | '@for !a from 1 to "foo"' => '"foo" is not an integer.', |
99 | '@for !a from 1 to 1.232323' => '1.232 is not an integer.', |
100 | '@for !a from 1px to 3em' => "Incompatible units: 'em' and 'px'.", |
101 | '@if' => "Invalid if directive '@if': expected expression.", |
102 | '@while' => "Invalid while directive '@while': expected expression.", |
103 | '@debug' => "Invalid debug directive '@debug': expected expression.", |
104 | "/* foo\n bar\n baz" => "Inconsistent indentation: previous line was indented by 4 spaces, but this line was indented by 2 spaces.", |
105 | |
106 | # Regression tests |
107 | "a\n b:\n c\n d" => ["Illegal nesting: Only properties may be nested beneath properties.", 3], |
108 | "& foo\n bar: baz\n blat: bang" => ["Base-level rules cannot contain the parent-selector-referencing character '&'.", 1], |
109 | "a\n b: c\n& foo\n bar: baz\n blat: bang" => ["Base-level rules cannot contain the parent-selector-referencing character '&'.", 3], |
110 | } |
111 | |
112 | def teardown |
113 | clean_up_sassc |
114 | end |
115 | |
116 | def test_basic_render |
117 | renders_correctly "basic", { :style => :compact } |
118 | end |
119 | |
120 | def test_empty_render |
121 | assert_equal "", render("") |
122 | end |
123 | |
124 | def test_multiple_calls_to_render |
125 | sass = Sass::Engine.new("a\n b: c") |
126 | assert_equal sass.render, sass.render |
127 | end |
128 | |
129 | def test_alternate_styles |
130 | renders_correctly "expanded", { :style => :expanded } |
131 | renders_correctly "compact", { :style => :compact } |
132 | renders_correctly "nested", { :style => :nested } |
133 | renders_correctly "compressed", { :style => :compressed } |
134 | end |
135 | |
136 | def test_flexible_tabulation |
137 | assert_equal("p {\n a: b; }\n p q {\n c: d; }\n", |
138 | render("p\n a: b\n q\n c: d\n")) |
139 | assert_equal("p {\n a: b; }\n p q {\n c: d; }\n", |
140 | render("p\n\ta: b\n\tq\n\t\tc: d\n")) |
141 | end |
142 | |
143 | EXCEPTION_MAP.each do |key, value| |
144 | define_method("test_exception (#{key.inspect})") do |
145 | line = 10 |
146 | begin |
147 | silence_warnings {Sass::Engine.new(key, :filename => __FILE__, :line => line).render} |
148 | rescue Sass::SyntaxError => err |
149 | value = [value] unless value.is_a?(Array) |
150 | |
151 | assert_equal(value.first.rstrip, err.message, "Line: #{key}") |
152 | assert_equal(__FILE__, err.sass_filename) |
153 | assert_equal((value[1] || key.split("\n").length) + line - 1, err.sass_line, "Line: #{key}") |
154 | assert_match(/#{Regexp.escape(__FILE__)}:[0-9]+/, err.backtrace[0], "Line: #{key}") |
155 | else |
156 | assert(false, "Exception not raised for\n#{key}") |
157 | end |
158 | end |
159 | end |
160 | |
161 | def test_exception_line |
162 | to_render = <<SASS |
163 | rule |
164 | :prop val |
165 | // comment! |
166 | |
167 | :broken |
168 | SASS |
169 | begin |
170 | Sass::Engine.new(to_render).render |
171 | rescue Sass::SyntaxError => err |
172 | assert_equal(5, err.sass_line) |
173 | else |
174 | assert(false, "Exception not raised for '#{to_render}'!") |
175 | end |
176 | end |
177 | |
178 | def test_exception_location |
179 | to_render = <<SASS |
180 | rule |
181 | :prop val |
182 | // comment! |
183 | |
184 | :broken |
185 | SASS |
186 | begin |
187 | Sass::Engine.new(to_render, :filename => __FILE__, :line => (__LINE__-7)).render |
188 | rescue Sass::SyntaxError => err |
189 | assert_equal(__FILE__, err.sass_filename) |
190 | assert_equal((__LINE__-6), err.sass_line) |
191 | else |
192 | assert(false, "Exception not raised for '#{to_render}'!") |
193 | end |
194 | end |
195 | |
196 | def test_imported_exception |
197 | [nil, 2].each do |i| |
198 | begin |
199 | Sass::Engine.new("@import bork#{i}", :load_paths => [File.dirname(__FILE__) + '/templates/']).render |
200 | rescue Sass::SyntaxError => err |
201 | assert_equal(2, err.sass_line) |
202 | assert_match(/bork#{i}\.sass$/, err.sass_filename) |
203 | else |
204 | assert(false, "Exception not raised for imported template: bork#{i}") |
205 | end |
206 | end |
207 | end |
208 | |
209 | def test_css_import |
210 | assert_equal("@import url(./fonts.css) screen;\n", render("@import url(./fonts.css) screen")) |
211 | assert_equal("@import \"./fonts.css\" screen;\n", render("@import \"./fonts.css\" screen")) |
212 | end |
213 | |
214 | def test_sass_import |
215 | assert !File.exists?(sassc_path("importee")) |
216 | renders_correctly "import", { :style => :compact, :load_paths => [File.dirname(__FILE__) + "/templates"] } |
217 | assert File.exists?(sassc_path("importee")) |
218 | end |
219 | |
220 | def test_nonexistent_extensionless_import |
221 | assert_warning(<<WARN) do |
222 | WARNING: nonexistent.sass not found. Using nonexistent.css instead. |
223 | This behavior is deprecated and will be removed in a future version. |
224 | If you really need nonexistent.css, import it explicitly. |
225 | WARN |
226 | assert_equal("@import url(nonexistent.css);\n", render("@import nonexistent")) |
227 | end |
228 | end |
229 | |
230 | def test_no_cache |
231 | assert !File.exists?(sassc_path("importee")) |
232 | renders_correctly("import", { |
233 | :style => :compact, :cache => false, |
234 | :load_paths => [File.dirname(__FILE__) + "/templates"], |
235 | }) |
236 | assert !File.exists?(sassc_path("importee")) |
237 | end |
238 | |
239 | def test_units |
240 | renders_correctly "units" |
241 | end |
242 | |
243 | def test_default_function |
244 | assert_equal("foo {\n bar: url(foo.png); }\n", render(%Q{foo\n bar = url("foo.png")\n})); |
245 | assert_equal("foo {\n bar: url(); }\n", render("foo\n bar = url()\n")); |
246 | end |
247 | |
248 | def test_string_minus |
249 | assert_equal("foo {\n bar: baz-boom-bat; }\n", render(%Q{foo\n bar = "baz"-"boom"-"bat"})) |
250 | assert_equal("foo {\n bar: -baz-boom; }\n", render(%Q{foo\n bar = -"baz"-"boom"})) |
251 | end |
252 | |
253 | def test_string_div |
254 | assert_equal("foo {\n bar: baz/boom/bat; }\n", render(%Q{foo\n bar = "baz"/"boom"/"bat"})) |
255 | assert_equal("foo {\n bar: /baz/boom; }\n", render(%Q{foo\n bar = /"baz"/"boom"})) |
256 | end |
257 | |
258 | def test_basic_multiline_selector |
259 | assert_equal("#foo #bar,\n#baz #boom {\n foo: bar; }\n", |
260 | render("#foo #bar,\n#baz #boom\n :foo bar")) |
261 | assert_equal("#foo #bar,\n#foo #baz {\n foo: bar; }\n", |
262 | render("#foo\n #bar,\n #baz\n :foo bar")) |
263 | assert_equal("#foo,\n#bar {\n foo: bar; }\n #foo #baz,\n #bar #baz {\n foo: bar; }\n", |
264 | render("#foo,\n#bar\n :foo bar\n #baz\n :foo bar")) |
265 | assert_equal("#foo #bar, #baz #boom { foo: bar; }\n", |
266 | render("#foo #bar,\n#baz #boom\n :foo bar", :style => :compact)) |
267 | |
268 | assert_equal("#foo #bar,#baz #boom{foo:bar}\n", |
269 | render("#foo #bar,\n#baz #boom\n :foo bar", :style => :compressed)) |
270 | end |
271 | |
272 | def test_complex_multiline_selector |
273 | renders_correctly "multiline" |
274 | end |
275 | |
276 | def test_colon_only |
277 | begin |
278 | render("a\n b: c", :property_syntax => :old) |
279 | rescue Sass::SyntaxError => e |
280 | assert_equal("Illegal property syntax: can't use new syntax when :property_syntax => :old is set.", |
281 | e.message) |
282 | assert_equal(2, e.sass_line) |
283 | else |
284 | assert(false, "SyntaxError not raised for :property_syntax => :old") |
285 | end |
286 | |
287 | begin |
288 | render("a\n :b c", :property_syntax => :new) |
289 | assert_equal(2, e.sass_line) |
290 | rescue Sass::SyntaxError => e |
291 | assert_equal("Illegal property syntax: can't use old syntax when :property_syntax => :new is set.", |
292 | e.message) |
293 | else |
294 | assert(false, "SyntaxError not raised for :property_syntax => :new") |
295 | end |
296 | end |
297 | |
298 | def test_pseudo_elements |
299 | assert_equal(<<CSS, render(<<SASS)) |
300 | ::first-line { |
301 | size: 10em; } |
302 | CSS |
303 | ::first-line |
304 | size: 10em |
305 | SASS |
306 | end |
307 | |
308 | def test_directive |
309 | assert_equal("@a b;\n", render("@a b")) |
310 | |
311 | assert_equal("@a {\n b: c; }\n", render("@a\n :b c")) |
312 | assert_equal("@a { b: c; }\n", render("@a\n :b c", :style => :compact)) |
313 | assert_equal("@a {\n b: c;\n}\n", render("@a\n :b c", :style => :expanded)) |
314 | assert_equal("@a{b:c}\n", render("@a\n :b c", :style => :compressed)) |
315 | |
316 | assert_equal("@a {\n b: c;\n d: e; }\n", |
317 | render("@a\n :b c\n :d e")) |
318 | assert_equal("@a { b: c; d: e; }\n", |
319 | render("@a\n :b c\n :d e", :style => :compact)) |
320 | assert_equal("@a {\n b: c;\n d: e;\n}\n", |
321 | render("@a\n :b c\n :d e", :style => :expanded)) |
322 | assert_equal("@a{b:c;d:e}\n", |
323 | render("@a\n :b c\n :d e", :style => :compressed)) |
324 | |
325 | assert_equal("@a {\n #b {\n c: d; } }\n", |
326 | render("@a\n #b\n :c d")) |
327 | assert_equal("@a { #b { c: d; } }\n", |
328 | render("@a\n #b\n :c d", :style => :compact)) |
329 | assert_equal("@a {\n #b {\n c: d;\n }\n}\n", |
330 | render("@a\n #b\n :c d", :style => :expanded)) |
331 | assert_equal("@a{#b{c:d}}\n", |
332 | render("@a\n #b\n :c d", :style => :compressed)) |
333 | |
334 | assert_equal("@a {\n #b {\n a: b; }\n #b #c {\n d: e; } }\n", |
335 | render("@a\n #b\n :a b\n #c\n :d e")) |
336 | assert_equal("@a { #b { a: b; }\n #b #c { d: e; } }\n", |
337 | render("@a\n #b\n :a b\n #c\n :d e", :style => :compact)) |
338 | assert_equal("@a {\n #b {\n a: b;\n }\n #b #c {\n d: e;\n }\n}\n", |
339 | render("@a\n #b\n :a b\n #c\n :d e", :style => :expanded)) |
340 | assert_equal("@a{#b{a:b}#b #c{d:e}}\n", |
341 | render("@a\n #b\n :a b\n #c\n :d e", :style => :compressed)) |
342 | |
343 | assert_equal("@a {\n #foo,\n #bar {\n b: c; } }\n", |
344 | render("@a\n #foo, \n #bar\n :b c")) |
345 | assert_equal("@a { #foo, #bar { b: c; } }\n", |
346 | render("@a\n #foo, \n #bar\n :b c", :style => :compact)) |
347 | assert_equal("@a {\n #foo,\n #bar {\n b: c;\n }\n}\n", |
348 | render("@a\n #foo, \n #bar\n :b c", :style => :expanded)) |
349 | assert_equal("@a{#foo,#bar{b:c}}\n", |
350 | render("@a\n #foo, \n #bar\n :b c", :style => :compressed)) |
351 | |
352 | to_render = <<END |
353 | @a |
354 | :b c |
355 | #d |
356 | :e f |
357 | :g h |
358 | END |
359 | rendered = <<END |
360 | @a { b: c; |
361 | #d { e: f; } |
362 | g: h; } |
363 | END |
364 | assert_equal(rendered, render(to_render, :style => :compact)) |
365 | |
366 | assert_equal("@a{b:c;#d{e:f}g:h}\n", render(to_render, :style => :compressed)) |
367 | end |
368 | |
369 | def test_line_annotations |
370 | assert_equal(<<CSS, render(<<SASS, :line_comments => true, :style => :compact)) |
371 | /* line 2, test_line_annotations_inline.sass */ |
372 | foo bar { foo: bar; } |
373 | /* line 5, test_line_annotations_inline.sass */ |
374 | foo baz { blip: blop; } |
375 | |
376 | /* line 9, test_line_annotations_inline.sass */ |
377 | floodle { flop: blop; } |
378 | |
379 | /* line 18, test_line_annotations_inline.sass */ |
380 | bup { mix: on; } |
381 | /* line 15, test_line_annotations_inline.sass */ |
382 | bup mixin { moop: mup; } |
383 | |
384 | /* line 22, test_line_annotations_inline.sass */ |
385 | bip hop, skip hop { a: b; } |
386 | CSS |
387 | foo |
388 | bar |
389 | foo: bar |
390 | |
391 | baz |
392 | blip: blop |
393 | |
394 | |
395 | floodle |
396 | |
397 | flop: blop |
398 | |
399 | =mxn |
400 | mix: on |
401 | mixin |
402 | moop: mup |
403 | |
404 | bup |
405 | +mxn |
406 | |
407 | bip, skip |
408 | hop |
409 | a: b |
410 | SASS |
411 | end |
412 | |
413 | def test_line_annotations_with_filename |
414 | renders_correctly "line_numbers", :line_comments => true, :load_paths => [File.dirname(__FILE__) + "/templates"] |
415 | end |
416 | |
417 | def test_empty_first_line |
418 | assert_equal("#a {\n b: c; }\n", render("#a\n\n b: c")) |
419 | end |
420 | |
421 | def test_escaped_rule |
422 | assert_equal(":focus {\n a: b; }\n", render("\\:focus\n a: b")) |
423 | assert_equal("a {\n b: c; }\n a :focus {\n d: e; }\n", render("\\a\n b: c\n \\:focus\n d: e")) |
424 | end |
425 | |
426 | def test_cr_newline |
427 | 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")) |
428 | end |
429 | |
430 | def test_or_eq |
431 | assert_equal("foo {\n a: b; }\n", render(%Q{!foo = "b"\n!foo ||= "c"\nfoo\n a = !foo})) |
432 | assert_equal("foo {\n a: b; }\n", render(%Q{!foo ||= "b"\nfoo\n a = !foo})) |
433 | end |
434 | |
435 | def test_mixins |
436 | renders_correctly "mixins", { :style => :expanded } |
437 | end |
438 | |
439 | def test_mixins_dont_interfere_with_sibling_combinator |
440 | assert_equal("foo + bar {\n a: b; }\nfoo + baz {\n c: d; }\n", |
441 | render("foo\n +\n bar\n a: b\n baz\n c: d")) |
442 | end |
443 | |
444 | def test_mixin_args |
445 | assert_equal("blat {\n baz: hi; }\n", render(<<SASS)) |
446 | =foo(!bar) |
447 | baz = !bar |
448 | blat |
449 | +foo(\"hi\") |
450 | SASS |
451 | assert_equal("blat {\n baz: 3; }\n", render(<<SASS)) |
452 | =foo(!a, !b) |
453 | baz = !a + !b |
454 | blat |
455 | +foo(1, 2) |
456 | SASS |
457 | assert_equal("blat {\n baz: 4;\n baz: 3;\n baz: 5;\n bang: 3; }\n", render(<<SASS)) |
458 | =foo(!c = (6 + 4) / 2) |
459 | baz = !c |
460 | !c = 3 |
461 | blat |
462 | +foo(!c + 1) |
463 | +foo((!c + 3)/2) |
464 | +foo |
465 | bang = !c |
466 | SASS |
467 | end |
468 | |
469 | def test_default_values_for_mixin_arguments |
470 | assert_equal("white {\n color: white; }\n\nblack {\n color: black; }\n", render(<<SASS)) |
471 | =foo(!a = #FFF) |
472 | :color= !a |
473 | white |
474 | +foo |
475 | black |
476 | +foo(#000) |
477 | SASS |
478 | assert_equal(<<CSS, render(<<SASS)) |
479 | one { |
480 | color: white; |
481 | padding: 1px; |
482 | margin: 4px; } |
483 | |
484 | two { |
485 | color: white; |
486 | padding: 2px; |
487 | margin: 5px; } |
488 | |
489 | three { |
490 | color: white; |
491 | padding: 2px; |
492 | margin: 3px; } |
493 | CSS |
494 | !a = 5px |
495 | =foo(!a, !b = 1px, !c = 3px + !b) |
496 | :color= !a |
497 | :padding= !b |
498 | :margin= !c |
499 | one |
500 | +foo(#fff) |
501 | two |
502 | +foo(#fff, 2px) |
503 | three |
504 | +foo(#fff, 2px, 3px) |
505 | SASS |
506 | end |
507 | |
508 | def test_interpolation |
509 | assert_equal("a-1 {\n b-2-3: c-3; }\n", render(<<SASS)) |
510 | !a = 1 |
511 | !b = 2 |
512 | !c = 3 |
513 | a-\#{!a} |
514 | b-\#{!b}-\#{!c}: c-\#{!a + !b} |
515 | SASS |
516 | end |
517 | |
518 | def test_if_directive |
519 | assert_equal("a {\n b: 1; }\n", render(<<SASS)) |
520 | !var = true |
521 | a |
522 | @if !var |
523 | b: 1 |
524 | @if not !var |
525 | b: 2 |
526 | SASS |
527 | end |
528 | |
529 | def test_for |
530 | assert_equal(<<CSS, render(<<SASS)) |
531 | a-0 { |
532 | 2i: 0; } |
533 | |
534 | a-1 { |
535 | 2i: 2; } |
536 | |
537 | a-2 { |
538 | 2i: 4; } |
539 | |
540 | a-3 { |
541 | 2i: 6; } |
542 | |
543 | b-1 { |
544 | j-1: 0; } |
545 | |
546 | b-2 { |
547 | j-1: 1; } |
548 | |
549 | b-3 { |
550 | j-1: 2; } |
551 | |
552 | b-4 { |
553 | j-1: 3; } |
554 | CSS |
555 | !a = 3 |
556 | @for !i from 0 to !a + 1 |
557 | a-\#{!i} |
558 | 2i = 2 * !i |
559 | |
560 | @for !j from 1 through 4 |
561 | b-\#{!j} |
562 | j-1 = !j - 1 |
563 | SASS |
564 | end |
565 | |
566 | def test_while |
567 | assert_equal(<<CSS, render(<<SASS)) |
568 | a-5 { |
569 | blooble: gloop; } |
570 | |
571 | a-4 { |
572 | blooble: gloop; } |
573 | |
574 | a-3 { |
575 | blooble: gloop; } |
576 | |
577 | a-2 { |
578 | blooble: gloop; } |
579 | |
580 | a-1 { |
581 | blooble: gloop; } |
582 | CSS |
583 | !a = 5 |
584 | @while !a != 0 |
585 | a-\#{!a} |
586 | blooble: gloop |
587 | !a = !a - 1 |
588 | SASS |
589 | end |
590 | |
591 | def test_else |
592 | assert_equal(<<CSS, render(<<SASS)) |
593 | a { |
594 | t1: t; |
595 | t2: t; |
596 | t3: t; |
597 | t4: t; } |
598 | CSS |
599 | a |
600 | @if true |
601 | t1: t |
602 | @else |
603 | f1: f |
604 | |
605 | @if false |
606 | f2: f |
607 | @else |
608 | t2: t |
609 | |
610 | @if false |
611 | f3: f1 |
612 | @else if 1 + 1 == 3 |
613 | f3: f2 |
614 | @else |
615 | t3: t |
616 | |
617 | @if false |
618 | f4: f1 |
619 | @else if 1 + 1 == 2 |
620 | t4: t |
621 | @else |
622 | f4: f2 |
623 | |
624 | @if false |
625 | f5: f1 |
626 | @else if false |
627 | f5: f2 |
628 | SASS |
629 | end |
630 | |
631 | def test_variable_reassignment |
632 | assert_equal(<<CSS, render(<<SASS)) |
633 | a { |
634 | b: 1; |
635 | c: 2; } |
636 | CSS |
637 | !a = 1 |
638 | a |
639 | b = !a |
640 | !a = 2 |
641 | c = !a |
642 | SASS |
643 | end |
644 | |
645 | def test_variable_scope |
646 | assert_equal(<<CSS, render(<<SASS)) |
647 | a { |
648 | b-1: c; |
649 | b-2: c; |
650 | d: 12; } |
651 | |
652 | b { |
653 | d: 17; } |
654 | CSS |
655 | !i = 12 |
656 | a |
657 | @for !i from 1 through 2 |
658 | b-\#{!i}: c |
659 | d = !i |
660 | |
661 | =foo |
662 | !i = 17 |
663 | |
664 | b |
665 | +foo |
666 | d = !i |
667 | SASS |
668 | end |
669 | |
670 | def test_argument_error |
671 | assert_raise(Sass::SyntaxError) { render("a\n b = hsl(1)") } |
672 | end |
673 | |
674 | def test_comments_at_the_top_of_a_document |
675 | render(<<SASS) |
676 | // |
677 | This is a comment that |
678 | continues to the second line. |
679 | foo |
680 | bar: baz |
681 | SASS |
682 | end |
683 | |
684 | def test_loud_comments_containing_a_comment_close |
685 | actual_css = render(<<SASS) |
686 | /* |
687 | This is a comment that |
688 | continues to the second line. */ |
689 | foo |
690 | bar: baz |
691 | SASS |
692 | assert_equal(<<CSS, actual_css) |
693 | /* This is a comment that |
694 | * continues to the second line. */ |
695 | foo { |
696 | bar: baz; } |
697 | CSS |
698 | end |
699 | |
700 | def test_comment_indentation_at_beginning_of_doc |
701 | assert_equal <<CSS, render(<<SASS) |
702 | /* foo |
703 | * bar |
704 | * baz */ |
705 | foo { |
706 | a: b; } |
707 | CSS |
708 | /* foo |
709 | bar |
710 | baz |
711 | foo |
712 | a: b |
713 | SASS |
714 | end |
715 | |
716 | def test_unusual_comment_indentation |
717 | assert_equal <<CSS, render(<<SASS) |
718 | foo { |
719 | /* foo |
720 | * bar |
721 | * baz */ } |
722 | CSS |
723 | foo |
724 | /* foo |
725 | bar |
726 | baz |
727 | SASS |
728 | end |
729 | |
730 | def test_attribute_selector_with_spaces |
731 | assert_equal(<<CSS, render(<<SASS)) |
732 | a b[foo = bar] { |
733 | c: d; } |
734 | CSS |
735 | a |
736 | b[foo = bar] |
737 | c: d |
738 | SASS |
739 | end |
740 | |
741 | def test_quoted_colon |
742 | assert_equal(<<CSS, render(<<SASS)) |
743 | a b[foo="bar: baz"] { |
744 | c: d; } |
745 | CSS |
746 | a |
747 | b[foo="bar: baz"] |
748 | c: d |
749 | SASS |
750 | end |
751 | |
752 | def test_quoted_comma |
753 | assert_equal(<<CSS, render(<<SASS)) |
754 | a b[foo="bar, baz"] { |
755 | c: d; } |
756 | CSS |
757 | a |
758 | b[foo="bar, baz"] |
759 | c: d |
760 | SASS |
761 | end |
762 | |
763 | def test_quoted_ampersand |
764 | assert_equal(<<CSS, render(<<SASS)) |
765 | a b[foo="bar & baz"] { |
766 | c: d; } |
767 | CSS |
768 | a |
769 | b[foo="bar & baz"] |
770 | c: d |
771 | SASS |
772 | end |
773 | |
774 | def test_empty_selector_warning |
775 | assert_warning(<<END) {render("foo bar")} |
776 | WARNING on line 1 of test_empty_selector_warning_inline.sass: |
777 | Selector "foo bar" doesn't have any properties and will not be rendered. |
778 | END |
779 | |
780 | assert_warning(<<END) {render(<<SASS)} |
781 | WARNING on line 3 of test_empty_selector_warning_inline.sass: |
782 | Selector |
783 | foo, bar, baz, |
784 | bang, bip, bop |
785 | doesn't have any properties and will not be rendered. |
786 | END |
787 | |
788 | |
789 | foo, bar, baz, |
790 | bang, bip, bop |
791 | SASS |
792 | |
793 | assert_warning(<<END) {render("foo bar", :filename => nil)} |
794 | WARNING on line 1: |
795 | Selector "foo bar" doesn't have any properties and will not be rendered. |
796 | END |
797 | end |
798 | |
799 | def test_root_level_pseudo_class_with_new_properties |
800 | assert_equal(<<CSS, render(<<SASS, :property_syntax => :new)) |
801 | :focus { |
802 | outline: 0; } |
803 | CSS |
804 | :focus |
805 | outline: 0 |
806 | SASS |
807 | end |
808 | |
809 | def test_pseudo_class_with_new_properties |
810 | assert_equal(<<CSS, render(<<SASS, :property_syntax => :new)) |
811 | p :focus { |
812 | outline: 0; } |
813 | CSS |
814 | p |
815 | :focus |
816 | outline: 0 |
817 | SASS |
818 | end |
819 | |
820 | def test_nil_option |
821 | assert_equal(<<CSS, render(<<SASS, :format => nil)) |
822 | foo { |
823 | a: b; } |
824 | CSS |
825 | foo |
826 | a: b |
827 | SASS |
828 | end |
829 | |
830 | # Regression tests |
831 | |
832 | def test_parens_in_mixins |
833 | assert_equal(<<CSS, render(<<SASS)) |
834 | .foo { |
835 | color: #01ff7f; |
836 | background-color: #000102; } |
837 | CSS |
838 | =foo(!c1, !c2 = rgb(0, 1, 2)) |
839 | color = !c1 |
840 | background-color = !c2 |
841 | |
842 | .foo |
843 | +foo(rgb(1,255,127)) |
844 | SASS |
845 | end |
846 | |
847 | def test_comment_beneath_prop |
848 | assert_equal(<<RESULT, render(<<SOURCE)) |
849 | .box { |
850 | border-style: solid; } |
851 | RESULT |
852 | .box |
853 | :border |
854 | //:color black |
855 | :style solid |
856 | SOURCE |
857 | |
858 | assert_equal(<<RESULT, render(<<SOURCE)) |
859 | .box { |
860 | /* :color black */ |
861 | border-style: solid; } |
862 | RESULT |
863 | .box |
864 | :border |
865 | /*:color black |
866 | :style solid |
867 | SOURCE |
868 | |
869 | assert_equal(<<RESULT, render(<<SOURCE, :style => :compressed)) |
870 | .box{border-style:solid} |
871 | RESULT |
872 | .box |
873 | :border |
874 | /*:color black |
875 | :style solid |
876 | SOURCE |
877 | end |
878 | |
879 | def test_compressed_comment_beneath_directive |
880 | assert_equal(<<RESULT, render(<<SOURCE, :style => :compressed)) |
881 | @foo{a:b} |
882 | RESULT |
883 | @foo |
884 | a: b |
885 | /*b: c |
886 | SOURCE |
887 | end |
888 | |
889 | def test_comment_with_crazy_indentation |
890 | assert_equal(<<CSS, render(<<SASS)) |
891 | /* This is a loud comment: |
892 | * Where the indentation is wonky. */ |
893 | .comment { |
894 | width: 1px; } |
895 | CSS |
896 | /* |
897 | This is a loud comment: |
898 | Where the indentation is wonky. |
899 | // |
900 | This is a silent comment: |
901 | Where the indentation is wonky. |
902 | .comment |
903 | width: 1px |
904 | SASS |
905 | end |
906 | |
907 | def test_plus_with_space |
908 | assert_equal(<<CSS, render(<<SASS)) |
909 | a + b { |
910 | color: green; } |
911 | CSS |
912 | a |
913 | + b |
914 | color: green |
915 | SASS |
916 | end |
917 | |
918 | def test_empty_line_comment |
919 | assert_equal(<<CSS, render(<<SASS)) |
920 | /* Foo |
921 | * |
922 | * Bar */ |
923 | CSS |
924 | /* |
925 | Foo |
926 | |
927 | Bar |
928 | SASS |
929 | end |
930 | |
931 | def test_empty_comment |
932 | assert_equal(<<CSS, render(<<SASS)) |
933 | /* */ |
934 | a { |
935 | /* */ |
936 | b: c; } |
937 | CSS |
938 | /* |
939 | a |
940 | /* |
941 | b: c |
942 | SASS |
943 | end |
944 | |
945 | private |
946 | |
947 | def render(sass, options = {}) |
948 | munge_filename options |
949 | Sass::Engine.new(sass, options).render |
950 | end |
951 | |
952 | def renders_correctly(name, options={}) |
953 | sass_file = load_file(name, "sass") |
954 | css_file = load_file(name, "css") |
955 | options[:filename] ||= filename(name, "sass") |
956 | options[:css_filename] ||= filename(name, "css") |
957 | css_result = Sass::Engine.new(sass_file, options).render |
958 | assert_equal css_file, css_result |
959 | end |
960 | |
961 | def load_file(name, type = "sass") |
962 | @result = '' |
963 | File.new(filename(name, type)).each_line { |l| @result += l } |
964 | @result |
965 | end |
966 | |
967 | def filename(name, type) |
968 | File.dirname(__FILE__) + "/#{type == 'sass' ? 'templates' : 'results'}/#{name}.#{type}" |
969 | end |
970 | |
971 | def sassc_path(template) |
972 | sassc_path = File.join(File.dirname(__FILE__) + "/templates/#{template}.sass") |
973 | Sass::Files.send(:sassc_filename, sassc_path, Sass::Engine::DEFAULT_OPTIONS) |
974 | end |
975 | end |