JezK
Edit File: extend_test.rb
#!/usr/bin/env ruby require File.dirname(__FILE__) + '/../test_helper' class ExtendTest < Test::Unit::TestCase def test_basic assert_equal <<CSS, render(<<SCSS) .foo, .bar { a: b; } CSS .foo {a: b} .bar {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) .foo, .bar { a: b; } CSS .bar {@extend .foo} .foo {a: b} SCSS assert_equal <<CSS, render(<<SCSS) .foo, .bar { a: b; } .bar { c: d; } CSS .foo {a: b} .bar {c: d; @extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) .foo, .bar { a: b; } .bar { c: d; } CSS .foo {a: b} .bar {@extend .foo; c: d} SCSS end def test_indented_syntax assert_equal <<CSS, render(<<SASS, :syntax => :sass) .foo, .bar { a: b; } CSS .foo a: b .bar @extend .foo SASS assert_equal <<CSS, render(<<SASS, :syntax => :sass) .foo, .bar { a: b; } CSS .foo a: b .bar @extend \#{".foo"} SASS end def test_multiple_targets assert_equal <<CSS, render(<<SCSS) .foo, .bar { a: b; } .blip .foo, .blip .bar { c: d; } CSS .foo {a: b} .bar {@extend .foo} .blip .foo {c: d} SCSS end def test_multiple_extendees assert_equal <<CSS, render(<<SCSS) .foo, .baz { a: b; } .bar, .baz { c: d; } CSS .foo {a: b} .bar {c: d} .baz {@extend .foo; @extend .bar} SCSS end def test_multiple_extends_with_single_extender_and_single_target assert_equal <<CSS, render(<<SCSS) .foo .bar, .baz .bar, .foo .baz, .baz .baz { a: b; } CSS .foo .bar {a: b} .baz {@extend .foo; @extend .bar} SCSS assert_equal <<CSS, render(<<SCSS) .foo.bar, .baz { a: b; } CSS .foo.bar {a: b} .baz {@extend .foo; @extend .bar} SCSS end def test_multiple_extends_with_multiple_extenders_and_single_target assert_equal <<CSS, render(<<SCSS) .foo .bar, .baz .bar, .foo .bang, .baz .bang { a: b; } CSS .foo .bar {a: b} .baz {@extend .foo} .bang {@extend .bar} SCSS assert_equal <<CSS, render(<<SCSS) .foo.bar, .bar.baz, .baz.bang, .foo.bang { a: b; } CSS .foo.bar {a: b} .baz {@extend .foo} .bang {@extend .bar} SCSS end def test_chained_extends assert_equal <<CSS, render(<<SCSS) .foo, .bar, .baz, .bip { a: b; } CSS .foo {a: b} .bar {@extend .foo} .baz {@extend .bar} .bip {@extend .bar} SCSS end def test_dynamic_extendee assert_equal <<CSS, render(<<SCSS) .foo, .bar { a: b; } CSS .foo {a: b} .bar {@extend \#{".foo"}} SCSS assert_equal <<CSS, render(<<SCSS) [baz^="blip12px"], .bar { a: b; } CSS [baz^="blip12px"] {a: b} .bar {@extend [baz^="blip\#{12px}"]} SCSS end def test_nested_target assert_equal <<CSS, render(<<SCSS) .foo .bar, .foo .baz { a: b; } CSS .foo .bar {a: b} .baz {@extend .bar} SCSS end def test_target_with_child assert_equal <<CSS, render(<<SCSS) .foo .bar, .baz .bar { a: b; } CSS .foo .bar {a: b} .baz {@extend .foo} SCSS end def test_class_unification assert_equal <<CSS, render(<<SCSS) .foo.bar, .bar.baz { a: b; } CSS .foo.bar {a: b} .baz {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) .baz { a: b; } CSS .foo.baz {a: b} .baz {@extend .foo} SCSS end def test_id_unification assert_equal <<CSS, render(<<SCSS) .foo.bar, .bar#baz { a: b; } CSS .foo.bar {a: b} #baz {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) #baz { a: b; } CSS .foo#baz {a: b} #baz {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) .foo#baz { a: b; } CSS .foo#baz {a: b} #bar {@extend .foo} SCSS end def test_universal_unification_with_simple_target assert_equal <<CSS, render(<<SCSS) .foo, * { a: b; } CSS .foo {a: b} * {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) .foo, *|* { a: b; } CSS .foo {a: b} *|* {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) .bar { a: b; } CSS .foo.bar {a: b} * {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) .bar { a: b; } CSS .foo.bar {a: b} *|* {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) .foo.bar, ns|*.bar { a: b; } CSS .foo.bar {a: b} ns|* {@extend .foo} SCSS end def test_universal_unification_with_namespaceless_universal_target assert_equal <<CSS, render(<<SCSS) * { a: b; } CSS *.foo {a: b} * {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) * { a: b; } CSS *.foo {a: b} *|* {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) *|*.foo, * { a: b; } CSS *|*.foo {a: b} * {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) *|* { a: b; } CSS *|*.foo {a: b} *|* {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) *.foo, ns|* { a: b; } CSS *.foo {a: b} ns|* {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) *|*.foo, ns|* { a: b; } CSS *|*.foo {a: b} ns|* {@extend .foo} SCSS end def test_universal_unification_with_namespaced_universal_target assert_equal <<CSS, render(<<SCSS) ns|* { a: b; } CSS ns|*.foo {a: b} * {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) ns|* { a: b; } CSS ns|*.foo {a: b} *|* {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) ns1|*.foo { a: b; } CSS ns1|*.foo {a: b} ns2|* {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) ns|* { a: b; } CSS ns|*.foo {a: b} ns|* {@extend .foo} SCSS end def test_universal_unification_with_namespaceless_element_target assert_equal <<CSS, render(<<SCSS) a { a: b; } CSS a.foo {a: b} * {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) a { a: b; } CSS a.foo {a: b} *|* {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) *|a.foo, a { a: b; } CSS *|a.foo {a: b} * {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) *|a { a: b; } CSS *|a.foo {a: b} *|* {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) a.foo, ns|a { a: b; } CSS a.foo {a: b} ns|* {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) *|a.foo, ns|a { a: b; } CSS *|a.foo {a: b} ns|* {@extend .foo} SCSS end def test_universal_unification_with_namespaced_element_target assert_equal <<CSS, render(<<SCSS) ns|a { a: b; } CSS ns|a.foo {a: b} * {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) ns|a { a: b; } CSS ns|a.foo {a: b} *|* {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) ns1|a.foo { a: b; } CSS ns1|a.foo {a: b} ns2|* {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) ns|a { a: b; } CSS ns|a.foo {a: b} ns|* {@extend .foo} SCSS end def test_element_unification_with_simple_target assert_equal <<CSS, render(<<SCSS) .foo, a { a: b; } CSS .foo {a: b} a {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) .foo.bar, a.bar { a: b; } CSS .foo.bar {a: b} a {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) .foo.bar, *|a.bar { a: b; } CSS .foo.bar {a: b} *|a {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) .foo.bar, ns|a.bar { a: b; } CSS .foo.bar {a: b} ns|a {@extend .foo} SCSS end def test_element_unification_with_namespaceless_universal_target assert_equal <<CSS, render(<<SCSS) *.foo, a { a: b; } CSS *.foo {a: b} a {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) *.foo, a { a: b; } CSS *.foo {a: b} *|a {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) *|*.foo, a { a: b; } CSS *|*.foo {a: b} a {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) *|*.foo, *|a { a: b; } CSS *|*.foo {a: b} *|a {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) *.foo, ns|a { a: b; } CSS *.foo {a: b} ns|a {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) *|*.foo, ns|a { a: b; } CSS *|*.foo {a: b} ns|a {@extend .foo} SCSS end def test_element_unification_with_namespaced_universal_target assert_equal <<CSS, render(<<SCSS) ns|*.foo, ns|a { a: b; } CSS ns|*.foo {a: b} a {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) ns|*.foo, ns|a { a: b; } CSS ns|*.foo {a: b} *|a {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) ns1|*.foo { a: b; } CSS ns1|*.foo {a: b} ns2|a {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) ns|*.foo, ns|a { a: b; } CSS ns|*.foo {a: b} ns|a {@extend .foo} SCSS end def test_element_unification_with_namespaceless_element_target assert_equal <<CSS, render(<<SCSS) a { a: b; } CSS a.foo {a: b} a {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) a { a: b; } CSS a.foo {a: b} *|a {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) *|a.foo, a { a: b; } CSS *|a.foo {a: b} a {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) *|a { a: b; } CSS *|a.foo {a: b} *|a {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) a.foo, ns|a { a: b; } CSS a.foo {a: b} ns|a {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) *|a.foo, ns|a { a: b; } CSS *|a.foo {a: b} ns|a {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) a.foo { a: b; } CSS a.foo {a: b} h1 {@extend .foo} SCSS end def test_element_unification_with_namespaced_element_target assert_equal <<CSS, render(<<SCSS) ns|a { a: b; } CSS ns|a.foo {a: b} a {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) ns|a { a: b; } CSS ns|a.foo {a: b} *|a {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) ns1|a.foo { a: b; } CSS ns1|a.foo {a: b} ns2|a {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) ns|a { a: b; } CSS ns|a.foo {a: b} ns|a {@extend .foo} SCSS end def test_attribute_unification assert_equal <<CSS, render(<<SCSS) [foo=bar].baz, [foo=bar][foo=baz] { a: b; } CSS [foo=bar].baz {a: b} [foo=baz] {@extend .baz} SCSS assert_equal <<CSS, render(<<SCSS) [foo=bar].baz, [foo=bar][foo^=bar] { a: b; } CSS [foo=bar].baz {a: b} [foo^=bar] {@extend .baz} SCSS assert_equal <<CSS, render(<<SCSS) [foo=bar].baz, [foo=bar][foot=bar] { a: b; } CSS [foo=bar].baz {a: b} [foot=bar] {@extend .baz} SCSS assert_equal <<CSS, render(<<SCSS) [foo=bar].baz, [foo=bar][ns|foo=bar] { a: b; } CSS [foo=bar].baz {a: b} [ns|foo=bar] {@extend .baz} SCSS assert_equal <<CSS, render(<<SCSS) [foo=bar] { a: b; } CSS [foo=bar].baz {a: b} [foo=bar] {@extend .baz} SCSS end def test_pseudo_unification assert_equal <<CSS, render(<<SCSS) :foo.baz, :foo:foo(2n+1) { a: b; } CSS :foo.baz {a: b} :foo(2n+1) {@extend .baz} SCSS assert_equal <<CSS, render(<<SCSS) :foo.baz, :foo::foo { a: b; } CSS :foo.baz {a: b} ::foo {@extend .baz} SCSS assert_equal <<CSS, render(<<SCSS) ::foo.baz { a: b; } CSS ::foo.baz {a: b} ::bar {@extend .baz} SCSS assert_equal <<CSS, render(<<SCSS) ::foo.baz { a: b; } CSS ::foo.baz {a: b} ::foo(2n+1) {@extend .baz} SCSS assert_equal <<CSS, render(<<SCSS) ::foo { a: b; } CSS ::foo.baz {a: b} ::foo {@extend .baz} SCSS assert_equal <<CSS, render(<<SCSS) ::foo(2n+1) { a: b; } CSS ::foo(2n+1).baz {a: b} ::foo(2n+1) {@extend .baz} SCSS assert_equal <<CSS, render(<<SCSS) :foo.baz, :foo:bar { a: b; } CSS :foo.baz {a: b} :bar {@extend .baz} SCSS assert_equal <<CSS, render(<<SCSS) .baz:foo, :foo:after { a: b; } CSS .baz:foo {a: b} :after {@extend .baz} SCSS assert_equal <<CSS, render(<<SCSS) .baz:after, :foo:after { a: b; } CSS .baz:after {a: b} :foo {@extend .baz} SCSS assert_equal <<CSS, render(<<SCSS) :foo { a: b; } CSS :foo.baz {a: b} :foo {@extend .baz} SCSS end def test_pseudoelement_remains_at_end_of_selector assert_equal <<CSS, render(<<SCSS) .foo::bar, .baz::bar { a: b; } CSS .foo::bar {a: b} .baz {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) a.foo::bar, a.baz::bar { a: b; } CSS a.foo::bar {a: b} .baz {@extend .foo} SCSS end def test_pseudoclass_remains_at_end_of_selector assert_equal <<CSS, render(<<SCSS) .foo:bar, .baz:bar { a: b; } CSS .foo:bar {a: b} .baz {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) a.foo:bar, a.baz:bar { a: b; } CSS a.foo:bar {a: b} .baz {@extend .foo} SCSS end def test_not_remains_at_end_of_selector assert_equal <<CSS, render(<<SCSS) .foo:not(.bar), .baz:not(.bar) { a: b; } CSS .foo:not(.bar) {a: b} .baz {@extend .foo} SCSS end def test_pseudoelement_goes_lefter_than_pseudoclass assert_equal <<CSS, render(<<SCSS) .foo::bar, .baz:bang::bar { a: b; } CSS .foo::bar {a: b} .baz:bang {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) .foo:bar, .baz:bar::bang { a: b; } CSS .foo:bar {a: b} .baz::bang {@extend .foo} SCSS end def test_pseudoelement_goes_lefter_than_not assert_equal <<CSS, render(<<SCSS) .foo::bar, .baz:not(.bang)::bar { a: b; } CSS .foo::bar {a: b} .baz:not(.bang) {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) .foo:not(.bang), .baz:not(.bang)::bar { a: b; } CSS .foo:not(.bang) {a: b} .baz::bar {@extend .foo} SCSS end def test_negation_unification assert_equal <<CSS, render(<<SCSS) :not(.foo).baz, :not(.foo):not(.bar) { a: b; } CSS :not(.foo).baz {a: b} :not(.bar) {@extend .baz} SCSS assert_equal <<CSS, render(<<SCSS) :not(.foo) { a: b; } CSS :not(.foo).baz {a: b} :not(.foo) {@extend .baz} SCSS assert_equal <<CSS, render(<<SCSS) :not([a=b]) { a: b; } CSS :not([a=b]).baz {a: b} :not([a = b]) {@extend .baz} SCSS end def test_comma_extendee assert_equal <<CSS, render(<<SCSS) .foo, .baz { a: b; } .bar, .baz { c: d; } CSS .foo {a: b} .bar {c: d} .baz {@extend .foo, .bar} SCSS end ## Long Extendees def test_long_extendee assert_equal <<CSS, render(<<SCSS) .foo.bar, .baz { a: b; } CSS .foo.bar {a: b} .baz {@extend .foo.bar} SCSS end def test_long_extendee_requires_all_selectors assert_equal <<CSS, render(<<SCSS) .foo { a: b; } CSS .foo {a: b} .baz {@extend .foo.bar} SCSS end def test_long_extendee_matches_supersets assert_equal <<CSS, render(<<SCSS) .foo.bar.bap, .bap.baz { a: b; } CSS .foo.bar.bap {a: b} .baz {@extend .foo.bar} SCSS end def test_long_extendee_runs_unification assert_equal <<CSS, render(<<SCSS) ns|*.foo.bar, ns|a.baz { a: b; } CSS ns|*.foo.bar {a: b} a.baz {@extend .foo.bar} SCSS end ## Long Extenders def test_long_extender assert_equal <<CSS, render(<<SCSS) .foo.bar, .bar.baz.bang { a: b; } CSS .foo.bar {a: b} .baz.bang {@extend .foo} SCSS end def test_long_extender_runs_unification assert_equal <<CSS, render(<<SCSS) ns|*.foo.bar, ns|a.bar.baz { a: b; } CSS ns|*.foo.bar {a: b} a.baz {@extend .foo} SCSS end def test_long_extender_aborts_unification assert_equal <<CSS, render(<<SCSS) a.foo#bar { a: b; } CSS a.foo#bar {a: b} h1.baz {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) a.foo#bar { a: b; } CSS a.foo#bar {a: b} .bang#baz {@extend .foo} SCSS end ## Nested Extenders def test_nested_extender assert_equal <<CSS, render(<<SCSS) .foo, foo bar { a: b; } CSS .foo {a: b} foo bar {@extend .foo} SCSS end def test_nested_extender_runs_unification assert_equal <<CSS, render(<<SCSS) .foo.bar, foo bar.bar { a: b; } CSS .foo.bar {a: b} foo bar {@extend .foo} SCSS end def test_nested_extender_aborts_unification assert_equal <<CSS, render(<<SCSS) baz.foo { a: b; } CSS baz.foo {a: b} foo bar {@extend .foo} SCSS end def test_nested_extender_alternates_parents assert_equal <<CSS, render(<<SCSS) .baz .bip .foo, .baz .bip foo .grank bar, foo .grank .baz .bip bar { a: b; } CSS .baz .bip .foo {a: b} foo .grank bar {@extend .foo} SCSS end def test_nested_extender_unifies_identical_parents assert_equal <<CSS, render(<<SCSS) .baz .bip .foo, .baz .bip bar { a: b; } CSS .baz .bip .foo {a: b} .baz .bip bar {@extend .foo} SCSS end def test_nested_extender_unifies_common_substring assert_equal <<CSS, render(<<SCSS) .baz .bip .bap .bink .foo, .baz .brat .bip .bap .bink bar, .brat .baz .bip .bap .bink bar { a: b; } CSS .baz .bip .bap .bink .foo {a: b} .brat .bip .bap bar {@extend .foo} SCSS end def test_nested_extender_unifies_common_subseq assert_equal <<CSS, render(<<SCSS) .a .x .b .y .foo, .a .x .n .b .y .m bar, .a .n .x .b .y .m bar, .a .x .n .b .m .y bar, .a .n .x .b .m .y bar { a: b; } CSS .a .x .b .y .foo {a: b} .a .n .b .m bar {@extend .foo} SCSS end def test_nested_extender_chooses_first_subseq assert_equal <<CSS, render(<<SCSS) .a .b .c .d .foo, .a .b .c .d .a .b .bar { a: b; } CSS .a .b .c .d .foo {a: b} .c .d .a .b .bar {@extend .foo} SCSS end def test_nested_extender_counts_extended_subselectors assert_equal <<CSS, render(<<SCSS) .a .bip.bop .foo, .a .b .bip.bop .bar, .b .a .bip.bop .bar { a: b; } CSS .a .bip.bop .foo {a: b} .b .bip .bar {@extend .foo} SCSS end def test_nested_extender_counts_extended_superselectors assert_equal <<CSS, render(<<SCSS) .a .bip .foo, .a .b .bip.bop .bar, .b .a .bip.bop .bar { a: b; } CSS .a .bip .foo {a: b} .b .bip.bop .bar {@extend .foo} SCSS end def test_nested_extender_with_child_selector assert_equal <<CSS, render(<<SCSS) .baz .foo, .baz foo > bar { a: b; } CSS .baz .foo {a: b} foo > bar {@extend .foo} SCSS end def test_nested_extender_finds_common_selectors_around_child_selector assert_equal <<CSS, render(<<SCSS) a > b c .c1, a > b c .c2 { a: b; } CSS a > b c .c1 {a: b} a c .c2 {@extend .c1} SCSS assert_equal <<CSS, render(<<SCSS) a > b c .c1, a > b c .c2 { a: b; } CSS a > b c .c1 {a: b} b c .c2 {@extend .c1} SCSS end def test_nested_extender_doesnt_find_common_selectors_around_adjacent_sibling_selector assert_equal <<CSS, render(<<SCSS) a + b c .c1, a + b a c .c2, a a + b c .c2 { a: b; } CSS a + b c .c1 {a: b} a c .c2 {@extend .c1} SCSS assert_equal <<CSS, render(<<SCSS) a + b c .c1, a a + b c .c2 { a: b; } CSS a + b c .c1 {a: b} a b .c2 {@extend .c1} SCSS assert_equal <<CSS, render(<<SCSS) a + b c .c1, a + b c .c2 { a: b; } CSS a + b c .c1 {a: b} b c .c2 {@extend .c1} SCSS end def test_nested_extender_doesnt_find_common_selectors_around_sibling_selector assert_equal <<CSS, render(<<SCSS) a ~ b c .c1, a ~ b a c .c2, a a ~ b c .c2 { a: b; } CSS a ~ b c .c1 {a: b} a c .c2 {@extend .c1} SCSS assert_equal <<CSS, render(<<SCSS) a ~ b c .c1, a a ~ b c .c2 { a: b; } CSS a ~ b c .c1 {a: b} a b .c2 {@extend .c1} SCSS assert_equal <<CSS, render(<<SCSS) a ~ b c .c1, a ~ b c .c2 { a: b; } CSS a ~ b c .c1 {a: b} b c .c2 {@extend .c1} SCSS end def test_nested_extender_with_early_child_selectors_doesnt_subseq_them assert_equal <<CSS, render(<<SCSS) .bip > .bap .foo, .bip > .bap .grip > .bap .bar, .grip > .bap .bip > .bap .bar { a: b; } CSS .bip > .bap .foo {a: b} .grip > .bap .bar {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) .bap > .bip .foo, .bap > .bip .bap > .grip .bar, .bap > .grip .bap > .bip .bar { a: b; } CSS .bap > .bip .foo {a: b} .bap > .grip .bar {@extend .foo} SCSS end def test_nested_extender_with_child_selector_unifies assert_equal <<CSS, render(<<SCSS) .baz.foo, foo > bar.baz { a: b; } CSS .baz.foo {a: b} foo > bar {@extend .foo} SCSS end def test_nested_extender_with_trailing_child_selector assert_raise(Sass::SyntaxError, "bar > can't extend: invalid selector") do render("bar > {@extend .baz}") end end def test_nested_extender_with_sibling_selector assert_equal <<CSS, render(<<SCSS) .baz .foo, .baz foo + bar { a: b; } CSS .baz .foo {a: b} foo + bar {@extend .foo} SCSS end def test_nested_extender_with_hacky_selector assert_equal <<CSS, render(<<SCSS) .baz .foo, .baz foo + > > + bar { a: b; } CSS .baz .foo {a: b} foo + > > + bar {@extend .foo} SCSS assert_equal <<CSS, render(<<SCSS) .baz .foo, .baz > > bar { a: b; } CSS .baz .foo {a: b} > > bar {@extend .foo} SCSS end def test_nested_extender_merges_with_same_selector assert_equal <<CSS, render(<<SCSS) .foo .bar, .foo .baz { a: b; } CSS .foo { .bar {a: b} .baz {@extend .bar} } SCSS end def test_nested_extender_with_child_selector_merges_with_same_selector assert_equal <<CSS, render(<<SCSS) .foo > .bar .baz, .foo > .bar .bang { a: b; } CSS .foo > .bar .baz {a: b} .foo > .bar .bang {@extend .baz} SCSS end # Loops def test_extend_self_loop assert_equal <<CSS, render(<<SCSS) .foo { a: b; } CSS .foo {a: b; @extend .foo} SCSS end def test_basic_extend_loop assert_equal <<CSS, render(<<SCSS) .bar, .foo { a: b; } .foo, .bar { c: d; } CSS .foo {a: b; @extend .bar} .bar {c: d; @extend .foo} SCSS end def test_three_level_extend_loop assert_equal <<CSS, render(<<SCSS) .baz, .bar, .foo { a: b; } .foo, .baz, .bar { c: d; } .bar, .foo, .baz { e: f; } CSS .foo {a: b; @extend .bar} .bar {c: d; @extend .baz} .baz {e: f; @extend .foo} SCSS end def test_nested_extend_loop assert_equal <<CSS, render(<<SCSS) .bar, .bar .foo { a: b; } .bar .foo, .bar .foo .foo { c: d; } CSS .bar { a: b; .foo {c: d; @extend .bar} } SCSS end def test_multiple_extender_merges_with_superset_selector assert_equal <<CSS, render(<<SCSS) a.bar.baz, a.foo { a: b; } CSS .foo {@extend .bar; @extend .baz} a.bar.baz {a: b} SCSS end def test_control_flow_if assert_equal <<CSS, render(<<SCSS) .true, .also-true { color: green; } .false, .also-false { color: red; } CSS .true { color: green; } .false { color: red; } .also-true { @if true { @extend .true; } @else { @extend .false; } } .also-false { @if false { @extend .true; } @else { @extend .false; } } SCSS end def test_control_flow_for assert_equal <<CSS, render(<<SCSS) .base-0, .added { color: green; } .base-1, .added { display: block; } .base-2, .added { border: 1px solid blue; } CSS .base-0 { color: green; } .base-1 { display: block; } .base-2 { border: 1px solid blue; } .added { @for $i from 0 to 3 { @extend .base-\#{$i}; } } SCSS end def test_control_flow_while assert_equal <<CSS, render(<<SCSS) .base-0, .added { color: green; } .base-1, .added { display: block; } .base-2, .added { border: 1px solid blue; } CSS .base-0 { color: green; } .base-1 { display: block; } .base-2 { border: 1px solid blue; } .added { $i : 0; @while $i < 3 { @extend .base-\#{$i}; $i : $i + 1; } } SCSS end def test_extend_out_of_media assert_warning(<<WARN) {assert_equal(<<CSS, render(<<SCSS))} DEPRECATION WARNING on line 3 of test_extend_out_of_media_inline.sass: @extending an outer selector from within @media is deprecated. You may only @extend selectors within the same directive. This will be an error in Sass 3.3. It can only work once @extend is supported natively in the browser. WARN .foo { a: b; } CSS .foo {a: b} @media screen { .bar {@extend .foo} } SCSS end def test_extend_out_of_unknown_directive assert_warning(<<WARN) {assert_equal(<<CSS, render(<<SCSS))} DEPRECATION WARNING on line 3 of test_extend_out_of_unknown_directive_inline.sass: @extending an outer selector from within @flooblehoof is deprecated. You may only @extend selectors within the same directive. This will be an error in Sass 3.3. It can only work once @extend is supported natively in the browser. WARN .foo { a: b; } @flooblehoof {} CSS .foo {a: b} @flooblehoof { .bar {@extend .foo} } SCSS end def test_extend_out_of_nested_directives assert_warning(<<WARN) {assert_equal(<<CSS, render(<<SCSS))} DEPRECATION WARNING on line 4 of test_extend_out_of_nested_directives_inline.sass: @extending an outer selector from within @flooblehoof is deprecated. You may only @extend selectors within the same directive. This will be an error in Sass 3.3. It can only work once @extend is supported natively in the browser. WARN @media screen { .foo { a: b; } @flooblehoof {} } CSS @media screen { .foo {a: b} @flooblehoof { .bar {@extend .foo} } } SCSS end def test_extend_within_media assert_equal(<<CSS, render(<<SCSS)) @media screen { .foo, .bar { a: b; } } CSS @media screen { .foo {a: b} .bar {@extend .foo} } SCSS end def test_extend_within_unknown_directive assert_equal(<<CSS, render(<<SCSS)) @flooblehoof { .foo, .bar { a: b; } } CSS @flooblehoof { .foo {a: b} .bar {@extend .foo} } SCSS end def test_extend_within_nested_directives assert_equal(<<CSS, render(<<SCSS)) @media screen { @flooblehoof { .foo, .bar { a: b; } } } CSS @media screen { @flooblehoof { .foo {a: b} .bar {@extend .foo} } } SCSS end def test_extend_within_disparate_media assert_equal(<<CSS, render(<<SCSS)) @media screen { .foo, .bar { a: b; } } CSS @media screen {.foo {a: b}} @media screen {.bar {@extend .foo}} SCSS end def test_extend_within_disparate_unknown_directive assert_equal(<<CSS, render(<<SCSS)) @flooblehoof { .foo, .bar { a: b; } } @flooblehoof {} CSS @flooblehoof {.foo {a: b}} @flooblehoof {.bar {@extend .foo}} SCSS end def test_extend_within_disparate_nested_directives assert_equal(<<CSS, render(<<SCSS)) @media screen { @flooblehoof { .foo, .bar { a: b; } } } @media screen { @flooblehoof {} } CSS @media screen {@flooblehoof {.foo {a: b}}} @media screen {@flooblehoof {.bar {@extend .foo}}} SCSS end def test_extend_within_and_without_media assert_warning(<<WARN) {assert_equal(<<CSS, render(<<SCSS))} DEPRECATION WARNING on line 4 of test_extend_within_and_without_media_inline.sass: @extending an outer selector from within @media is deprecated. You may only @extend selectors within the same directive. This will be an error in Sass 3.3. It can only work once @extend is supported natively in the browser. WARN .foo { a: b; } @media screen { .foo, .bar { c: d; } } CSS .foo {a: b} @media screen { .foo {c: d} .bar {@extend .foo} } SCSS end def test_extend_within_and_without_unknown_directive assert_warning(<<WARN) {assert_equal(<<CSS, render(<<SCSS))} DEPRECATION WARNING on line 4 of test_extend_within_and_without_unknown_directive_inline.sass: @extending an outer selector from within @flooblehoof is deprecated. You may only @extend selectors within the same directive. This will be an error in Sass 3.3. It can only work once @extend is supported natively in the browser. WARN .foo { a: b; } @flooblehoof { .foo, .bar { c: d; } } CSS .foo {a: b} @flooblehoof { .foo {c: d} .bar {@extend .foo} } SCSS end def test_extend_within_and_without_nested_directives assert_warning(<<WARN) {assert_equal(<<CSS, render(<<SCSS))} DEPRECATION WARNING on line 5 of test_extend_within_and_without_nested_directives_inline.sass: @extending an outer selector from within @flooblehoof is deprecated. You may only @extend selectors within the same directive. This will be an error in Sass 3.3. It can only work once @extend is supported natively in the browser. WARN @media screen { .foo { a: b; } @flooblehoof { .foo, .bar { c: d; } } } CSS @media screen { .foo {a: b} @flooblehoof { .foo {c: d} .bar {@extend .foo} } } SCSS end # Regression Tests def test_duplicated_selector_with_newlines assert_equal(<<CSS, render(<<SCSS)) .example-1-1, .example-1-2, .my-page-1 .my-module-1-1, .example-1-3 { a: b; } CSS .example-1-1, .example-1-2, .example-1-3 { a: b; } .my-page-1 .my-module-1-1 {@extend .example-1-2} SCSS end def test_nested_selector_with_child_selector_hack_extendee assert_equal <<CSS, render(<<SCSS) > .foo, > foo bar { a: b; } CSS > .foo {a: b} foo bar {@extend .foo} SCSS end def test_nested_selector_with_child_selector_hack_extender assert_equal <<CSS, render(<<SCSS) .foo .bar, > .foo foo bar, > foo .foo bar { a: b; } CSS .foo .bar {a: b} > foo bar {@extend .bar} SCSS end def test_nested_selector_with_child_selector_hack_extender_and_extendee assert_equal <<CSS, render(<<SCSS) > .foo, > foo bar { a: b; } CSS > .foo {a: b} > foo bar {@extend .foo} SCSS end def test_nested_selector_with_child_selector_hack_extender_and_sibling_selector_extendee assert_equal <<CSS, render(<<SCSS) ~ .foo { a: b; } CSS ~ .foo {a: b} > foo bar {@extend .foo} SCSS end def test_nested_selector_with_child_selector_hack_extender_and_extendee_and_newline assert_equal <<CSS, render(<<SCSS) > .foo, > flip, > foo bar { a: b; } CSS > .foo {a: b} flip, > foo bar {@extend .foo} SCSS end private def render(sass, options = {}) munge_filename options Sass::Engine.new(sass, {:syntax => :scss}.merge(options)).render end end