grass/tests/mixins.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

697 lines
16 KiB
Rust

#[macro_use]
mod macros;
test!(
basic_mixin,
"@mixin a {\n color: red;\n}\n\nb {\n @include a;\n}\n",
"b {\n color: red;\n}\n"
);
test!(empty_mixin, "@mixin a {}\n\nb {\n @include a;\n}\n", "");
test!(
just_a_comment,
"@mixin foo() {\n /* begin foo */\n}\n\na {\n @include foo();\n}\n",
"a {\n /* begin foo */\n}\n"
);
test!(
mixin_two_styles,
"@mixin a {\n color: red;\n color: blue;\n}\n\nb {\n @include a;\n}\n",
"b {\n color: red;\n color: blue;\n}\n"
);
test!(
mixin_ruleset,
"@mixin a {\n b {\n color: red;\n }\n}\nb {\n @include a;\n}\n",
"b b {\n color: red;\n}\n"
);
test!(
mixin_two_rulesets,
"@mixin a {\n b {\n color: red;\n }\n c {\n color: blue;\n }\n}\nd {\n @include a;\n}\n",
"d b {\n color: red;\n}\nd c {\n color: blue;\n}\n"
);
test!(
mixin_ruleset_and_style,
"@mixin a {\n b {\n color: red;\n }\n color: blue;\n}\nd {\n @include a;\n}\n",
"d {\n color: blue;\n}\nd b {\n color: red;\n}\n"
);
test!(
mixin_style_and_ruleset,
"@mixin a {\n color: blue;\n b {\n color: red;\n}\n}\nd {\n @include a;\n}\n",
"d {\n color: blue;\n}\nd b {\n color: red;\n}\n"
);
test!(
mixin_nested_rulesets,
"@mixin a {\n b {\n c {\n color: red;\n}\n}\n}\nd {\n @include a;\n}\n",
"d b c {\n color: red;\n}\n"
);
test!(
mixin_removes_empty_ruleset,
"@mixin a {\n color: red; b {\n}\n}\nd {\n @include a;\n}\n",
"d {\n color: red;\n}\n"
);
test!(
mixin_variable_scope_one_ruleset,
"@mixin a {\n $a: blue;\nb {\n $a: red;\n} color: $a\n}\nd {\n @include a;\n}\n",
"d {\n color: red;\n}\n"
);
test!(
mixin_no_args,
"@mixin a {\n color: red;\n}\nd {\n @include a();\n}\n",
"d {\n color: red;\n}\n"
);
test!(
mixin_single_arg,
"@mixin a($b) {\n color: $b;\n}\nd {\n @include a(red);\n}\n",
"d {\n color: red;\n}\n"
);
test!(
mixin_two_args,
"@mixin a($b, $c) {\n color: $b;\n color: $c\n}\nd {\n @include a(red, blue);\n}\n",
"d {\n color: red;\n color: blue;\n}\n"
);
test!(
mixin_arg_trailing_comma,
"@mixin a($b, $c,) {\n color: $b;\n color: $c\n}\nd {\n @include a(red, blue);\n}\n",
"d {\n color: red;\n color: blue;\n}\n"
);
test!(
mixin_property_interpolation,
"@mixin a($b) {\n #{$b}: red;\n}\nd {\n @include a(color);\n}\n",
"d {\n color: red;\n}\n"
);
test!(
mixin_style_interpolation,
"@mixin a($b) {\n color: #{$b};\n}\nd {\n @include a(red);\n}\n",
"d {\n color: red;\n}\n"
);
test!(
mixin_simple_default_value,
"@mixin a($b: red) {\n color: $b;\n}\nd {\n @include a;\n}\n",
"d {\n color: red;\n}\n"
);
test!(
mixin_second_value_default,
"@mixin a($a, $b: blue) {\n color: $a $b;\n}\nd {\n @include a(red);\n}\n",
"d {\n color: red blue;\n}\n"
);
test!(
mixin_two_default_values,
"@mixin a($a: red, $b: blue) {\n color: $a $b;\n}\nd {\n @include a;\n}\n",
"d {\n color: red blue;\n}\n"
);
test!(
mixin_override_default_value_positionally,
"@mixin a($a: red) {\n color: $a;\n}\nd {\n @include a(blue);\n}\n",
"d {\n color: blue;\n}\n"
);
test!(
mixin_keyword_arg,
"@mixin a($a) {\n color: $a;\n}\nd {\n @include a($a: blue);\n}\n",
"d {\n color: blue;\n}\n"
);
test!(
mixin_keyword_arg_override_default,
"@mixin a($a: red) {\n color: $a;\n}\nd {\n @include a($a: blue);\n}\n",
"d {\n color: blue;\n}\n"
);
test!(
mixin_keyword_applies_to_second_arg,
"@mixin a($a: red, $b) {\n color: $a $b;\n}\nd {\n @include a($b: blue);\n}\n",
"d {\n color: red blue;\n}\n"
);
test!(
mixin_two_keywords,
"@mixin a($a, $b) {\n color: $a $b;\n}\nd {\n @include a($a: red, $b: blue);\n}\n",
"d {\n color: red blue;\n}\n"
);
test!(
mixin_two_keywords_wrong_direction,
"@mixin a($a, $b) {\n color: $a $b;\n}\nd {\n @include a($b: blue, $a: red);\n}\n",
"d {\n color: red blue;\n}\n"
);
test!(
variable_in_call_args,
"@mixin a($a) {\n color: $a;\n}\nd {\n $c: red;\n @include a($c);\n}\n",
"d {\n color: red;\n}\n"
);
test!(
comment_before_positional_call_arg,
"@mixin a($a) {\n color: $a;\n}\nd {\n @include a(/*foo*/red);\n}\n",
"d {\n color: red;\n}\n"
);
test!(
comment_after_positional_call_arg,
"@mixin a($a) {\n color: $a;\n}\nd {\n @include a(red/*foo*/);\n}\n",
"d {\n color: red;\n}\n"
);
test!(
comment_before_keyword_call_arg_val,
"@mixin a($a) {\n color: $a;\n}\nd {\n @include a($a: /*foo*/red);\n}\n",
"d {\n color: red;\n}\n"
);
test!(
comment_after_keyword_call_arg_val,
"@mixin a($a) {\n color: $a;\n}\nd {\n @include a($a: red/*foo*/);\n}\n",
"d {\n color: red;\n}\n"
);
test!(
comment_before_keyword_call_arg_name,
"@mixin a($a) {\n color: $a;\n}\nd {\n @include a(/*foo*/$a: red);\n}\n",
"d {\n color: red;\n}\n"
);
test!(
comment_after_keyword_call_arg_name,
"@mixin a($a) {\n color: $a;\n}\nd {\n @include a($a/*foo*/: red);\n}\n",
"d {\n color: red;\n}\n"
);
test!(
toplevel_include,
"@mixin a {\n a {\n color: red;\n }\n}\n\n@include a;\n",
"a {\n color: red;\n}\n"
);
test!(
include_list,
"@mixin foo($x) {\n color: $x;\n}\na {\n @include foo(0px 0px 0px 0px #ef8086 inset !important);\n}\n",
"a {\n color: 0px 0px 0px 0px #ef8086 inset !important;\n}\n"
);
test!(
content_without_variable,
"@mixin foo {\n @content;\n}\n\na {\n @include foo {\n color: red;\n }\n}\n",
"a {\n color: red;\n}\n"
);
test!(
content_with_variable,
"@mixin foo($a) {\n @content;\n}\n\na {\n @include foo(red) {\n color: red;\n }\n}\n",
"a {\n color: red;\n}\n"
);
test!(
mixin_style_does_not_end_with_semicolon,
"@mixin foo {\n color: red\n}\n\na {\n @include foo;\n}\n",
"a {\n color: red;\n}\n"
);
test!(
args_hyphen_arg_allows_underscore,
"@mixin foo($a-b) {\n color: $a-b;\n color: $a_b;\n}\na {\n @include foo($a_b: a);\n @include foo($a-b: a);\n}\n",
"a {\n color: a;\n color: a;\n color: a;\n color: a;\n}\n"
);
test!(
args_underscore_arg_allows_hyphen,
"@mixin foo($a_b) {\n color: $a-b;\n color: $a_b;\n}\na {\n @include foo($a_b: a);\n @include foo($a-b: a);\n}\n",
"a {\n color: a;\n color: a;\n color: a;\n color: a;\n}\n"
);
test!(
control_flow_in_content,
"@mixin foo {\n @content;\n}\n\na {\n @include foo {@if true {color: red;}}\n}\n",
"a {\n color: red;\n}\n"
);
test!(
content_in_control_flow,
"@mixin foo() {\n @if true {\n @content;\n }\n}\n\na {\n @include foo {\n color: red;\n }\n}\n",
"a {\n color: red;\n}\n"
);
test!(
content_inside_unknown_at_rule,
"@mixin foo() {\n @foo (max-width: max) {\n @content;\n }\n}\n\na {\n @include foo {\n color: red;\n }\n}\n",
"@foo (max-width: max) {\n a {\n color: red;\n }\n}\n"
);
test!(
content_inside_media,
"@mixin foo() {\n @media (max-width: max) {\n @content;\n }\n}\n\na {\n @include foo {\n color: red;\n }\n}\n",
"@media (max-width: max) {\n a {\n color: red;\n }\n}\n"
);
error!(
function_inside_mixin,
"@mixin foo() {\n @function bar() {\n @return foo;\n }\n}\n\na {\n @include foo {\n color: red;\n }\n}\n",
"Error: Mixins may not contain function declarations."
);
error!(
content_inside_control_flow_outside_mixin,
"a {\n @if true {\n @content;\n }\n}\n",
"Error: @content is only allowed within mixin declarations."
);
error!(
undefined_mixin,
"a {@include foo;}", "Error: Undefined mixin."
);
error!(
body_missing_closing_curly_brace,
"@mixin foo() {", "Error: expected \"}\"."
);
test!(
include_empty_args_no_semicolon,
"@mixin c {}\n\na {\n @include c()\n}\n",
""
);
test!(
local_variable_declared_before_mixin_is_still_in_scope,
"@mixin foo {}\n\na {\n $a: red;\n @include foo;\n color: $a;\n}\n",
"a {\n color: red;\n}\n"
);
test!(
empty_content_args,
"@mixin foo {
@content()
}
a {
@include foo {
color: red;
};
}",
"a {\n color: red;\n}\n"
);
test!(
empty_content_args_using_empty_args,
"@mixin foo {
@content()
}
a {
@include foo using () {
color: red;
};
}",
"a {\n color: red;\n}\n"
);
test!(
content_using_one_arg,
"@mixin foo {
@content(red)
}
a {
@include foo using ($a) {
color: $a;
}
}",
"a {\n color: red;\n}\n"
);
test!(
multiple_content_using_different_args,
"@mixin foo {
@content(1);
@content(2);
}
@mixin bar {
@include foo using ($a) {
color: $a
}
}
a {
@include bar;
}",
"a {\n color: 1;\n color: 2;\n}\n"
);
test!(
chained_content,
"@mixin foo {
@content;
}
@mixin bar {
@include foo {
@content;
}
}
a {
@include bar {
color: red;
}
}",
"a {\n color: red;\n}\n"
);
test!(
content_can_access_local_variables,
"@mixin foo {
@content;
}
a {
$bar: red;
@include foo {
color: $bar;
}
}",
"a {\n color: red;\n}\n"
);
error!(
content_using_too_many_args,
"@mixin foo {
@content(red, blue)
}
a {
@include foo using ($a) {
color: $a;
}
}",
"Error: Only 1 argument allowed, but 2 were passed."
);
error!(
content_using_too_few_args,
"@mixin foo {
@content()
}
a {
@include foo using ($a) {
color: $a;
}
}",
"Error: Missing argument $a."
);
test!(
inner_mixin_can_have_scope_modified,
"a {
$a: red;
@mixin foo {
color: $a;
}
$a: green;
@include foo;
}",
"a {\n color: green;\n}\n"
);
test!(
redeclaration_in_inner_scope,
"@mixin foo {
color: foo;
}
a {
@include foo();
@mixin foo {
color: bar;
}
a {
@mixin foo {
color: baz;
}
}
@include foo();
}",
"a {\n color: foo;\n color: bar;\n}\n"
);
test!(
three_depth_of_content,
"@mixin foo($arg) {
@include bar {
color: $arg;
}
}
@mixin bar {
@include baz {
@content;
}
}
@mixin baz {
@content;
}
@mixin font-size($value) {
@include foo($value);
}
a {
@include font-size(1rem);
}",
"a {\n color: 1rem;\n}\n"
);
test!(
can_access_global_variables_set_after_other_include,
"$x: true;
@mixin foobar() {
@if $x {
$x: false !global;
color: foo;
}
@else {
$x: true !global;
color: bar;
}
}
a {
@include foobar();
$x: true !global;
@include foobar();
}",
"a {\n color: foo;\n color: foo;\n}\n"
);
test!(
can_access_variables_declared_before_content,
"@mixin foo {
$a: red;
@content;
color: $a;
}
a {
@include foo;
}",
"a {\n color: red;\n}\n"
);
test!(
content_contains_variable_declared_in_outer_scope_not_declared_at_root,
"a {
$a: red;
@mixin foo {
@content;
}
@include foo {
color: $a;
}
}",
"a {\n color: red;\n}\n"
);
test!(
content_contains_variable_declared_in_outer_scope_declared_at_root,
"@mixin foo {
@content;
}
a {
$a: red;
@include foo {
color: $a;
}
}",
"a {\n color: red;\n}\n"
);
test!(
content_contains_variable_declared_in_outer_scope_not_declared_at_root_and_modified,
"a {
$a: wrong;
@mixin foo {
$a: correct;
@content;
}
@include foo {
color: $a;
}
}",
"a {\n color: correct;\n}\n"
);
test!(
content_contains_variable_declared_in_outer_scope_declared_at_root_and_modified,
"@mixin foo {
$a: wrong;
@content;
}
a {
$a: correct;
@include foo {
color: $a;
}
}",
"a {\n color: correct;\n}\n"
);
test!(
content_default_arg_value_no_parens,
"a {
@mixin foo {
@content;
}
@include foo using ($a: red) {
color: $a;
}
}",
"a {\n color: red;\n}\n"
);
test!(
space_between_content_and_args,
"space-after-content {
@mixin mixin {
@content /**/ (value1, value2);
}
@include mixin using ($arg1, $arg2) {
arg1: $arg1;
arg2: $arg2;
}
}",
"space-after-content {\n arg1: value1;\n arg2: value2;\n}\n"
);
test!(
space_between_mixin_and_args,
"@mixin foo /**/ () /**/ {
color: red;
}
a {
@include foo;
}",
"a {\n color: red;\n}\n"
);
test!(
mixin_cant_affect_scope_in_which_it_was_included,
"@mixin test {
$a: wrong;
}
a {
$a: correct;
@include test;
color: $a;
}",
"a {\n color: correct;\n}\n"
);
test!(
content_block_has_two_rulesets,
"@mixin foo() {
@content;
}
@include foo {
a {
color: red;
}
b {
color: red;
}
}",
"a {\n color: red;\n}\n\nb {\n color: red;\n}\n"
);
test!(
mixin_has_two_rulesets,
"@mixin foo() {
a {
display: none;
}
b {
display: block;
}
}
@include foo();",
"a {\n display: none;\n}\n\nb {\n display: block;\n}\n"
);
test!(
sass_spec__188_test_mixin_content,
"$color: blue;
@mixin context($class, $color: red) {
.#{$class} {
background-color: $color;
@content;
border-color: $color;
}
}
@include context(parent) {
@include context(child, $color: yellow) {
color: $color;
}
}",
".parent {\n background-color: red;\n border-color: red;\n}\n.parent .child {\n background-color: yellow;\n color: blue;\n border-color: yellow;\n}\n"
);
test!(
sass_spec__mixin_environment_locality,
r#"// The "$var" variable should only be set locally, despite being in the same
// mixin each time.
@mixin with-local-variable($recurse) {
$var: before;
@if ($recurse) {
@include with-local-variable($recurse: false);
}
var: $var;
$var: after;
}
.environment-locality {
@include with-local-variable($recurse: true);
}"#,
".environment-locality {\n var: before;\n var: before;\n}\n"
);
error!(
mixin_in_function,
"@function foo() {
@mixin bar {}
}
a {
color: foo();
}
",
"Error: This at-rule is not allowed here."
);
error!(
mixin_in_mixin,
"@mixin foo {
@mixin bar {}
}
a {
@include foo;
}
",
"Error: Mixins may not contain mixin declarations."
);
error!(
mixin_in_control_directives,
"@if true {
@mixin bar {}
}",
"Error: Mixins may not be declared in control directives."
);
error!(
does_not_allow_interpolation_in_name_of_declaration,
"@mixin n#{a}me {
color: red;
}
a {
@include name;
}",
"Error: expected \"{\"."
);
error!(
disallows_content_block_when_mixin_has_no_content_block,
"@mixin foo () {}
@include foo {}
",
"Error: Mixin doesn't accept a content block."
);