improve code coverage
This commit is contained in:
parent
5889859968
commit
40d2aa232a
@ -11,32 +11,20 @@ pub(crate) fn length(mut args: ArgumentResult, visitor: &mut Visitor) -> SassRes
|
||||
pub(crate) fn nth(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
|
||||
args.max_args(2)?;
|
||||
let mut list = args.get_err(0, "list")?.as_list();
|
||||
let (n, unit) = match args.get_err(1, "n")? {
|
||||
Value::Dimension(SassNumber {
|
||||
num: n, unit: u, ..
|
||||
}) if n.is_nan() => {
|
||||
return Err((format!("$n: NaN{} is not an int.", u), args.span()).into())
|
||||
}
|
||||
Value::Dimension(SassNumber { num, unit, .. }) => (num, unit),
|
||||
v => {
|
||||
return Err((
|
||||
format!("$n: {} is not a number.", v.inspect(args.span())?),
|
||||
args.span(),
|
||||
)
|
||||
.into())
|
||||
}
|
||||
};
|
||||
let index = args
|
||||
.get_err(1, "n")?
|
||||
.assert_number_with_name("n", args.span())?;
|
||||
|
||||
if n.is_zero() {
|
||||
if index.num.is_zero() {
|
||||
return Err(("$n: List index may not be 0.", args.span()).into());
|
||||
}
|
||||
|
||||
if n.abs() > Number::from(list.len()) {
|
||||
if index.num.abs() > Number::from(list.len()) {
|
||||
return Err((
|
||||
format!(
|
||||
"$n: Invalid index {}{} for a list with {} elements.",
|
||||
n.inspect(),
|
||||
unit,
|
||||
index.num.inspect(),
|
||||
index.unit,
|
||||
list.len()
|
||||
),
|
||||
args.span(),
|
||||
@ -44,12 +32,13 @@ pub(crate) fn nth(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult
|
||||
.into());
|
||||
}
|
||||
|
||||
Ok(list.remove(if n.is_positive() {
|
||||
let index = n.assert_int_with_name("n", args.span())? - 1;
|
||||
debug_assert!(index > -1);
|
||||
index as usize
|
||||
let index_int = index.assert_int_with_name("n", args.span())?;
|
||||
|
||||
Ok(list.remove(if index.num.is_positive() {
|
||||
debug_assert!(index_int > 0);
|
||||
index_int as usize - 1
|
||||
} else {
|
||||
list.len() - n.abs().assert_int_with_name("n", args.span())? as usize
|
||||
list.len() - index_int.unsigned_abs() as usize
|
||||
}))
|
||||
}
|
||||
|
||||
@ -73,34 +62,24 @@ pub(crate) fn set_nth(mut args: ArgumentResult, visitor: &mut Visitor) -> SassRe
|
||||
Value::Map(m) => (m.as_list(), ListSeparator::Comma, Brackets::None),
|
||||
v => (vec![v], ListSeparator::Undecided, Brackets::None),
|
||||
};
|
||||
let (n, unit) = match args.get_err(1, "n")? {
|
||||
Value::Dimension(SassNumber {
|
||||
num: n, unit: u, ..
|
||||
}) if n.is_nan() => {
|
||||
return Err((format!("$n: NaN{} is not an int.", u), args.span()).into())
|
||||
}
|
||||
Value::Dimension(SassNumber { num, unit, .. }) => (num, unit),
|
||||
v => {
|
||||
return Err((
|
||||
format!("$n: {} is not a number.", v.inspect(args.span())?),
|
||||
args.span(),
|
||||
)
|
||||
.into())
|
||||
}
|
||||
};
|
||||
let index = args
|
||||
.get_err(1, "n")?
|
||||
.assert_number_with_name("n", args.span())?;
|
||||
|
||||
if n.is_zero() {
|
||||
if index.num.is_zero() {
|
||||
return Err(("$n: List index may not be 0.", args.span()).into());
|
||||
}
|
||||
|
||||
let index_int = index.assert_int_with_name("n", args.span())?;
|
||||
|
||||
let len = list.len();
|
||||
|
||||
if n.abs() > Number::from(len) {
|
||||
if index.num.abs() > Number::from(len) {
|
||||
return Err((
|
||||
format!(
|
||||
"$n: Invalid index {}{} for a list with {} elements.",
|
||||
n.inspect(),
|
||||
unit,
|
||||
index.num.inspect(),
|
||||
index.unit,
|
||||
len
|
||||
),
|
||||
args.span(),
|
||||
@ -108,16 +87,12 @@ pub(crate) fn set_nth(mut args: ArgumentResult, visitor: &mut Visitor) -> SassRe
|
||||
.into());
|
||||
}
|
||||
|
||||
if n.is_decimal() {
|
||||
return Err((format!("$n: {} is not an int.", n.inspect()), args.span()).into());
|
||||
}
|
||||
|
||||
let val = args.get_err(2, "value")?;
|
||||
|
||||
if n.is_positive() {
|
||||
list[n.assert_int_with_name("n", args.span())? as usize - 1] = val;
|
||||
if index_int.is_positive() {
|
||||
list[index_int as usize - 1] = val;
|
||||
} else {
|
||||
list[len - n.abs().assert_int_with_name("n", args.span())? as usize] = val;
|
||||
list[len - index_int.unsigned_abs() as usize] = val;
|
||||
}
|
||||
|
||||
Ok(Value::List(list, sep, brackets))
|
||||
|
@ -99,8 +99,9 @@ pub(crate) fn random(mut args: ArgumentResult, visitor: &mut Visitor) -> SassRes
|
||||
)));
|
||||
}
|
||||
|
||||
let limit = limit.assert_number_with_name("limit", args.span())?.num;
|
||||
let limit = limit.assert_number_with_name("limit", args.span())?;
|
||||
let limit_int = limit.assert_int_with_name("limit", args.span())?;
|
||||
let limit = limit.num;
|
||||
|
||||
if limit.is_one() {
|
||||
return Ok(Value::Dimension(SassNumber::new_unitless(1.0)));
|
||||
|
@ -152,7 +152,7 @@ pub(crate) fn str_insert(mut args: ArgumentResult, visitor: &mut Visitor) -> Sas
|
||||
.get_err(2, "index")?
|
||||
.assert_number_with_name("index", span)?;
|
||||
index.assert_no_units("index", span)?;
|
||||
let index_int = index.num.assert_int_with_name("index", span)?;
|
||||
let index_int = index.assert_int_with_name("index", span)?;
|
||||
|
||||
if s1.is_empty() {
|
||||
return Ok(Value::String(substr, quotes));
|
||||
|
@ -309,10 +309,6 @@ pub(crate) trait StylesheetParser<'a>: BaseParser<'a> + Sized {
|
||||
}
|
||||
|
||||
fn parse_at_root_query(&mut self) -> SassResult<Interpolation> {
|
||||
if self.toks_mut().next_char_is('#') {
|
||||
return self.parse_single_interpolation();
|
||||
}
|
||||
|
||||
let mut buffer = Interpolation::new();
|
||||
self.expect_char('(')?;
|
||||
buffer.add_char('(');
|
||||
|
@ -116,21 +116,6 @@ impl Number {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assert_int_with_name(self, name: &'static str, span: Span) -> SassResult<i64> {
|
||||
match fuzzy_as_int(self.0) {
|
||||
Some(i) => Ok(i),
|
||||
None => Err((
|
||||
format!(
|
||||
"${name}: {} is not an int.",
|
||||
self.to_string(false),
|
||||
name = name,
|
||||
),
|
||||
span,
|
||||
)
|
||||
.into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn round(self) -> Self {
|
||||
Self(self.0.round())
|
||||
}
|
||||
@ -147,10 +132,6 @@ impl Number {
|
||||
Self(self.0.abs())
|
||||
}
|
||||
|
||||
pub fn is_decimal(self) -> bool {
|
||||
self.0.fract() != 0.0
|
||||
}
|
||||
|
||||
pub fn clamp(self, min: f64, max: f64) -> Self {
|
||||
Number(min.max(self.0.min(max)))
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ use crate::{
|
||||
Options,
|
||||
};
|
||||
|
||||
use super::Number;
|
||||
use super::{fuzzy_as_int, Number};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct SassNumber {
|
||||
@ -176,6 +176,21 @@ impl SassNumber {
|
||||
self.assert_bounds_with_unit(name, min, max, &self.unit, span)
|
||||
}
|
||||
|
||||
pub fn assert_int_with_name(&self, name: &'static str, span: Span) -> SassResult<i64> {
|
||||
match fuzzy_as_int(self.num.0) {
|
||||
Some(i) => Ok(i),
|
||||
None => Err((
|
||||
format!(
|
||||
"${name}: {} is not an int.",
|
||||
inspect_number(self, &Options::default(), span)?,
|
||||
name = name,
|
||||
),
|
||||
span,
|
||||
)
|
||||
.into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assert_bounds_with_unit(
|
||||
&self,
|
||||
name: &str,
|
||||
|
@ -171,3 +171,5 @@ test!(
|
||||
"a { /**/ }\n"
|
||||
);
|
||||
test!(silent_comment_as_child, "a {\n// silent\n}\n", "");
|
||||
test!(single_hash_in_loud_comment, "/*#*/", "/*#*/\n");
|
||||
error!(unclosed_loud_comment, "/*", "Error: expected more input.");
|
||||
|
@ -128,3 +128,21 @@ test!(
|
||||
"a{color:0;color:0}",
|
||||
grass::Options::default().style(grass::OutputStyle::Compressed)
|
||||
);
|
||||
test!(
|
||||
color_can_be_three_hex,
|
||||
"a {\n color: white;\n}\n",
|
||||
"a{color:#fff}",
|
||||
grass::Options::default().style(grass::OutputStyle::Compressed)
|
||||
);
|
||||
test!(
|
||||
color_cant_be_three_hex_but_hex_is_shorter,
|
||||
"a {\n color: aquamarine;\n}\n",
|
||||
"a{color:#7fffd4}",
|
||||
grass::Options::default().style(grass::OutputStyle::Compressed)
|
||||
);
|
||||
test!(
|
||||
slash_list,
|
||||
"@use 'sass:list'; a {\n color: list.slash(a, b, c);\n}\n",
|
||||
"a{color:a/b/c}",
|
||||
grass::Options::default().style(grass::OutputStyle::Compressed)
|
||||
);
|
||||
|
@ -581,6 +581,15 @@ test!(
|
||||
r#"@import "a" b c d(e) supports(f: g) h i j(k) l m (n: o), (p: q);"#,
|
||||
"@import \"a\" b c d(e) supports(f: g) h i j(k) l m (n: o), (p: q);\n"
|
||||
);
|
||||
test!(
|
||||
import_supports_condition_with_paren,
|
||||
r#"@import "a" supports(a( /**/ ));"#,
|
||||
"@import \"a\" supports(a( /**/ ));\n"
|
||||
);
|
||||
error!(
|
||||
import_supports_condition_non_ident,
|
||||
r#"@import "a" supports(1a)"#, "Error: expected \":\"."
|
||||
);
|
||||
error!(unclosed_single_quote, r#"@import '"#, "Error: Expected '.");
|
||||
error!(unclosed_double_quote, r#"@import ""#, "Error: Expected \".");
|
||||
error!(
|
||||
|
@ -86,6 +86,21 @@ test!(
|
||||
"a {\n color: inspect([(1, 2), (3, 4)]);\n}\n",
|
||||
"a {\n color: [(1, 2), (3, 4)];\n}\n"
|
||||
);
|
||||
test!(
|
||||
inspect_map_with_bracketed_key_and_value,
|
||||
"a {\n color: inspect(([a, b]: [c, d]));\n}\n",
|
||||
"a {\n color: ([a, b]: [c, d]);\n}\n"
|
||||
);
|
||||
test!(
|
||||
inspect_map_with_comma_separated_key_and_value,
|
||||
"a {\n color: inspect(((a, b): (c, d)));\n}\n",
|
||||
"a {\n color: ((a, b): (c, d));\n}\n"
|
||||
);
|
||||
test!(
|
||||
inspect_slash_list_singleton,
|
||||
"a {\n color: inspect(join((a,), (), slash));\n}\n",
|
||||
"a {\n color: (a/);\n}\n"
|
||||
);
|
||||
test!(
|
||||
inspect_empty_list,
|
||||
"a {\n color: inspect(())\n}\n",
|
||||
|
@ -528,3 +528,110 @@ error!(
|
||||
empty_list_is_invalid,
|
||||
"a {\n color: ();\n}\n", "Error: () isn't a valid CSS value."
|
||||
);
|
||||
test!(
|
||||
is_bracketed_empty_bracket_list,
|
||||
"a {\n color: is-bracketed([]);\n}\n",
|
||||
"a {\n color: true;\n}\n"
|
||||
);
|
||||
test!(
|
||||
is_bracketed_bracket_list_containing_space_list,
|
||||
"a {\n color: is-bracketed([a b]);\n}\n",
|
||||
"a {\n color: true;\n}\n"
|
||||
);
|
||||
test!(
|
||||
is_bracketed_bracket_list_containing_comma_list,
|
||||
"a {\n color: is-bracketed([a, b]);\n}\n",
|
||||
"a {\n color: true;\n}\n"
|
||||
);
|
||||
test!(
|
||||
is_bracketed_space_list,
|
||||
"a {\n color: is-bracketed(a b);\n}\n",
|
||||
"a {\n color: false;\n}\n"
|
||||
);
|
||||
test!(
|
||||
is_bracketed_number,
|
||||
"a {\n color: is-bracketed(1);\n}\n",
|
||||
"a {\n color: false;\n}\n"
|
||||
);
|
||||
error!(
|
||||
is_bracketed_two_args,
|
||||
"a {\n color: is-bracketed(a, b);\n}\n", "Error: Only 1 argument allowed, but 2 were passed."
|
||||
);
|
||||
error!(
|
||||
nth_non_numeric_index,
|
||||
"a {\n color: nth(a b, c);\n}\n", "Error: $n: c is not a number."
|
||||
);
|
||||
error!(
|
||||
set_nth_non_numeric_index,
|
||||
"a {\n color: set-nth(a b, c, d);\n}\n", "Error: $n: c is not a number."
|
||||
);
|
||||
error!(
|
||||
set_nth_index_zero,
|
||||
"a {\n color: set-nth(a b, 0, d);\n}\n", "Error: $n: List index may not be 0."
|
||||
);
|
||||
error!(
|
||||
set_nth_index_decimal,
|
||||
"a {\n color: set-nth(a b, 1.5, d);\n}\n", "Error: $n: 1.5 is not an int."
|
||||
);
|
||||
error!(
|
||||
set_nth_index_negative_outside_range,
|
||||
"a {\n color: set-nth(a b, -3, d);\n}\n",
|
||||
"Error: $n: Invalid index -3 for a list with 2 elements."
|
||||
);
|
||||
test!(
|
||||
set_nth_index_negative_inside_range,
|
||||
"a {\n color: set-nth(a b, -1, d);\n}\n",
|
||||
"a {\n color: a d;\n}\n"
|
||||
);
|
||||
error!(
|
||||
set_nth_index_infinity,
|
||||
"a {\n color: set-nth(a b, 1/0, d);\n}\n", "Error: $n: Infinity is not an int."
|
||||
);
|
||||
error!(
|
||||
set_nth_index_negative_infinity,
|
||||
"a {\n color: set-nth(a b, -1/0, d);\n}\n", "Error: $n: -Infinity is not an int."
|
||||
);
|
||||
error!(
|
||||
set_nth_decimal_outside_range,
|
||||
"a {\n color: set-nth(a b, 8.5, d);\n}\n", "Error: $n: 8.5 is not an int."
|
||||
);
|
||||
test!(
|
||||
append_with_slash_separator,
|
||||
"a {\n color: append(a b, c, slash);\n}\n",
|
||||
"a {\n color: a / b / c;\n}\n"
|
||||
);
|
||||
error!(
|
||||
append_invalid_separator,
|
||||
"a {\n color: append(a b, c, foo);\n}\n",
|
||||
"Error: $separator: Must be \"space\", \"comma\", \"slash\", or \"auto\"."
|
||||
);
|
||||
test!(
|
||||
join_with_slash_separator,
|
||||
"a {\n color: join(a, b, slash);\n}\n",
|
||||
"a {\n color: a / b;\n}\n"
|
||||
);
|
||||
error!(
|
||||
join_invalid_separator,
|
||||
"a {\n color: join(a b, c, foo);\n}\n",
|
||||
"Error: $separator: Must be \"space\", \"comma\", \"slash\", or \"auto\"."
|
||||
);
|
||||
error!(
|
||||
join_invalid_separator_non_string,
|
||||
"a {\n color: join(a b, c, 1);\n}\n", "Error: $separator: 1 is not a string."
|
||||
);
|
||||
test!(
|
||||
join_bracketed_true,
|
||||
"a {\n color: join(a, b, space, true);\n}\n",
|
||||
"a {\n color: [a b];\n}\n"
|
||||
);
|
||||
test!(
|
||||
join_bracketed_truthy,
|
||||
"a {\n color: join(a, b, space, a);\n}\n",
|
||||
"a {\n color: [a b];\n}\n"
|
||||
);
|
||||
test!(
|
||||
join_bracketed_falsey,
|
||||
"a {\n color: join(a, b, space, null);\n}\n",
|
||||
"a {\n color: a b;\n}\n"
|
||||
);
|
||||
test!(zip_no_args, "a {\n color: zip();\n}\n", "");
|
||||
|
@ -115,7 +115,6 @@ test!(
|
||||
"a {\n color: NaNdeg;\n}\n"
|
||||
);
|
||||
error!(
|
||||
#[ignore = "we dont emit units"]
|
||||
unitful_nan_random,
|
||||
"@use \"sass:math\";\na {\n color: random(math.acos(2));\n}\n",
|
||||
"Error: $limit: NaNdeg is not an int."
|
||||
|
@ -221,3 +221,31 @@ test!(
|
||||
"@supports (foo) {\n a {\n color: red;\n }\n}\n",
|
||||
grass::Options::default().input_syntax(InputSyntax::Css)
|
||||
);
|
||||
test!(
|
||||
custom_property,
|
||||
"a {
|
||||
--foo: /* */;
|
||||
}",
|
||||
"a {\n --foo: /* */;\n}\n",
|
||||
grass::Options::default().input_syntax(InputSyntax::Css)
|
||||
);
|
||||
error!(
|
||||
single_nested_property,
|
||||
"a {
|
||||
b: {
|
||||
c: d;
|
||||
}
|
||||
}",
|
||||
"Error: Nested declarations aren't allowed in plain CSS.",
|
||||
grass::Options::default().input_syntax(InputSyntax::Css)
|
||||
);
|
||||
error!(
|
||||
single_nested_property_with_expression,
|
||||
"a {
|
||||
b: 2 {
|
||||
c: d;
|
||||
}
|
||||
}",
|
||||
"Error: Nested declarations aren't allowed in plain CSS.",
|
||||
grass::Options::default().input_syntax(InputSyntax::Css)
|
||||
);
|
||||
|
@ -103,6 +103,20 @@ a
|
||||
"a + b {\n color: red;\n}\n",
|
||||
grass::Options::default().input_syntax(InputSyntax::Sass)
|
||||
);
|
||||
test!(
|
||||
if_else_if_else,
|
||||
r#"
|
||||
a
|
||||
@if false
|
||||
color: red
|
||||
@else if false
|
||||
color: blue
|
||||
@else
|
||||
color: orange
|
||||
"#,
|
||||
"a {\n color: orange;\n}\n",
|
||||
grass::Options::default().input_syntax(InputSyntax::Sass)
|
||||
);
|
||||
error!(
|
||||
multiline_comment_in_value_position,
|
||||
r#"
|
||||
|
@ -1008,6 +1008,35 @@ error!(
|
||||
child_selector_starts_with_forward_slash,
|
||||
"a { /b { } }", "Error: expected selector."
|
||||
);
|
||||
test!(
|
||||
selector_module_exists,
|
||||
"@use 'sass:selector';
|
||||
a {
|
||||
color: selector.parse('a');
|
||||
}
|
||||
",
|
||||
"a {\n color: a;\n}\n"
|
||||
);
|
||||
test!(
|
||||
selector_contains_url_without_parens,
|
||||
"a url {\n color: red;\n}\n",
|
||||
"a url {\n color: red;\n}\n"
|
||||
);
|
||||
test!(
|
||||
selector_contains_capital_u,
|
||||
"a U {\n color: red;\n}\n",
|
||||
"a U {\n color: red;\n}\n"
|
||||
);
|
||||
test!(
|
||||
selector_contains_url_with_quoted_string_inside_parens,
|
||||
"a :url(\"foo.css\") {\n color: red;\n}\n",
|
||||
"a :url(\"foo.css\") {\n color: red;\n}\n"
|
||||
);
|
||||
test!(
|
||||
selector_contains_url_with_hash_inside_parens,
|
||||
"a :url(#) {\n color: red;\n}\n",
|
||||
"a :url(#) {\n color: red;\n}\n"
|
||||
);
|
||||
|
||||
// todo:
|
||||
// [attr=url] {
|
||||
|
@ -299,3 +299,12 @@ test!(
|
||||
}",
|
||||
""
|
||||
);
|
||||
test!(
|
||||
string_module_exists,
|
||||
"@use 'sass:string';
|
||||
a {
|
||||
color: string.to-lower-case('AAA');
|
||||
}
|
||||
",
|
||||
"a {\n color: \"aaa\";\n}\n"
|
||||
);
|
||||
|
@ -202,7 +202,6 @@ test!(
|
||||
"a {\n color /**/ : red;\n}\n",
|
||||
"a {\n color: red;\n}\n"
|
||||
);
|
||||
// todo: many other strange edge cases like `.style: val` (dealing with ambiguity is hard for very little gain)
|
||||
test!(
|
||||
style_begins_with_asterisk_without_whitespace,
|
||||
"a {\n *zoom: 1;\n}\n",
|
||||
@ -231,6 +230,20 @@ test!(
|
||||
}",
|
||||
"a {\n position: relative;\n}\nc {\n white-space: nowrap;\n}\n"
|
||||
);
|
||||
test!(
|
||||
symbol_before_property_name_hacks,
|
||||
"a {
|
||||
.color: foo;
|
||||
#color: foo;
|
||||
:color: foo;
|
||||
*color: foo;
|
||||
.--color: foo;
|
||||
#--color: foo;
|
||||
:--color: foo;
|
||||
*--color: foo;
|
||||
}",
|
||||
"a {\n .color: foo;\n #color: foo;\n :color: foo;\n *color: foo;\n .--color: foo;\n #--color: foo;\n :--color: foo;\n *--color: foo;\n}\n"
|
||||
);
|
||||
error!(
|
||||
media_inside_nested_declaration,
|
||||
"a {
|
||||
|
Loading…
x
Reference in New Issue
Block a user