diff --git a/tests/addition.rs b/tests/addition.rs index 4dfce74..dd392c5 100644 --- a/tests/addition.rs +++ b/tests/addition.rs @@ -434,3 +434,8 @@ test!( "a {\n color: (0/0) + (0/0);\n}\n", "a {\n color: NaN;\n}\n" ); +test!(null_plus_null, "a {\n color: null + null;\n}\n", ""); +error!( + color_plus_number, + "a {\n color: red + 1;\n}\n", r#"Error: Undefined operation "red + 1"."# +); diff --git a/tests/custom-property.rs b/tests/custom-property.rs index 27cef26..7406f52 100644 --- a/tests/custom-property.rs +++ b/tests/custom-property.rs @@ -95,3 +95,10 @@ error!( }", r#"Error: Declarations whose names begin with "--" may not be nested"# ); +error!( + empty_value, + "a { + --color:#{null}; + }", + "Error: Custom property values may not be empty." +); diff --git a/tests/division.rs b/tests/division.rs index 1131b60..a52d16e 100644 --- a/tests/division.rs +++ b/tests/division.rs @@ -279,3 +279,11 @@ test!( "a {\n color: foo / calc(1vh + 1px);\n}\n", "a {\n color: foo/calc(1vh + 1px);\n}\n" ); +error!( + color_div_color, + "a {\n color: red / green;\n}\n", r#"Error: Undefined operation "red / green"."# +); +error!( + color_div_number, + "a {\n color: red / 1;\n}\n", r#"Error: Undefined operation "red / 1"."# +); diff --git a/tests/equality.rs b/tests/equality.rs index 4b4e6f4..2b98c01 100644 --- a/tests/equality.rs +++ b/tests/equality.rs @@ -260,3 +260,10 @@ test!( }", "a {\n color: false;\n}\n" ); +test!( + number_equality_is_fuzzy, + "a { + color: .9999999999999999999999999999999==.99999999999999999999999999999998; + }", + "a {\n color: true;\n}\n" +); diff --git a/tests/extend.rs b/tests/extend.rs index 13bc1b0..6765f9d 100644 --- a/tests/extend.rs +++ b/tests/extend.rs @@ -1965,6 +1965,13 @@ error!( }", "" ); +error!( + extend_complex_selector, + "a { + @extend a>b; + }", + "Error: complex selectors may not be extended." +); // todo: extend_loop (massive test) // todo: extend tests in folders diff --git a/tests/for.rs b/tests/for.rs index 7d2fea9..cef16c6 100644 --- a/tests/for.rs +++ b/tests/for.rs @@ -233,3 +233,12 @@ error!( invalid_keyword_after_first_number, "@for $i from 1 FOO 3 {}", "Error: Expected \"to\" or \"through\"." ); +test!( + to_equals_from, + "@for $i from 5 to 5 { + a { + color: red; + } + }", + "" +); diff --git a/tests/functions.rs b/tests/functions.rs index 92366b8..09d93b1 100644 --- a/tests/functions.rs +++ b/tests/functions.rs @@ -409,5 +409,14 @@ test!( }", "a {\n color: before;\n}\n" ); +error!( + function_no_return, + "@function foo() {} + + a { + color: foo(); + }", + "Error: Function finished without @return." +); // todo: return inside if, return inside while, return inside for diff --git a/tests/imports.rs b/tests/imports.rs index 057b0d7..4b6b852 100644 --- a/tests/imports.rs +++ b/tests/imports.rs @@ -283,6 +283,22 @@ fn imports_import_only_scss() { ); } +#[test] +fn imports_sass_file() { + let mut fs = TestFs::new(); + + fs.add_file("a.sass", "a\n\tcolor: red\n"); + + let input = r#" + @import "a"; + "#; + + assert_eq!( + "a {\n color: red;\n}\n", + &grass::from_string(input.to_string(), &grass::Options::default().fs(&fs)).expect(input) + ); +} + #[test] fn imports_absolute_scss() { let mut fs = TestFs::new(); diff --git a/tests/keyframes.rs b/tests/keyframes.rs index 30fb964..150f59e 100644 --- a/tests/keyframes.rs +++ b/tests/keyframes.rs @@ -300,6 +300,15 @@ test!( }", "@keyframes spinner-border {\n to {\n color: red;\n }\n}\na {\n color: red;\n}\n" ); +test!( + percent_selector_leading_plus, + "@keyframes foo { + +5% { + color: red; + } + }", + "@keyframes foo {\n +5% {\n color: red;\n }\n}\n" +); error!( invalid_escape_in_place_of_e, "@keyframes foo { @@ -309,6 +318,15 @@ error!( }", r#"Error: expected "%"."# ); +error!( + percent_selector_no_number_after_e, + "@keyframes foo { + 5e% { + color: red; + } + }", + r#"Error: Expected digit."# +); // todo: span for this // @keyframes foo { diff --git a/tests/mixins.rs b/tests/mixins.rs index 817f059..929c717 100644 --- a/tests/mixins.rs +++ b/tests/mixins.rs @@ -694,3 +694,10 @@ error!( ", "Error: Mixin doesn't accept a content block." ); +error!( + disallows_content_block_to_builtin_mixin, + r#"@use "sass:meta"; + + @include meta.load-css("") {}"#, + "Error: Mixin doesn't accept a content block." +); diff --git a/tests/modulo.rs b/tests/modulo.rs index 2ceed82..55f92a3 100644 --- a/tests/modulo.rs +++ b/tests/modulo.rs @@ -180,3 +180,7 @@ error!( "a {\n color: calc(1rem + 1px) % calc(1rem + 1px);\n}\n", r#"Error: Undefined operation "calc(1rem + 1px) % calc(1rem + 1px)"."# ); +error!( + number_mod_color, + "a {\n color: 5 % red;\n}\n", r#"Error: Undefined operation "5 % red"."# +); diff --git a/tests/plain-css-fn.rs b/tests/plain-css-fn.rs index d738741..437ac06 100644 --- a/tests/plain-css-fn.rs +++ b/tests/plain-css-fn.rs @@ -81,5 +81,12 @@ test!( "a {\n color: or(foo);\n}\n", "a {\n color: or(foo);\n}\n" ); - -// todo: @function and($a) {} a { color: and(foo) } +error!( + denies_keyword_arguments_to_interpolated_function, + "a {\n color: f#{o}o($a: red);\n}\n", + "Error: Plain CSS functions don't support keyword arguments." +); +error!( + denies_function_named_after_keyword, + "@function and($a) {}", "Error: Invalid function name." +); diff --git a/tests/plain-css.rs b/tests/plain-css.rs index b456ce2..d70c318 100644 --- a/tests/plain-css.rs +++ b/tests/plain-css.rs @@ -45,3 +45,121 @@ test!( "a {\n color: not 2;\n color: not true;\n color: not false;\n}\n", grass::Options::default().input_syntax(InputSyntax::Css) ); +error!( + denies_silent_comment, + "// silent", + "Error: Silent comments aren't allowed in plain CSS.", + grass::Options::default().input_syntax(InputSyntax::Css) +); +error!( + denies_function_rule, + "@function foo() { + @return 2; + }", + "Error: This at-rule isn't allowed in plain CSS.", + grass::Options::default().input_syntax(InputSyntax::Css) +); +error!( + denies_content_rule, + "@content", + "Error: This at-rule isn't allowed in plain CSS.", + grass::Options::default().input_syntax(InputSyntax::Css) +); +test!( + allows_media_rule, + "@media (foo) { + a { + color: red; + } + }", + "@media (foo) {\n a {\n color: red;\n }\n}\n", + grass::Options::default().input_syntax(InputSyntax::Css) +); +test!( + allows_var_empty_second_arg, + "a { + color: var(1, ); + }", + "a {\n color: var(1, );\n}\n", + grass::Options::default().input_syntax(InputSyntax::Css) +); +error!( + disallows_empty_second_arg_in_non_var_function, + "a { + color: foo(1, ); + }", + "Error: Expected expression.", + grass::Options::default().input_syntax(InputSyntax::Css) +); +error!( + disallows_if_function, + "a { + color: if(true, a, b); + }", + "Error: This function isn't allowed in plain CSS.", + grass::Options::default().input_syntax(InputSyntax::Css) +); +error!( + disallows_map_get_function, + "a { + color: map-get(true, a, b); + }", + "Error: This function isn't allowed in plain CSS.", + grass::Options::default().input_syntax(InputSyntax::Css) +); +error!( + disallows_plus_operator, + "a { + color: 1 + 2; + }", + "Error: Operators aren't allowed in plain CSS.", + grass::Options::default().input_syntax(InputSyntax::Css) +); +error!( + disallows_parens, + "a { + color: (a b); + }", + "Error: Parentheses aren't allowed in plain CSS.", + grass::Options::default().input_syntax(InputSyntax::Css) +); +error!( + disallows_variable_expr, + "a { + color: $a; + }", + "Error: Sass variables aren't allowed in plain CSS.", + grass::Options::default().input_syntax(InputSyntax::Css) +); +error!( + disallows_parent_selector_expr, + "a { + color: &; + }", + "Error: The parent selector isn't allowed in plain CSS.", + grass::Options::default().input_syntax(InputSyntax::Css) +); +error!( + disallows_unary_plus, + "a { + color: +(1); + }", + "Error: Operators aren't allowed in plain CSS.", + grass::Options::default().input_syntax(InputSyntax::Css) +); +error!( + disallows_unary_minus, + "a { + color: -(1); + }", + "Error: Operators aren't allowed in plain CSS.", + grass::Options::default().input_syntax(InputSyntax::Css) +); +test!( + allows_rgb_function, + "a { + color: rgb(true, a, b); + }", + "a {\n color: rgb(true, a, b);\n}\n", + grass::Options::default().input_syntax(InputSyntax::Css) +); diff --git a/tests/special-functions.rs b/tests/special-functions.rs index 4b41807..c9b274d 100644 --- a/tests/special-functions.rs +++ b/tests/special-functions.rs @@ -251,6 +251,35 @@ test!( "a {\n color: calc(1 + #{c});\n}\n", "a {\n color: calc(1 + c);\n}\n" ); +test!( + progid_no_colon, + "a {\n color: progid;\n}\n", + "a {\n color: progid;\n}\n" +); +test!( + progid_no_colon_with_args, + "a {\n color: progid();\n}\n", + "a {\n color: progid();\n}\n" +); +test!( + can_add_s_and_ms, + "a {\n color: calc(1s + 1ms);\n}\n", + "a {\n color: 1.001s;\n}\n" +); +test!( + can_add_hz_and_khz, + "a {\n color: calc(1Hz + 1kHz);\n}\n", + "a {\n color: 1001Hz;\n}\n" +); +test!( + can_add_dpi_and_dppx, + "a {\n color: calc(1dpi + 1dppx);\n}\n", + "a {\n color: 97dpi;\n}\n" +); +error!( + nothing_after_last_arg, + "a { color: calc(1 + 1", r#"Error: expected "+", "-", "*", "/", or ")"."# +); error!( progid_nothing_after, "a { color: progid:", "Error: expected \"(\"." diff --git a/tests/strings.rs b/tests/strings.rs index 48bef7d..1f6fb81 100644 --- a/tests/strings.rs +++ b/tests/strings.rs @@ -239,3 +239,8 @@ test!( "a {\n color: \"#foo\";\n}\n", "a {\n color: \"#foo\";\n}\n" ); +test!( + escaped_newline_inside_string, + "a {\n color: \"f\\\n oo\";\n}\n", + "a {\n color: \"f oo\";\n}\n" +); diff --git a/tests/subtraction.rs b/tests/subtraction.rs index 854caca..d25160a 100644 --- a/tests/subtraction.rs +++ b/tests/subtraction.rs @@ -284,14 +284,19 @@ test!( ); test!( sub_nan_left, - "a {\n left:0/0-0;\n}\n", + "a {\n left: (0/0) - 0;\n}\n", "a {\n left: NaN;\n}\n" ); test!( sub_nan_right, - "a {\n left:0-0/0;\n}\n", + "a {\n left: 0 - (0/0);\n}\n", "a {\n left: NaN;\n}\n" ); +test!( + true_minus_null, + "a {\n color: true - null;\n}\n", + "a {\n color: true-;\n}\n" +); error!( number_minus_color, "a {\n color: 1 - #abc;\n}\n", "Error: Undefined operation \"1 - #abc\"." @@ -332,3 +337,11 @@ error!( num_minus_calculation, "a {color: 1 - calc(1rem + 1px);}", r#"Error: Undefined operation "1 - calc(1rem + 1px)"."# ); +error!( + color_minus_number, + "a {color: red - 1;}", r#"Error: Undefined operation "red - 1"."# +); +error!( + map_minus_null, + "a {color: inspect((a: b) - null);}", r#"Error: (a: b) isn't a valid CSS value."# +); diff --git a/tests/supports.rs b/tests/supports.rs index e46718d..44072f6 100644 --- a/tests/supports.rs +++ b/tests/supports.rs @@ -137,3 +137,25 @@ test!( "" ); test!(supports_empty_body, "@supports (a: b) {}", ""); +test!( + does_not_simplify_calculation_in_args, + "@supports (calc(1 + 1)) { + a { + color: red; + } + }", + "@supports (calc(1 + 1)) {\n a {\n color: red;\n }\n}\n" +); +error!( + supports_inside_declaration_body, + "@mixin foo() { + @supports (foo) {} + } + + a { + color: { + @include foo(); + } + }", + "Error: Supports rules may not be used within nested declarations." +); diff --git a/tests/units.rs b/tests/units.rs index ac40228..dd0172c 100644 --- a/tests/units.rs +++ b/tests/units.rs @@ -212,6 +212,16 @@ test!( "a {\n color: unit((1rem/1px) / 1vh);\n}\n", "a {\n color: \"rem/px*vh\";\n}\n" ); +test!( + complex_unit_empty_numerator_single_denom, + "a {\n color: unit(1 / 1px);\n}\n", + "a {\n color: \"px^-1\";\n}\n" +); +test!( + complex_unit_empty_numerator_multiple_denom, + "a {\n color: unit(1 / (1px*1rem));\n}\n", + "a {\n color: \"(px*rem)^-1\";\n}\n" +); error!( display_single_div_with_none_numerator, "a {\n color: (1 / 1em);\n}\n", "Error: 1em^-1 isn't a valid CSS value." @@ -243,6 +253,47 @@ error!( display_single_div_with_none_numerator_percent, "a {\n color: (35 / 7%);\n}\n", "Error: 5%^-1 isn't a valid CSS value." ); +test!( + /// Verify the display implementation of all special-cased units + render_units, + "a { + color: 1px; + color: 1mm; + color: 1in; + color: 1cm; + color: 1q; + color: 1pt; + color: 1pc; + color: 1em; + color: 1rem; + color: 1lh; + color: 1%; + color: 1ex; + color: 1ch; + color: 1cap; + color: 1ic; + color: 1rlh; + color: 1vw; + color: 1vh; + color: 1vmin; + color: 1vmax; + color: 1vi; + color: 1vb; + color: 1deg; + color: 1grad; + color: 1rad; + color: 1turn; + color: 1s; + color: 1ms; + color: 1Hz; + color: 1kHz; + color: 1dpi; + color: 1dpcm; + color: 1dppx; + color: 1fr; + color: 1foo; + }", "a {\n color: 1px;\n color: 1mm;\n color: 1in;\n color: 1cm;\n color: 1q;\n color: 1pt;\n color: 1pc;\n color: 1em;\n color: 1rem;\n color: 1lh;\n color: 1%;\n color: 1ex;\n color: 1ch;\n color: 1cap;\n color: 1ic;\n color: 1rlh;\n color: 1vw;\n color: 1vh;\n color: 1vmin;\n color: 1vmax;\n color: 1vi;\n color: 1vb;\n color: 1deg;\n color: 1grad;\n color: 1rad;\n color: 1turn;\n color: 1s;\n color: 1ms;\n color: 1Hz;\n color: 1kHz;\n color: 1dpi;\n color: 1dpcm;\n color: 1dppx;\n color: 1fr;\n color: 1foo;\n}\n" +); macro_rules! test_unit_addition { ($u1:ident, $u2:ident, $out:literal) => { diff --git a/tests/unknown-at-rule.rs b/tests/unknown-at-rule.rs index 3ed6d6b..91b631f 100644 --- a/tests/unknown-at-rule.rs +++ b/tests/unknown-at-rule.rs @@ -105,5 +105,18 @@ test!( "a {\n @box-shadow : $btn-focus-box-shadow, // $btn-active-box-shadow;;\n}\n" ); test!(contains_multiline_comment, "@foo /**/;\n", "@foo;\n"); +error!( + unknown_at_rule_inside_declaration_body, + "@mixin foo { + @foo; + } + + a { + color: { + @include foo; + } + }", + "Error: At-rules may not be used within nested declarations." +); // todo: test scoping in rule diff --git a/tests/use.rs b/tests/use.rs index 1db3072..7e8c489 100644 --- a/tests/use.rs +++ b/tests/use.rs @@ -52,6 +52,10 @@ error!( use_empty_string, r#"@use "";"#, r#"Error: The default namespace "" is not a valid Sass identifier."# ); +error!( + configure_builtin_module, + r#"@use "sass:math" with ($e: 5);"#, r#"Error: Built-in modules can't be configured."# +); test!( use_as, "@use \"sass:math\" as foo;