| require 'minitest/autorun' |
| require File.dirname(__FILE__) + '/../test_helper' |
| require 'sass/css' |
| |
| class CSS2SassTest < MiniTest::Test |
| def test_basic |
| css = <<CSS |
| h1 { |
| color: red; |
| } |
| CSS |
| assert_equal(<<SASS, css2sass(css)) |
| h1 |
| color: red |
| SASS |
| silence_warnings {assert_equal(<<SASS, css2sass(css, :old => true))} |
| h1 |
| :color red |
| SASS |
| end |
| |
| def test_nesting |
| assert_equal(<<SASS, css2sass(<<CSS)) |
| li |
| display: none |
| |
| a |
| text-decoration: none |
| |
| span |
| color: yellow |
| |
| &:hover |
| text-decoration: underline |
| SASS |
| li { |
| display: none; |
| } |
| |
| li a { |
| text-decoration: none; |
| } |
| |
| li a span { |
| color: yellow; |
| } |
| |
| li a:hover { |
| text-decoration: underline; |
| } |
| CSS |
| end |
| |
| def test_no_nesting_around_rules |
| assert_equal(<<SASS, css2sass(<<CSS)) |
| div .warning |
| color: #d21a19 |
| |
| span .debug |
| cursor: crosshair |
| |
| div .debug |
| cursor: default |
| SASS |
| div .warning { |
| color: #d21a19; } |
| span .debug { |
| cursor: crosshair;} |
| div .debug { |
| cursor: default; } |
| CSS |
| end |
| |
| def test_comments_multiline |
| css = <<CSS |
| /* comment */ |
| elephant.rawr { |
| rampages: excessively; |
| } |
| |
| /* actual multiline |
| comment */ |
| span.turkey { |
| isdinner: true; |
| } |
| |
| .turducken { |
| /* Sounds funny |
| doesn't it */ |
| chimera: not_really; |
| } |
| |
| #overhere { |
| bored: sorta; /* it's for a good |
| cause */ |
| better_than: thread_pools; |
| } |
| |
| #one_more { |
| finally: srsly; |
| } /* just a line here */ |
| CSS |
| sass = <<SASS |
| /* comment |
| |
| elephant.rawr |
| rampages: excessively |
| |
| /* actual multiline |
| *comment |
| |
| span.turkey |
| isdinner: true |
| |
| .turducken |
| /* Sounds funny |
| * doesn't it |
| chimera: not_really |
| |
| #overhere |
| bored: sorta |
| |
| /* it's for a good |
| * cause |
| better_than: thread_pools |
| |
| #one_more |
| finally: srsly |
| |
| /* just a line here |
| SASS |
| assert_equal(sass, css2sass(css)) |
| end |
| |
| def test_fold_commas |
| assert_equal(<<SASS, css2sass(<<CSS)) |
| li |
| .one, .two |
| color: red |
| SASS |
| li .one { |
| color: red; |
| } |
| li .two { |
| color: red; |
| } |
| CSS |
| |
| assert_equal(<<SASS, css2sass(<<CSS)) |
| .one |
| color: green |
| |
| .two |
| color: green |
| color: red |
| |
| .three |
| color: red |
| SASS |
| .one, .two { |
| color: green; |
| } |
| |
| .two, .three { |
| color: red; |
| } |
| CSS |
| end |
| |
| def test_bad_formatting |
| assert_equal(<<SASS, css2sass(<<CSS)) |
| hello |
| parent: true |
| |
| there |
| parent: false |
| |
| who |
| hoo: false |
| |
| why |
| y: true |
| |
| when |
| wen: nao |
| |
| down_here |
| yeah: true |
| SASS |
| hello { |
| parent: true; |
| } |
| |
| hello there { |
| parent: false; |
| } |
| hello who { |
| hoo: false; |
| } |
| hello why { |
| y: true; |
| } |
| hello when { |
| wen: nao; |
| } |
| |
| |
| |
| down_here { yeah: true; } |
| CSS |
| end |
| |
| def test_comments_in_selectors |
| assert_equal(<<SASS, css2sass(<<CSS)) |
| .js |
| #location-navigation-form .form-submit, #business-listing-form .form-submit, #detailTabs ul, #detailsEnhanced #addTags, #locationSearchList, #moreHoods |
| display: none |
| |
| #navListLeft |
| display: none |
| SASS |
| .js #location-navigation-form .form-submit, |
| .js #business-listing-form .form-submit, |
| .js #detailTabs ul, |
| /* .js #addReview, */ |
| /* .js #addTags, */ |
| .js #detailsEnhanced #addTags, |
| .js #locationSearchList, |
| .js #moreHoods, |
| #navListLeft |
| { display: none; } |
| CSS |
| end |
| |
| def test_pseudo_classes_are_escaped |
| assert_equal(<<SASS, css2sass(<<CSS)) |
| \\:focus |
| a: b |
| |
| \\:foo |
| bar: baz |
| SASS |
| :focus {a: b;} |
| :focus :foo {bar: baz;} |
| CSS |
| end |
| |
| def test_subject |
| silence_warnings {assert_equal(<<SASS, css2sass(<<CSS))} |
| .foo |
| .bar! |
| .baz |
| a: b |
| |
| .bip |
| c: d |
| |
| .bar .bonk |
| e: f |
| |
| .flip! |
| &.bar |
| a: b |
| |
| &.baz |
| c: d |
| SASS |
| .foo .bar! .baz {a: b;} |
| .foo .bar! .bip {c: d;} |
| .foo .bar .bonk {e: f;} |
| |
| .flip.bar! {a: b;} |
| .flip.baz! {c: d;} |
| CSS |
| end |
| |
| def test_keyframes |
| assert_equal(<<SASS, css2sass(<<CSS)) |
| @keyframes dash |
| from |
| stroke-dasharray: 1,200 |
| stroke-dashoffset: 0 |
| |
| 50% |
| stroke-dasharray: 89,200 |
| stroke-dashoffset: -35 |
| |
| to |
| stroke-dasharray: 89,200 |
| stroke-dashoffset: -124 |
| SASS |
| @keyframes dash { |
| from { |
| stroke-dasharray: 1,200; |
| stroke-dashoffset: 0; |
| } |
| 50% { |
| stroke-dasharray: 89,200; |
| stroke-dashoffset: -35; |
| } |
| to { |
| stroke-dasharray: 89,200; |
| stroke-dashoffset: -124; |
| } |
| } |
| CSS |
| end |
| |
| # Regressions |
| |
| def test_nesting_with_matching_property |
| assert_equal(<<SASS, css2sass(<<CSS)) |
| ul |
| width: 10px |
| |
| div |
| width: 20px |
| |
| article |
| width: 10px |
| |
| p |
| width: 20px |
| SASS |
| ul {width: 10px} |
| ul div {width: 20px} |
| article {width: 10px} |
| article p {width: 20px} |
| CSS |
| end |
| |
| def test_empty_rule |
| assert_equal(<<SASS, css2sass(<<CSS)) |
| a |
| SASS |
| a {} |
| CSS |
| end |
| |
| def test_empty_rule_with_selector_combinator |
| assert_equal(<<SASS, css2sass(<<CSS)) |
| a |
| color: red |
| |
| > b |
| SASS |
| a {color: red} |
| a > b {} |
| CSS |
| end |
| |
| def test_nesting_within_media |
| assert_equal(<<SASS, css2sass(<<CSS)) |
| @media all |
| .next .vevent |
| padding-left: 0 |
| padding-right: 0 |
| SASS |
| @media all { |
| .next .vevent { |
| padding-left: 0; |
| padding-right: 0; } } |
| CSS |
| end |
| |
| def test_multiline_selector_within_media_and_with_child_selector |
| assert_equal(<<SASS, css2sass(<<CSS)) |
| @media all |
| foo bar, baz |
| padding-left: 0 |
| padding-right: 0 |
| SASS |
| @media all { |
| foo bar, |
| baz { |
| padding-left: 0; |
| padding-right: 0; } } |
| CSS |
| end |
| |
| def test_double_comma |
| assert_equal(<<SASS, css2sass(<<CSS)) |
| foo, bar |
| a: b |
| SASS |
| foo, , bar { a: b } |
| CSS |
| end |
| |
| def test_selector_splitting |
| assert_equal(<<SASS, css2sass(<<CSS)) |
| .foo > |
| .bar |
| a: b |
| |
| .baz |
| c: d |
| SASS |
| .foo>.bar {a: b} |
| .foo>.baz {c: d} |
| CSS |
| |
| assert_equal(<<SASS, css2sass(<<CSS)) |
| .foo |
| &::bar |
| a: b |
| |
| &::baz |
| c: d |
| SASS |
| .foo::bar {a: b} |
| .foo::baz {c: d} |
| CSS |
| end |
| |
| def test_triple_nesting |
| assert_equal(<<SASS, css2sass(<<CSS)) |
| .foo .bar .baz |
| a: b |
| SASS |
| .foo .bar .baz {a: b} |
| CSS |
| |
| assert_equal(<<SASS, css2sass(<<CSS)) |
| .bar > .baz |
| c: d |
| SASS |
| .bar > .baz {c: d} |
| CSS |
| end |
| |
| # Error reporting |
| |
| def test_error_reporting |
| css2sass("foo") |
| assert(false, "Expected exception") |
| rescue Sass::SyntaxError => err |
| assert_equal(1, err.sass_line) |
| assert_equal('Invalid CSS after "foo": expected "{", was ""', err.message) |
| end |
| |
| def test_error_reporting_in_line |
| css2sass("foo\nbar }\nbaz") |
| assert(false, "Expected exception") |
| rescue Sass::SyntaxError => err |
| assert_equal(2, err.sass_line) |
| assert_equal('Invalid CSS after "bar ": expected "{", was "}"', err.message) |
| end |
| |
| def test_error_truncate_after |
| css2sass("#{"a" * 16}foo") |
| assert(false, "Expected exception") |
| rescue Sass::SyntaxError => err |
| assert_equal(1, err.sass_line) |
| assert_equal('Invalid CSS after "...aaaaaaaaaaaafoo": expected "{", was ""', err.message) |
| end |
| |
| def test_error_truncate_was |
| css2sass("foo }foo#{"a" * 15}") |
| assert(false, "Expected exception") |
| rescue Sass::SyntaxError => err |
| assert_equal(1, err.sass_line) |
| assert_equal('Invalid CSS after "foo ": expected "{", was "}fooaaaaaaaaaaa..."', err.message) |
| end |
| |
| def test_error_doesnt_truncate_after_when_elipsis_would_add_length |
| css2sass("#{"a" * 15}foo") |
| assert(false, "Expected exception") |
| rescue Sass::SyntaxError => err |
| assert_equal(1, err.sass_line) |
| assert_equal('Invalid CSS after "aaaaaaaaaaaaaaafoo": expected "{", was ""', err.message) |
| end |
| |
| def test_error_doesnt_truncate_was_when_elipsis_would_add_length |
| css2sass("foo }foo#{"a" * 14}") |
| assert(false, "Expected exception") |
| rescue Sass::SyntaxError => err |
| assert_equal(1, err.sass_line) |
| assert_equal('Invalid CSS after "foo ": expected "{", was "}fooaaaaaaaaaaaaaa"', err.message) |
| end |
| |
| def test_error_gets_rid_of_trailing_newline_for_after |
| css2sass("foo \n ") |
| assert(false, "Expected exception") |
| rescue Sass::SyntaxError => err |
| assert_equal(2, err.sass_line) |
| assert_equal('Invalid CSS after "foo": expected "{", was ""', err.message) |
| end |
| |
| def test_error_gets_rid_of_trailing_newline_for_was |
| css2sass("foo \n }foo") |
| assert(false, "Expected exception") |
| rescue Sass::SyntaxError => err |
| assert_equal(2, err.sass_line) |
| assert_equal('Invalid CSS after "foo": expected "{", was "}foo"', err.message) |
| end |
| |
| # Encodings |
| |
| def test_encoding_error |
| css2sass("foo\nbar\nb\xFEaz".force_encoding("utf-8")) |
| assert(false, "Expected exception") |
| rescue Sass::SyntaxError => e |
| assert_equal(3, e.sass_line) |
| assert_equal('Invalid UTF-8 character "\xFE"', e.message) |
| end |
| |
| def test_ascii_incompatible_encoding_error |
| template = "foo\nbar\nb_z".encode("utf-16le") |
| template[9] = "\xFE".force_encoding("utf-16le") |
| css2sass(template) |
| assert(false, "Expected exception") |
| rescue Sass::SyntaxError => e |
| assert_equal(3, e.sass_line) |
| assert_equal('Invalid UTF-16LE character "\xFE"', e.message) |
| end |
| |
| private |
| |
| def css2sass(string, opts={}) |
| Sass::CSS.new(string, opts).render |
| end |
| end |