grass/tests/extend.rs
Connor Skees ffaee04613
rewrite parsing, evaluation, and serialization (#67)
Adds support for the indented syntax, plain CSS imports, `@forward`, and many other previously missing features.
2022-12-26 15:33:04 -05:00

1972 lines
45 KiB
Rust

#[macro_use]
mod macros;
test!(empty_extend_self, "a { @extend a; }", "");
test!(
extend_self_with_styles,
"a {\n color: red;\n @extend a;\n}\n",
"a {\n color: red;\n}\n"
);
test!(
list_extends_both_of_compound,
".foo.bar {
a: b
}
.x, .y {
@extend .foo, .bar;
}
",
".foo.bar, .x, .y {\n a: b;\n}\n"
);
test!(
class_extends_class_placed_second,
".foo {a: b}
.bar {@extend .foo}",
".foo, .bar {\n a: b;\n}\n"
);
test!(
class_extends_class_placed_first,
".bar {@extend .foo}
.foo {a: b}",
".foo, .bar {\n a: b;\n}\n"
);
test!(
class_extends_class_style_before_extend,
".foo {a: b}
.bar {c: d; @extend .foo;}",
".foo, .bar {\n a: b;\n}\n\n.bar {\n c: d;\n}\n"
);
test!(
class_extends_class_style_after_extend,
".foo {a: b}
.bar {@extend .foo; c: d;}",
".foo, .bar {\n a: b;\n}\n\n.bar {\n c: d;\n}\n"
);
test!(
class_extends_class_applies_to_multiple_extendees,
".foo {a: b}
.bar {@extend .foo}
.blip .foo {c: d}",
".foo, .bar {\n a: b;\n}\n\n.blip .foo, .blip .bar {\n c: d;\n}\n"
);
test!(
class_extends_class_one_class_extends_multiple,
".foo {a: b}
.bar {c: d}
.baz {@extend .foo; @extend .bar}",
".foo, .baz {\n a: b;\n}\n\n.bar, .baz {\n c: d;\n}\n"
);
test!(
class_extends_class_multiple_classes_extend_one,
".foo {a: b}
.bar {@extend .foo}
.baz {@extend .bar}
.bip {@extend .bar}
",
".foo, .bar, .bip, .baz {\n a: b;\n}\n"
);
test!(
class_extends_class_all_parts_of_complex_selector_extended_by_one,
".foo .bar {a: b}
.baz {@extend .foo; @extend .bar}
",
".foo .bar, .foo .baz, .baz .bar, .baz .baz {\n a: b;\n}\n"
);
test!(
class_extends_class_all_parts_of_compound_selector_extended_by_one,
".foo.bar {a: b}
.baz {@extend .foo; @extend .bar}
",
".foo.bar, .baz {\n a: b;\n}\n"
);
test!(
class_extends_class_all_parts_of_complex_selector_extended_by_different,
".foo .bar {a: b}
.baz {@extend .foo}
.bang {@extend .bar}
",
".foo .bar, .foo .bang, .baz .bar, .baz .bang {\n a: b;\n}\n"
);
test!(
class_extends_class_all_parts_of_compound_selector_extended_by_different,
".foo.bar {a: b}
.baz {@extend .foo}
.bang {@extend .bar}
",
".foo.bar, .foo.bang, .bar.baz, .baz.bang {\n a: b;\n}\n"
);
test!(
class_extends_class_simple_selector_extended_chain,
".foo {a: b}
.bar {@extend .foo}
.baz {@extend .bar}
.bip {@extend .bar}
",
".foo, .bar, .bip, .baz {\n a: b;\n}\n"
);
test!(
class_extends_class_interpolated,
".foo {a: b}
.bar {@extend #{\".foo\"}}
",
".foo, .bar {\n a: b;\n}\n"
);
test!(
class_extends_class_target_child_of_complex,
".foo .bar {a: b}
.baz {@extend .bar}
",
".foo .bar, .foo .baz {\n a: b;\n}\n"
);
test!(
class_extends_class_target_parent_of_complex,
".foo .bar {a: b}
.baz {@extend .foo}
",
".foo .bar, .baz .bar {\n a: b;\n}\n"
);
test!(
class_unification_1,
"%-a .foo.bar {a: b}
.baz {@extend .foo} -a {@extend %-a}
",
"-a .foo.bar, -a .bar.baz {\n a: b;\n}\n"
);
test!(
class_unification_2,
"%-a .foo.baz {a: b}
.baz {@extend .foo} -a {@extend %-a}
",
"-a .baz {\n a: b;\n}\n"
);
test!(
id_unification_1,
"%-a .foo.bar {a: b}
#baz {@extend .foo} -a {@extend %-a}
",
"-a .foo.bar, -a .bar#baz {\n a: b;\n}\n"
);
test!(
id_unification_2,
"%-a .foo#baz {a: b}
#baz {@extend .foo} -a {@extend %-a}
",
"-a #baz {\n a: b;\n}\n"
);
test!(
universal_unification_simple_target_1,
"%-a .foo {a: b}
* {@extend .foo} -a {@extend %-a}
",
"-a .foo, -a * {\n a: b;\n}\n"
);
test!(
universal_unification_simple_target_2,
"%-a .foo.bar {a: b}
* {@extend .foo} -a {@extend %-a}
",
"-a .bar {\n a: b;\n}\n"
);
test!(
universal_unification_simple_target_3,
"%-a .foo.bar {a: b}
*|* {@extend .foo} -a {@extend %-a}
",
"-a .bar {\n a: b;\n}\n"
);
test!(
universal_unification_simple_target_4,
"%-a .foo.bar {a: b}
ns|* {@extend .foo} -a {@extend %-a}
",
"-a .foo.bar, -a ns|*.bar {\n a: b;\n}\n"
);
test!(
universal_unification_universal_target_without_namespace_1,
"%-a *.foo {a: b}
* {@extend .foo} -a {@extend %-a}
",
"-a * {\n a: b;\n}\n"
);
test!(
universal_unification_universal_target_without_namespace_2,
"%-a *.foo {a: b}
*|* {@extend .foo} -a {@extend %-a}
",
"-a * {\n a: b;\n}\n"
);
test!(
universal_unification_universal_target_without_namespace_3,
"%-a *|*.foo {a: b}
* {@extend .foo} -a {@extend %-a}
",
"-a *|*.foo, -a * {\n a: b;\n}\n"
);
test!(
universal_unification_universal_target_without_namespace_4,
"%-a *|*.foo {a: b}
*|* {@extend .foo} -a {@extend %-a}
",
"-a *|* {\n a: b;\n}\n"
);
test!(
universal_unification_universal_target_without_namespace_5,
"%-a *.foo {a: b}
ns|* {@extend .foo} -a {@extend %-a}
",
"-a *.foo {\n a: b;\n}\n"
);
test!(
universal_unification_universal_target_without_namespace_6,
"%-a *|*.foo {a: b}
ns|* {@extend .foo} -a {@extend %-a}
",
"-a *|*.foo, -a ns|* {\n a: b;\n}\n"
);
test!(
universal_unification_universal_target_without_namespace_7,
"%-a ns|*.foo {a: b}
* {@extend .foo} -a {@extend %-a}
",
"-a ns|*.foo {\n a: b;\n}\n"
);
test!(
universal_unification_universal_target_without_namespace_8,
"%-a ns|*.foo {a: b}
*|* {@extend .foo} -a {@extend %-a}
",
"-a ns|* {\n a: b;\n}\n"
);
test!(
universal_unification_universal_target_without_namespace_9,
"%-a ns|*.foo {a: b}
ns|* {@extend .foo} -a {@extend %-a}
",
"-a ns|* {\n a: b;\n}\n"
);
test!(
universal_unification_element_target_without_namespace_1,
"%-a a.foo {a: b}
*|* {@extend .foo} -a {@extend %-a}
",
"-a a {\n a: b;\n}\n"
);
test!(
universal_unification_element_target_without_namespace_2,
"%-a *|a.foo {a: b}
* {@extend .foo} -a {@extend %-a}
",
"-a *|a.foo, -a a {\n a: b;\n}\n"
);
test!(
universal_unification_element_target_without_namespace_3,
"%-a *|a.foo {a: b}
*|* {@extend .foo} -a {@extend %-a}
",
"-a *|a {\n a: b;\n}\n"
);
test!(
universal_unification_element_target_without_namespace_4,
"%-a a.foo {a: b}
ns|* {@extend .foo} -a {@extend %-a}
",
"-a a.foo {\n a: b;\n}\n"
);
test!(
universal_unification_element_target_without_namespace_5,
"%-a *|a.foo {a: b}
ns|* {@extend .foo} -a {@extend %-a}
",
"-a *|a.foo, -a ns|a {\n a: b;\n}\n"
);
test!(
universal_unification_element_target_without_namespace_6,
"%-a ns|a.foo {a: b}
* {@extend .foo} -a {@extend %-a}
",
"-a ns|a.foo {\n a: b;\n}\n"
);
test!(
universal_unification_element_target_without_namespace_7,
"%-a ns|a.foo {a: b}
*|* {@extend .foo} -a {@extend %-a}
",
"-a ns|a {\n a: b;\n}\n"
);
test!(
universal_unification_element_target_without_namespace_8,
"%-a ns|a.foo {a: b}
ns|* {@extend .foo} -a {@extend %-a}
",
"-a ns|a {\n a: b;\n}\n"
);
test!(
element_unification_simple_target_1,
"%-a .foo {a: b}
a {@extend .foo} -a {@extend %-a}
",
"-a .foo, -a a {\n a: b;\n}\n"
);
test!(
element_unification_simple_target_2,
"%-a .foo.bar {a: b}
a {@extend .foo} -a {@extend %-a}
",
"-a .foo.bar, -a a.bar {\n a: b;\n}\n"
);
test!(
element_unification_simple_target_3,
"%-a .foo.bar {a: b}
*|a {@extend .foo} -a {@extend %-a}
",
"-a .foo.bar, -a *|a.bar {\n a: b;\n}\n"
);
test!(
element_unification_simple_target_4,
"%-a .foo.bar {a: b}
ns|a {@extend .foo} -a {@extend %-a}
",
"-a .foo.bar, -a ns|a.bar {\n a: b;\n}\n"
);
test!(
element_unification_universal_without_namespace_1,
"%-a *.foo {a: b}
a {@extend .foo} -a {@extend %-a}
",
"-a *.foo, -a a {\n a: b;\n}\n"
);
test!(
element_unification_universal_without_namespace_2,
"%-a *.foo {a: b}
*|a {@extend .foo} -a {@extend %-a}
",
"-a *.foo, -a a {\n a: b;\n}\n"
);
test!(
element_unification_universal_without_namespace_3,
"%-a *|*.foo {a: b}
a {@extend .foo} -a {@extend %-a}
",
"-a *|*.foo, -a a {\n a: b;\n}\n"
);
test!(
element_unification_universal_without_namespace_4,
"%-a *|*.foo {a: b}
*|a {@extend .foo} -a {@extend %-a}
",
"-a *|*.foo, -a *|a {\n a: b;\n}\n"
);
test!(
element_unification_universal_without_namespace_5,
"%-a *.foo {a: b}
ns|a {@extend .foo} -a {@extend %-a}
",
"-a *.foo {\n a: b;\n}\n"
);
test!(
element_unification_universal_without_namespace_6,
"%-a *|*.foo {a: b}
ns|a {@extend .foo} -a {@extend %-a}
",
"-a *|*.foo, -a ns|a {\n a: b;\n}\n"
);
test!(
element_unification_universal_with_namespace_1,
"%-a ns|*.foo {a: b}
a {@extend .foo} -a {@extend %-a}
",
"-a ns|*.foo {\n a: b;\n}\n"
);
test!(
element_unification_universal_with_namespace_2,
"%-a ns|*.foo {a: b}
*|a {@extend .foo} -a {@extend %-a}
",
"-a ns|*.foo, -a ns|a {\n a: b;\n}\n"
);
test!(
element_unification_universal_with_namespace_3,
"%-a ns|*.foo {a: b}
ns|a {@extend .foo} -a {@extend %-a}
",
"-a ns|*.foo, -a ns|a {\n a: b;\n}\n"
);
test!(
element_unification_element_without_namespace_1,
"%-a a.foo {a: b}
a {@extend .foo} -a {@extend %-a}
",
"-a a {\n a: b;\n}\n"
);
test!(
element_unification_element_without_namespace_2,
"%-a a.foo {a: b}
*|a {@extend .foo} -a {@extend %-a}
",
"-a a {\n a: b;\n}\n"
);
test!(
element_unification_element_without_namespace_3,
"%-a *|a.foo {a: b}
a {@extend .foo} -a {@extend %-a}
",
"-a *|a.foo, -a a {\n a: b;\n}\n"
);
test!(
element_unification_element_without_namespace_4,
"%-a *|a.foo {a: b}
*|a {@extend .foo} -a {@extend %-a}
",
"-a *|a {\n a: b;\n}\n"
);
test!(
element_unification_element_without_namespace_5,
"%-a a.foo {a: b}
ns|a {@extend .foo} -a {@extend %-a}
",
"-a a.foo {\n a: b;\n}\n"
);
test!(
element_unification_element_without_namespace_6,
"%-a *|a.foo {a: b}
ns|a {@extend .foo} -a {@extend %-a}
",
"-a *|a.foo, -a ns|a {\n a: b;\n}\n"
);
test!(
element_unification_element_with_namespace_1,
"%-a ns|a.foo {a: b}
a {@extend .foo} -a {@extend %-a}
",
"-a ns|a.foo {\n a: b;\n}\n"
);
test!(
element_unification_element_with_namespace_2,
"%-a ns|a.foo {a: b}
*|a {@extend .foo} -a {@extend %-a}
",
"-a ns|a {\n a: b;\n}\n"
);
test!(
element_unification_element_with_namespace_3,
"%-a ns|a.foo {a: b}
ns|a {@extend .foo} -a {@extend %-a}
",
"-a ns|a {\n a: b;\n}\n"
);
test!(
attribute_unification_1,
"%-a [foo=bar].baz {a: b}
[foo=baz] {@extend .baz} -a {@extend %-a}
",
"-a [foo=bar].baz, -a [foo=bar][foo=baz] {\n a: b;\n}\n"
);
test!(
attribute_unification_2,
"%-a [foo=bar].baz {a: b}
[foo^=bar] {@extend .baz} -a {@extend %-a}
",
"-a [foo=bar].baz, -a [foo=bar][foo^=bar] {\n a: b;\n}\n"
);
test!(
attribute_unification_3,
"%-a [foo=bar].baz {a: b}
[foot=bar] {@extend .baz} -a {@extend %-a}
",
"-a [foo=bar].baz, -a [foo=bar][foot=bar] {\n a: b;\n}\n"
);
test!(
attribute_unification_4,
"%-a [foo=bar].baz {a: b}
[ns|foo=bar] {@extend .baz} -a {@extend %-a}
",
"-a [foo=bar].baz, -a [foo=bar][ns|foo=bar] {\n a: b;\n}\n"
);
test!(
attribute_unification_5,
"%-a %-a [foo=bar].bar {a: b}
[foo=bar] {@extend .bar} -a {@extend %-a}
",
"-a -a [foo=bar] {\n a: b;\n}\n"
);
test!(
pseudo_unification_1,
"%-a :foo.baz {a: b}
:foo(2n+1) {@extend .baz} -a {@extend %-a}
",
"-a :foo.baz, -a :foo:foo(2n+1) {\n a: b;\n}\n"
);
test!(
pseudo_unification_2,
"%-a :foo.baz {a: b}
::foo {@extend .baz} -a {@extend %-a}
",
"-a :foo.baz, -a :foo::foo {\n a: b;\n}\n"
);
test!(
pseudo_unification_3,
"%-a ::foo.baz {a: b}
::foo {@extend .baz} -a {@extend %-a}
",
"-a ::foo {\n a: b;\n}\n"
);
test!(
pseudo_unification_4,
"%-a ::foo(2n+1).baz {a: b}
::foo(2n+1) {@extend .baz} -a {@extend %-a}
",
"-a ::foo(2n+1) {\n a: b;\n}\n"
);
test!(
pseudo_unification_5,
"%-a :foo.baz {a: b}
:bar {@extend .baz} -a {@extend %-a}
",
"-a :foo.baz, -a :foo:bar {\n a: b;\n}\n"
);
test!(
pseudo_unification_6,
"%-a .baz:foo {a: b}
:after {@extend .baz} -a {@extend %-a}
",
"-a .baz:foo, -a :foo:after {\n a: b;\n}\n"
);
test!(
pseudo_unification_7,
"%-a .baz:after {a: b}
:foo {@extend .baz} -a {@extend %-a}
",
"-a .baz:after, -a :foo:after {\n a: b;\n}\n"
);
test!(
pseudo_unification_8,
"%-a :foo.baz {a: b}
:foo {@extend .baz} -a {@extend %-a}
",
"-a :foo {\n a: b;\n}\n"
);
test!(
pseudoelement_remains_at_end_of_selector_1,
".foo::bar {a: b}
.baz {@extend .foo}
",
".foo::bar, .baz::bar {\n a: b;\n}\n"
);
test!(
pseudoelement_remains_at_end_of_selector_2,
"a.foo::bar {a: b}
.baz {@extend .foo}
",
"a.foo::bar, a.baz::bar {\n a: b;\n}\n"
);
test!(
pseudoclass_remains_at_end_of_selector_1,
".foo:bar {a: b}
.baz {@extend .foo}
",
".foo:bar, .baz:bar {\n a: b;\n}\n"
);
test!(
pseudoclass_remains_at_end_of_selector_2,
"a.foo:bar {a: b}
.baz {@extend .foo}
",
"a.foo:bar, a.baz:bar {\n a: b;\n}\n"
);
test!(
pseudoclass_not_remains_at_end_of_selector,
".foo:not(.bar) {a: b}
.baz {@extend .foo}
",
".foo:not(.bar), .baz:not(.bar) {\n a: b;\n}\n"
);
test!(
pseudoelement_goes_lefter_than_pseudoclass_1,
".foo::bar {a: b}
.baz:bang {@extend .foo}
",
".foo::bar, .baz:bang::bar {\n a: b;\n}\n"
);
test!(
pseudoelement_goes_lefter_than_pseudoclass_2,
".foo:bar {a: b}
.baz::bang {@extend .foo}
",
".foo:bar, .baz:bar::bang {\n a: b;\n}\n"
);
test!(
pseudoelement_goes_lefter_than_not_1,
".foo::bar {a: b}
.baz:not(.bang) {@extend .foo}
",
".foo::bar, .baz:not(.bang)::bar {\n a: b;\n}\n"
);
test!(
pseudoelement_goes_lefter_than_not_2,
"%a {
a:b;
}
b:after:not(:first-child) {
@extend %a;
}
c:s {
@extend %a;
}
d::e {
@extend c;
}
",
"c:s, d:s::e, b:after:not(:first-child) {\n a: b;\n}\n"
);
test!(
pseudoelement_goes_lefter_than_not_3,
".foo:not(.bang) {a: b}
.baz::bar {@extend .foo}
",
".foo:not(.bang), .baz:not(.bang)::bar {\n a: b;\n}\n"
);
test!(
negation_unification_1,
"%-a :not(.foo).baz {a: b}
:not(.bar) {@extend .baz} -a {@extend %-a}
",
"-a :not(.foo).baz, -a :not(.foo):not(.bar) {\n a: b;\n}\n"
);
test!(
negation_unification_2,
"%-a :not(.foo).baz {a: b}
:not(.foo) {@extend .baz} -a {@extend %-a}
",
"-a :not(.foo) {\n a: b;\n}\n"
);
test!(
negation_unification_3,
"%-a :not([a=b]).baz {a: b}
:not([a = b]) {@extend .baz} -a {@extend %-a}
",
"-a :not([a=b]) {\n a: b;\n}\n"
);
test!(
comma_extendee,
".foo {a: b}
.bar {c: d}
.baz {@extend .foo, .bar}
",
".foo, .baz {\n a: b;\n}\n\n.bar, .baz {\n c: d;\n}\n"
);
test!(
redundant_selector_elimination,
".foo.bar {a: b}
.x {@extend .foo, .bar}
.y {@extend .foo, .bar}
",
".foo.bar, .y, .x {\n a: b;\n}\n"
);
error!(
extend_compound_selector,
"ns|*.foo.bar {a: b}
a.baz {@extend .foo.bar}
",
"Error: compound selectors may no longer be extended."
);
test!(
compound_extender,
".foo.bar {a: b}
.baz.bang {@extend .foo}
",
".foo.bar, .bar.baz.bang {\n a: b;\n}\n"
);
test!(
compound_extender_unification,
"ns|*.foo.bar {a: b}
a.baz {@extend .foo}
",
"ns|*.foo.bar {\n a: b;\n}\n"
);
test!(
complex_extender,
".foo {a: b}
foo bar {@extend .foo}
",
".foo, foo bar {\n a: b;\n}\n"
);
test!(
complex_extender_unification,
".foo.bar {a: b}
foo bar {@extend .foo}
",
".foo.bar, foo bar.bar {\n a: b;\n}\n"
);
test!(
complex_extender_alternates_parents,
".baz .bip .foo {a: b}
foo .grank bar {@extend .foo}
",
".baz .bip .foo, .baz .bip foo .grank bar, foo .grank .baz .bip bar {\n a: b;\n}\n"
);
test!(
complex_extender_unifies_identical_parents,
".baz .bip .foo {a: b}
.baz .bip bar {@extend .foo}
",
".baz .bip .foo, .baz .bip bar {\n a: b;\n}\n"
);
test!(
complex_extender_unifies_common_substring,
".baz .bip .bap .bink .foo {a: b}
.brat .bip .bap bar {@extend .foo}
",
".baz .bip .bap .bink .foo, .baz .brat .bip .bap .bink bar, .brat .baz .bip .bap .bink bar {\n a: b;\n}\n"
);
test!(
complex_extender_unifies_common_subsequence,
".a .x .b .y .foo {a: b}
.a .n .b .m bar {@extend .foo}
",
".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 {\n a: b;\n}\n"
);
test!(
complex_extender_chooses_first_subsequence,
".a .b .c .d .foo {a: b}
.c .d .a .b .bar {@extend .foo}
",
".a .b .c .d .foo, .a .b .c .d .a .b .bar {\n a: b;\n}\n"
);
test!(
complex_extender_counts_extended_superselectors,
".a .bip .foo {a: b}
.b .bip.bop .bar {@extend .foo}
",
".a .bip .foo, .a .b .bip.bop .bar, .b .a .bip.bop .bar {\n a: b;\n}\n"
);
test!(
complex_extender_child_combinator,
".baz .foo {a: b}
foo > bar {@extend .foo}
",
".baz .foo, .baz foo > bar {\n a: b;\n}\n"
);
test!(
complex_extender_finds_common_selectors_around_child_combinator_1,
"a > b c .c1 {a: b}
a c .c2 {@extend .c1}
",
"a > b c .c1, a > b c .c2 {\n a: b;\n}\n"
);
test!(
complex_extender_finds_common_selectors_around_child_combinator_2,
"a > b c .c1 {a: b}
b c .c2 {@extend .c1}
",
"a > b c .c1, a > b c .c2 {\n a: b;\n}\n"
);
test!(
complex_extender_finds_common_selectors_around_adjacent_sibling_combinator_1,
"a + b c .c1 {a: b}
a c .c2 {@extend .c1}
",
"a + b c .c1, a + b a c .c2, a a + b c .c2 {\n a: b;\n}\n"
);
test!(
complex_extender_finds_common_selectors_around_adjacent_sibling_combinator_2,
"a + b c .c1 {a: b}
a b .c2 {@extend .c1}
",
"a + b c .c1, a a + b c .c2 {\n a: b;\n}\n"
);
test!(
complex_extender_finds_common_selectors_around_adjacent_sibling_combinator_3,
"a + b c .c1 {a: b}
b c .c2 {@extend .c1}
",
"a + b c .c1, a + b c .c2 {\n a: b;\n}\n"
);
test!(
complex_extender_finds_common_selectors_around_sibling_combinator_1,
"a ~ b c .c1 {a: b}
a c .c2 {@extend .c1}
",
"a ~ b c .c1, a ~ b a c .c2, a a ~ b c .c2 {\n a: b;\n}\n"
);
test!(
complex_extender_finds_common_selectors_around_sibling_combinator_2,
"a ~ b c .c1 {a: b}
a b .c2 {@extend .c1}
",
"a ~ b c .c1, a a ~ b c .c2 {\n a: b;\n}\n"
);
test!(
complex_extender_finds_common_selectors_around_sibling_combinator_3,
"a ~ b c .c1 {a: b}
b c .c2 {@extend .c1}
",
"a ~ b c .c1, a ~ b c .c2 {\n a: b;\n}\n"
);
test!(
complex_extender_with_early_child_selectors_doesnt_subsequence_them_1,
".bip > .bap .foo {a: b}
.grip > .bap .bar {@extend .foo}
",
".bip > .bap .foo, .bip > .bap .grip > .bap .bar, .grip > .bap .bip > .bap .bar {\n a: b;\n}\n"
);
test!(
complex_extender_with_early_child_selectors_doesnt_subsequence_them_2,
".bap > .bip .foo {a: b}
.bap > .grip .bar {@extend .foo}
",
".bap > .bip .foo, .bap > .bip .bap > .grip .bar, .bap > .grip .bap > .bip .bar {\n a: b;\n}\n"
);
test!(
complex_extender_with_child_selector_unifies_1,
".baz.foo {a: b}
foo > bar {@extend .foo}
",
".baz.foo, foo > bar.baz {\n a: b;\n}\n"
);
test!(
complex_extender_with_child_selector_unifies_2,
".baz > {
.foo {a: b}
.bar {@extend .foo}
}
",
".baz > .foo, .baz > .bar {\n a: b;\n}\n"
);
test!(
complex_extender_with_child_selector_unifies_3,
".foo {
.bar {a: b}
> .baz {@extend .bar}
}
",
".foo .bar, .foo > .baz {\n a: b;\n}\n"
);
test!(
complex_extender_with_early_child_selector_1,
".foo {
.bar {a: b}
.bip > .baz {@extend .bar}
}
",
".foo .bar, .foo .bip > .baz {\n a: b;\n}\n"
);
test!(
complex_extender_with_early_child_selector_2,
".foo {
.bip .bar {a: b}
> .baz {@extend .bar}
}
",
".foo .bip .bar, .foo .bip .foo > .baz {\n a: b;\n}\n"
);
test!(
complex_extender_with_early_child_selector_3,
".foo > .bar {a: b}
.bip + .baz {@extend .bar}
",
".foo > .bar, .foo > .bip + .baz {\n a: b;\n}\n"
);
test!(
complex_extender_with_early_child_selector_4,
".foo + .bar {a: b}
.bip > .baz {@extend .bar}
",
".foo + .bar, .bip > .foo + .baz {\n a: b;\n}\n"
);
test!(
complex_extender_with_early_child_selector_5,
".foo > .bar {a: b}
.bip > .baz {@extend .bar}
",
".foo > .bar, .bip.foo > .baz {\n a: b;\n}\n"
);
test!(
complex_extender_with_sibling_selector,
".baz .foo {a: b}
foo + bar {@extend .foo}
",
".baz .foo, .baz foo + bar {\n a: b;\n}\n"
);
test!(
complex_extender_with_hacky_selector_1,
".baz .foo {a: b}
foo + > > + bar {@extend .foo}
",
".baz .foo, .baz foo + > > + bar, foo .baz + > > + bar {\n a: b;\n}\n"
);
test!(
complex_extender_with_hacky_selector_2,
".baz .foo {a: b}
> > bar {@extend .foo}
",
".baz .foo, > > .baz bar {\n a: b;\n}\n"
);
test!(
complex_extender_merges_with_the_same_selector,
".foo {
.bar {a: b}
.baz {@extend .bar}
}
",
".foo .bar, .foo .baz {\n a: b;\n}\n"
);
test!(
complex_extender_with_child_selector_merges_with_the_same_selector,
".foo > .bar .baz {a: b}
.foo > .bar .bang {@extend .baz}
",
".foo > .bar .baz, .foo > .bar .bang {\n a: b;\n}\n"
);
test!(
combinator_unification_for_hacky_combinators_1,
".a > + x {a: b}
.b y {@extend x}
",
".a > + x, .a .b > + y, .b .a > + y {\n a: b;\n}\n"
);
test!(
combinator_unification_for_hacky_combinators_2,
".a x {a: b}
.b > + y {@extend x}
",
".a x, .a .b > + y, .b .a > + y {\n a: b;\n}\n"
);
test!(
combinator_unification_for_hacky_combinators_3,
".a > + x {a: b}
.b > + y {@extend x}
",
".a > + x, .a .b > + y, .b .a > + y {\n a: b;\n}\n"
);
test!(
combinator_unification_for_hacky_combinators_4,
".a ~ > + x {a: b}
.b > + y {@extend x}
",
".a ~ > + x, .a .b ~ > + y, .b .a ~ > + y {\n a: b;\n}\n"
);
test!(
combinator_unification_for_hacky_combinators_5,
".a + > x {a: b}
.b > + y {@extend x}
",
".a + > x {\n a: b;\n}\n"
);
test!(
combinator_unification_for_hacky_combinators_6,
".a + > x {a: b}
.b > + y {@extend x}
",
".a + > x {\n a: b;\n}\n"
);
test!(
combinator_unification_for_hacky_combinators_7,
".a ~ > + .b > x {a: b}
.c > + .d > y {@extend x}
",
".a ~ > + .b > x, .a .c ~ > + .d.b > y, .c .a ~ > + .d.b > y {\n a: b;\n}\n"
);
test!(
combinator_unification_double_tilde_1,
".a.b ~ x {a: b}
.a ~ y {@extend x}
",
".a.b ~ x, .a.b ~ y {\n a: b;\n}\n"
);
test!(
combinator_unification_double_tilde_2,
".a ~ x {a: b}
.a.b ~ y {@extend x}
",
".a ~ x, .a.b ~ y {\n a: b;\n}\n"
);
test!(
combinator_unification_double_tilde_3,
".a ~ x {a: b}
.b ~ y {@extend x}
",
".a ~ x, .a ~ .b ~ y, .b ~ .a ~ y, .b.a ~ y {\n a: b;\n}\n"
);
test!(
combinator_unification_double_tilde_4,
"a.a ~ x {a: b}
b.b ~ y {@extend x}
",
"a.a ~ x, a.a ~ b.b ~ y, b.b ~ a.a ~ y {\n a: b;\n}\n"
);
test!(
combinator_unification_tilde_plus_1,
".a.b + x {a: b}
.a ~ y {@extend x}
",
".a.b + x, .a.b + y {\n a: b;\n}\n"
);
test!(
combinator_unification_tilde_plus_2,
".a + x {a: b}
.a.b ~ y {@extend x}
",
".a + x, .a.b ~ .a + y, .a.b + y {\n a: b;\n}\n"
);
test!(
combinator_unification_tilde_plus_3,
".a + x {a: b}
.b ~ y {@extend x}
",
".a + x, .b ~ .a + y, .b.a + y {\n a: b;\n}\n"
);
test!(
combinator_unification_tilde_plus_4,
"a.a + x {a: b}
b.b ~ y {@extend x}
",
"a.a + x, b.b ~ a.a + y {\n a: b;\n}\n"
);
test!(
combinator_unification_tilde_plus_5,
".a.b ~ x {a: b}
.a + y {@extend x}
",
".a.b ~ x, .a.b ~ .a + y, .a.b + y {\n a: b;\n}\n"
);
test!(
combinator_unification_tilde_plus_6,
".a ~ x {a: b}
.a.b + y {@extend x}
",
".a ~ x, .a.b + y {\n a: b;\n}\n"
);
test!(
combinator_unification_tilde_plus_7,
".a ~ x {a: b}
.b + y {@extend x}
",
".a ~ x, .a ~ .b + y, .b.a + y {\n a: b;\n}\n"
);
test!(
combinator_unification_angle_sibling_1,
".a > x {a: b}
.b ~ y {@extend x}
",
".a > x, .a > .b ~ y {\n a: b;\n}\n"
);
test!(
combinator_unification_angle_sibling_2,
".a > x {a: b}
.b + y {@extend x}
",
".a > x, .a > .b + y {\n a: b;\n}\n"
);
test!(
combinator_unification_angle_sibling_3,
".a ~ x {a: b}
.b > y {@extend x}
",
".a ~ x, .b > .a ~ y {\n a: b;\n}\n"
);
test!(
combinator_unification_angle_sibling_4,
".a + x {a: b}
.b > y {@extend x}
",
".a + x, .b > .a + y {\n a: b;\n}\n"
);
test!(
combinator_unification_double_angle_1,
".a.b > x {a: b}
.b > y {@extend x}
",
".a.b > x, .b.a > y {\n a: b;\n}\n"
);
test!(
combinator_unification_double_angle_2,
".a > x {a: b}
.a.b > y {@extend x}
",
".a > x, .a.b > y {\n a: b;\n}\n"
);
test!(
combinator_unification_double_angle_3,
".a > x {a: b}
.b > y {@extend x}
",
".a > x, .b.a > y {\n a: b;\n}\n"
);
test!(
combinator_unification_double_angle_4,
"a.a > x {a: b}
b.b > y {@extend x}
",
"a.a > x {\n a: b;\n}\n"
);
test!(
combinator_unification_double_plus_1,
".a.b + x {a: b}
.b + y {@extend x}
",
".a.b + x, .b.a + y {\n a: b;\n}\n"
);
test!(
combinator_unification_double_plus_2,
".a + x {a: b}
.a.b + y {@extend x}
",
".a + x, .a.b + y {\n a: b;\n}\n"
);
test!(
combinator_unification_double_plus_3,
".a + x {a: b}
.b + y {@extend x}
",
".a + x, .b.a + y {\n a: b;\n}\n"
);
test!(
combinator_unification_double_plus_4,
"a.a + x {a: b}
b.b + y {@extend x}
",
"a.a + x {\n a: b;\n}\n"
);
test!(
combinator_unification_angle_space_1,
".a.b > x {a: b}
.a y {@extend x}
",
".a.b > x, .a.b > y {\n a: b;\n}\n"
);
test!(
combinator_unification_angle_space_2,
".a > x {a: b}
.a.b y {@extend x}
",
".a > x, .a.b .a > y {\n a: b;\n}\n"
);
test!(
combinator_unification_angle_space_3,
".a > x {a: b}
.b y {@extend x}
",
".a > x, .b .a > y {\n a: b;\n}\n"
);
test!(
combinator_unification_angle_space_4,
".a.b x {a: b}
.a > y {@extend x}
",
".a.b x, .a.b .a > y {\n a: b;\n}\n"
);
test!(
combinator_unification_angle_space_5,
".a x {a: b}
.a.b > y {@extend x}
",
".a x, .a.b > y {\n a: b;\n}\n"
);
test!(
combinator_unification_angle_space_6,
".a x {a: b}
.b > y {@extend x}
",
".a x, .a .b > y {\n a: b;\n}\n"
);
test!(
combinator_unification_plus_space_1,
".a.b + x {a: b}
.a y {@extend x}
",
".a.b + x, .a .a.b + y {\n a: b;\n}\n"
);
test!(
combinator_unification_plus_space_2,
".a + x {a: b}
.a.b y {@extend x}
",
".a + x, .a.b .a + y {\n a: b;\n}\n"
);
test!(
combinator_unification_plus_space_3,
".a + x {a: b}
.b y {@extend x}
",
".a + x, .b .a + y {\n a: b;\n}\n"
);
test!(
combinator_unification_plus_space_4,
".a.b x {a: b}
.a + y {@extend x}
",
".a.b x, .a.b .a + y {\n a: b;\n}\n"
);
test!(
combinator_unification_plus_space_5,
".a x {a: b}
.a.b + y {@extend x}
",
".a x, .a .a.b + y {\n a: b;\n}\n"
);
test!(
combinator_unification_plus_space_6,
".a x {a: b}
.b + y {@extend x}
",
".a x, .a .b + y {\n a: b;\n}\n"
);
test!(
nested_combinator_unification_1,
".a > .b + x {a: b}
.c > .d + y {@extend x}
",
".a > .b + x, .c.a > .d.b + y {\n a: b;\n}\n"
);
test!(
nested_combinator_unification_2,
".a > .b + x {a: b}
.c > y {@extend x}
",
".a > .b + x, .c.a > .b + y {\n a: b;\n}\n"
);
test!(
combinator_unification_with_newlines,
".a >\n.b\n+ x {a: b}\n.c\n> .d +\ny {@extend x}\n",
".a > .b + x, .c.a > .d.b + y {\n a: b;\n}\n"
);
test!(
basic_extend_loop,
".foo {a: b; @extend .bar}
.bar {c: d; @extend .foo}
",
".foo, .bar {\n a: b;\n}\n\n.bar, .foo {\n c: d;\n}\n"
);
test!(
three_level_extend_loop,
".foo {a: b; @extend .bar}
.bar {c: d; @extend .baz}
.baz {e: f; @extend .foo}
",
".foo, .baz, .bar {\n a: b;\n}\n\n.bar, .foo, .baz {\n c: d;\n}\n\n.baz, .bar, .foo {\n e: f;\n}\n"
);
test!(
nested_extend_loop,
".bar {
a: b;
.foo {c: d; @extend .bar}
}
",
".bar, .bar .foo {\n a: b;\n}\n.bar .foo {\n c: d;\n}\n"
);
test!(
multiple_extender_merges_with_superset_selector,
".foo {@extend .bar; @extend .baz}
a.bar.baz {a: b}
",
"a.bar.baz, a.foo {\n a: b;\n}\n"
);
test!(
inside_control_flow_if,
".true { color: green; }
.false { color: red; }
.also-true {
@if true { @extend .true; }
@else { @extend .false; }
}
.also-false {
@if false { @extend .true; }
@else { @extend .false; }
}
",
".true, .also-true {\n color: green;\n}\n\n.false, .also-false {\n color: red;\n}\n"
);
test!(
inside_control_flow_for,
"
.base-0 { color: green; }
.base-1 { display: block; }
.base-2 { border: 1px solid blue; }
.added {
@for $i from 0 to 3 {
@extend .base-#{$i};
}
}
",
".base-0, .added {\n color: green;\n}\n\n.base-1, .added {\n display: block;\n}\n\n.base-2, .added {\n border: 1px solid blue;\n}\n"
);
test!(
inside_control_flow_while,
"
.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;
}
}
",
".base-0, .added {\n color: green;\n}\n\n.base-1, .added {\n display: block;\n}\n\n.base-2, .added {\n border: 1px solid blue;\n}\n"
);
test!(
basic_placeholder,
"%foo {a: b}
.bar {@extend %foo}
",
".bar {\n a: b;\n}\n"
);
test!(
unused_placeholder,
"%foo {a: b}
%bar {a: b}
.baz {@extend %foo}
",
".baz {\n a: b;\n}\n"
);
test!(
placeholder_descendant,
"#context %foo a {a: b}
.bar {@extend %foo}
",
"#context .bar a {\n a: b;\n}\n"
);
test!(
semi_placeholder,
"#context %foo, .bar .baz {a: b}
.bat {
@extend %foo;
}
",
"#context .bat, .bar .baz {\n a: b;\n}\n"
);
test!(
placeholder_with_multiple_extenders,
"%foo {a: b}
.bar {@extend %foo}
.baz {@extend %foo}
",
".baz, .bar {\n a: b;\n}\n"
);
test!(
placeholder_interpolation,
"$foo: foo;
%#{$foo} {a: b}
.bar {@extend %foo}
",
".bar {\n a: b;\n}\n"
);
test!(
media_inside_placeholder,
"%foo {bar {@media screen {a {b: c}}}}
.baz {c: d}
",
".baz {\n c: d;\n}\n"
);
test!(
extend_within_media,
"@media screen {
.foo {a: b}
.bar {@extend .foo}
}
",
"@media screen {\n .foo, .bar {\n a: b;\n }\n}\n"
);
test!(
extend_within_unknown_at_rule,
"@unknown {
.foo {a: b}
.bar {@extend .foo}
}
",
"@unknown {\n .foo, .bar {\n a: b;\n }\n}\n"
);
test!(
extend_within_nested_at_rules,
"@media screen {
@unknown {
.foo {a: b}
.bar {@extend .foo}
}
}
",
"@media screen {\n @unknown {\n .foo, .bar {\n a: b;\n }\n }\n}\n"
);
test!(
extend_within_separate_media_queries,
"@media screen {.foo {a: b}}
@media screen {.bar {@extend .foo}}
",
"@media screen {\n .foo, .bar {\n a: b;\n }\n}\n"
);
test!(
extend_within_separate_unknown_at_rules,
"@unknown {.foo {a: b}}
@unknown {.bar {@extend .foo}}
",
"@unknown {\n .foo, .bar {\n a: b;\n }\n}\n@unknown {}\n"
);
test!(
extend_within_separate_nested_at_rules,
"@media screen {@flooblehoof {.foo {a: b}}}
@media screen {@flooblehoof {.bar {@extend .foo}}}",
"@media screen {\n @flooblehoof {\n .foo, .bar {\n a: b;\n }\n }\n}\n@media screen {\n @flooblehoof {}\n}\n"
);
test!(
extend_succeeds_when_one_extend_fails_but_others_dont,
"a.bar {a: b}
.bar {c: d}
b.foo {@extend .bar}
",
"a.bar {\n a: b;\n}\n\n.bar, b.foo {\n c: d;\n}\n"
);
test!(
optional_extend_succeeds_when_extendee_doesnt_exist,
".foo {@extend .bar !optional}",
""
);
test!(
optional_extend_succeeds_when_extension_fails,
"a.bar {a: b}
b.foo {@extend .bar !optional}
",
"a.bar {\n a: b;\n}\n"
);
test!(
psuedo_element_superselector_1,
"%x#bar {a: b} // Add an id to make the results have high specificity
%y, %y::fblthp {@extend %x}
a {@extend %y}
",
"a#bar, a#bar::fblthp {\n a: b;\n}\n"
);
test!(
psuedo_element_superselector_2,
"%x#bar {a: b}
%y, %y:fblthp {@extend %x}
a {@extend %y}
",
"a#bar {\n a: b;\n}\n"
);
test!(
psuedo_element_superselector_3,
"%x#bar {a: b}
%y, %y:first-line {@extend %x}
a {@extend %y}
",
"a#bar, a#bar:first-line {\n a: b;\n}\n"
);
test!(
psuedo_element_superselector_4,
"%x#bar {a: b}
%y, %y:first-letter {@extend %x}
a {@extend %y}
",
"a#bar, a#bar:first-letter {\n a: b;\n}\n"
);
test!(
psuedo_element_superselector_5,
"%x#bar {a: b}
%y, %y:before {@extend %x}
a {@extend %y}
",
"a#bar, a#bar:before {\n a: b;\n}\n"
);
test!(
psuedo_element_superselector_6,
"%x#bar {a: b}
%y, %y:after {@extend %x}
a {@extend %y}
",
"a#bar, a#bar:after {\n a: b;\n}\n"
);
test!(
multiple_source_redundancy_elimination,
"%default-color {color: red}
%alt-color {color: green}
%default-style {
@extend %default-color;
&:hover {@extend %alt-color}
&:active {@extend %default-color}
}
.test-case {@extend %default-style}
",
".test-case:active, .test-case {\n color: red;\n}\n\n.test-case:hover {\n color: green;\n}\n"
);
test!(
nested_sibling_extend,
".foo {@extend .bar}
.parent {
.bar {
a: b;
}
.foo {
@extend .bar
}
}
",
".parent .bar, .parent .foo {\n a: b;\n}\n"
);
test!(
parent_and_sibling_extend,
"%foo %bar%baz {a: b}
.parent1 {
@extend %foo;
.child1 {@extend %bar}
}
.parent2 {
@extend %foo;
.child2 {@extend %baz}
}
",
".parent1 .parent2 .child1.child2, .parent2 .parent1 .child1.child2 {\n a: b;\n}\n"
);
test!(
nested_extend_specificity,
"%foo {a: b}
a {
:b {@extend %foo}
:b:c {@extend %foo}
}
",
"a :b:c, a :b {\n a: b;\n}\n"
);
test!(
double_extend_optimization,
"%foo %bar {
a: b;
}
.parent1 {
@extend %foo;
.child {
@extend %bar;
}
}
.parent2 {
@extend %foo;
}
",
".parent1 .child {\n a: b;\n}\n"
);
test!(
extend_inside_double_nested_media,
"@media all {
@media (orientation: landscape) {
%foo {color: blue}
.bar {@extend %foo}
}
}
",
"@media (orientation: landscape) {\n .bar {\n color: blue;\n }\n}\n"
);
test!(
partially_failed_extend,
"test { @extend .rc; }
.rc {color: white;}
.prices span.pill span.rc {color: red;}
",
".rc, test {\n color: white;\n}\n\n.prices span.pill span.rc {\n color: red;\n}\n"
);
test!(
newline_near_combinator,
".a +
.b x {a: b}
.c y {@extend x}
",
".a + .b x, .a + .b .c y, .c .a + .b y {\n a: b;\n}\n"
);
test!(
duplicated_selector_with_newlines,
".example-1-1,
.example-1-2,
.example-1-3 {
a: b;
}
.my-page-1 .my-module-1-1 {@extend .example-1-2}
",
".example-1-1,\n.example-1-2,\n.my-page-1 .my-module-1-1,\n.example-1-3 {\n a: b;\n}\n"
);
test!(
nested_selector_with_child_selector_hack_extendee,
"> .foo {a: b}
foo bar {@extend .foo}
",
"> .foo, > foo bar {\n a: b;\n}\n"
);
test!(
nested_selector_with_child_selector_hack_extender,
".foo .bar {a: b}
> foo bar {@extend .bar}
",
".foo .bar, > .foo foo bar, > foo .foo bar {\n a: b;\n}\n"
);
test!(
nested_selector_with_child_selector_hack_extender_and_extendee,
"> .foo {a: b}
> foo bar {@extend .foo}
",
"> .foo, > foo bar {\n a: b;\n}\n"
);
test!(
nested_selector_with_child_selector_hack_extender_and_sibling_extendee,
"~ .foo {a: b}
> foo bar {@extend .foo}
",
"~ .foo {\n a: b;\n}\n"
);
test!(
nested_selector_with_child_selector_hack_extender_and_extendee_newline,
"> .foo {a: b}\nflip,\n> foo bar {@extend .foo}\n",
"> .foo, > flip,\n> foo bar {\n a: b;\n}\n"
);
test!(
extended_parent_and_child_redundancy_elimination,
"a {
b {a: b}
c {@extend b}
}
d {@extend a}
",
"a b, d b, a c, d c {\n a: b;\n}\n"
);
test!(
redundancy_elimination_when_it_would_reduce_specificity,
"a {a: b}
a.foo {@extend a}
",
"a, a.foo {\n a: b;\n}\n"
);
test!(
redundancy_elimination_when_it_would_preserve_specificity,
".bar a {a: b}
a.foo {@extend a}
",
".bar a {\n a: b;\n}\n"
);
test!(
redundancy_elimination_never_eliminates_base_selector,
"a.foo {a: b}
.foo {@extend a}
",
"a.foo, .foo {\n a: b;\n}\n"
);
test!(
cross_branch_redundancy_elimination_1,
"%x .c %y {a: b}
.a, .b {@extend %x}
.a .d {@extend %y}
",
".a .c .d, .b .c .a .d {\n a: b;\n}\n"
);
test!(
cross_branch_redundancy_elimination_2,
".e %z {a: b}
%x .c %y {@extend %z}
.a, .b {@extend %x}
.a .d {@extend %y}
",
".e .a .c .d, .e .b .c .a .d, .a .e .b .c .d, .a .c .e .d, .b .c .e .a .d {\n a: b;\n}\n"
);
test!(
extend_with_universal_selector,
"%-a *.foo1 {a: b}
a {@extend .foo1}
-a {@extend %-a}
%-b *|*.foo2 {b: b}
b {@extend .foo2}
-b {@extend %-b}
",
"-a *.foo1, -a a {\n a: b;\n}\n\n-b *|*.foo2, -b b {\n b: b;\n}\n"
);
test!(
extend_with_universal_selector_empty_namespace,
"%-a |*.foo {a: b}
a {@extend .foo}
-a {@extend %-a}
",
"-a |*.foo {\n a: b;\n}\n"
);
test!(
extend_with_universal_selector_different_namespace,
"%-a ns|*.foo {a: b}
a {@extend .foo}
-a {@extend %-a}
",
"-a ns|*.foo {\n a: b;\n}\n"
);
test!(
unify_root_pseudo_element,
"// We assume that by default classes don't apply to the :root unless marked explicitly.
:root .foo-1 { test: 1; }
.bar-1 .baz-1 { @extend .foo-1; }
// We know the two classes must be the same :root element so we can combine them.
.foo-2:root .bar-2 { test: 2; }
.baz-2:root .bang-2 { @extend .bar-2; }
// This extend should not apply because the :root elements are different.
html:root .bar-3 { test: 3; }
xml:root .bang-3 { @extend .bar-3}
// We assume that direct descendant of the :root is not the same element as a descendant.
.foo-4:root > .bar-4 .x-4 { test: 4; }
.baz-4:root .bang-4 .y-4 {@extend .x-4}
",
":root .foo-1, :root .bar-1 .baz-1 {\n test: 1;\n}\n\n.foo-2:root .bar-2, .baz-2.foo-2:root .bang-2 {\n test: 2;\n}\n\nhtml:root .bar-3 {\n test: 3;\n}\n\n.foo-4:root > .bar-4 .x-4, .baz-4.foo-4:root > .bar-4 .bang-4 .y-4 {\n test: 4;\n}\n"
);
test!(
compound_unification_in_not,
"// Make sure compound selectors are unified when two :not()s are extended.
// :not() is special here because it's the only selector that's extended by
// adding to the compound selector, rather than creating a new selector list.
.a {@extend .c}
.b {@extend .d}
:not(.c):not(.d) {a: b}
",
":not(.c):not(.a):not(.d):not(.b) {\n a: b;\n}\n"
);
test!(
does_not_move_page_block_in_media,
"@media screen {
a { x:y; }
@page {}
}
",
"@media screen {\n a {\n x: y;\n }\n @page {}\n}\n"
);
test!(
escaped_selector,
"// Escapes in selectors' identifiers should be normalized before `@extend` is
// applied.
.foo {escape: none}
\\.foo {escape: slash dot}
\\2E foo {escape: hex}
.bar {@extend \\02e foo}
",
".foo {\n escape: none;\n}\n\n\\.foo, .bar {\n escape: slash dot;\n}\n\n\\.foo, .bar {\n escape: hex;\n}\n"
);
test!(
#[ignore = "Rc<RefCell<Selector>>"]
extend_extender,
"// For implementations like Dart Sass that process extensions as they occur,
// extending rules that contain their own extends needs special handling.
.b {@extend .a}
.c {@extend .b}
.a {x: y}
",
".a, .b, .c {\n x: y;\n}\n"
);
test!(
extend_result_of_extend,
"// The result of :not(.c) being extended should itself be extendable.
.a {@extend :not(.b)}
.b {@extend .c}
:not(.c) {x: y}
",
":not(.c):not(.b), .a:not(.c) {\n x: y;\n}\n"
);
test!(
extend_self,
"// This shouldn't change the selector.
.c, .a .b .c, .a .c .b {x: y; @extend .c}
",
".c, .a .b .c, .a .c .b {\n x: y;\n}\n"
);
test!(
dart_sass_issue_146,
"%btn-style-default {
background: green;
&:hover{
background: black;
}
}
button {
@extend %btn-style-default;
}
",
"button {\n background: green;\n}\nbutton:hover {\n background: black;\n}\n"
);
test!(
nested_compound_unification,
"// Make sure compound unification properly handles weaving together parent
// selectors.
.a .b {@extend .e}
.c .d {@extend .f}
.e.f {x: y}
",
".e.f, .a .f.b, .c .e.d, .a .c .b.d, .c .a .b.d {\n x: y;\n}\n"
);
test!(
not_into_not_not,
"// Regression test for dart-sass#191.
:not(:not(.x)) {a: b}
:not(.y) {@extend .x}
",
":not(:not(.x)) {\n a: b;\n}\n"
);
test!(
selector_list,
".foo {a: b}
.bar {x: y}
// Extending a selector list is equivalent to writing two @extends.
.baz {@extend .foo, .bar}
// The selector list should be parsed after interpolation is resolved.
.bang {@extend .foo #{\",\"} .bar}
",
".foo, .bang, .baz {\n a: b;\n}\n\n.bar, .bang, .baz {\n x: y;\n}\n"
);
test!(
selector_list_after_selector,
"a {
color: red;
}
b,
c {
@extend a;
}",
"a, b,\nc {\n color: red;\n}\n"
);
test!(
selector_list_before_selector,
"b, c {
@extend a;
}
a {
color: red;
}",
"a, b, c {\n color: red;\n}\n"
);
test!(
selector_list_of_selector_pseudo_classes_after_selector,
"foo {
color: black;
}
a:current(foo),
:current(foo) {
@extend foo;
}",
"foo, a:current(foo),\n:current(foo) {\n color: black;\n}\n"
);
test!(
extend_pseudo_selector_class_containing_combinator_without_rhs_selector,
":has(a >) b {
@extend b;
color: red;
}",
":has(a >) b, :has(a >) :has(a >) :has(a >) b, :has(a >) :has(a >) :has(a >) b {\n color: red;\n}\n"
);
test!(
extend_after_target,
".a .b {
c: d;
}
.a.mod1, .a.mod2 {
@extend .a, .b;
}
.a.mod3, .a.mod4 {
@extend .a, .b;
}
.a.mod5, .a.mod6 {
@extend .a, .b;
}",
".a .b, .a .a.mod5, .a .a.mod6, .a .a.mod3, .a .a.mod4, .a .a.mod1, .a .a.mod2 {\n c: d;\n}\n"
);
test!(
parent_selector_as_value_ignores_extend,
"a {
color: &;
}
b {
@extend a;
}",
"a, b {\n color: a;\n}\n"
);
test!(
complex_selector_with_combinator_removed_by_complex_selector_without_combinator,
"c b {
@extend %d;
}
c > b {
@extend %d;
}
%d {
color: red;
}",
"c b {\n color: red;\n}\n"
);
test!(
unification_subselector_of_target_where,
r#"a {b: selector-extend(".c:where(d)", ":where(d)", "d.e")}"#,
"a {\n b: .c:where(d);\n}\n"
);
error!(
extend_optional_keyword_not_complete,
"a {
@extend a !opt;
}",
"Error: Expected \"optional\"."
);
error!(
extend_contains_parent_in_compound_selector,
"a {
@extend &b:c;
}",
"Error: Parent selectors aren't allowed here."
);
error!(
#[ignore = "we do not currently respect this"]
extend_across_media_boundary,
"a {
display: none;
}
@media only screen and (min-width:300px) {
a {
@extend a;
}
}",
"Error: You may not @extend selectors across media queries."
);
error!(
#[ignore = "we do not error for this"]
extend_target_does_not_exist,
"a {
@extend dne;
}",
"Error: The target selector was not found."
);
error!(
#[ignore = "crash"]
extends_self_is_has_invalid_combinator,
"a :is(#a, >) {
@extend a
}",
""
);
// todo: extend_loop (massive test)
// todo: extend tests in folders
// todo: copy all :where extend tests, https://github.com/sass/sass-spec/pull/1783/files