ffaee04613
Adds support for the indented syntax, plain CSS imports, `@forward`, and many other previously missing features.
517 lines
12 KiB
Rust
517 lines
12 KiB
Rust
use std::{io::Write, path::Path};
|
|
|
|
use macros::TestFs;
|
|
|
|
#[macro_use]
|
|
mod macros;
|
|
|
|
#[test]
|
|
fn null_fs_cannot_import() {
|
|
let input = "@import \"foo\";";
|
|
tempfile!("foo.scss", "");
|
|
match grass::from_string(
|
|
input.to_string(),
|
|
&grass::Options::default().fs(&grass::NullFs),
|
|
) {
|
|
Err(e)
|
|
if e.to_string()
|
|
.starts_with("Error: Can't find stylesheet to import.\n") =>
|
|
{
|
|
()
|
|
}
|
|
Ok(..) => panic!("did not fail"),
|
|
Err(e) => panic!("failed in the wrong way: {}", e),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn imports_variable() {
|
|
let mut fs = TestFs::new();
|
|
|
|
fs.add_file("a.scss", r#"$a: red;"#);
|
|
|
|
let input = r#"
|
|
@import "a";
|
|
a {
|
|
color: $a;
|
|
}
|
|
"#;
|
|
|
|
assert_eq!(
|
|
"a {\n color: red;\n}\n",
|
|
&grass::from_string(input.to_string(), &grass::Options::default().fs(&fs)).expect(input)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
#[ignore = "we don't actually check if the semicolon exists"]
|
|
fn import_no_semicolon() {
|
|
let input = "@import \"import_no_semicolon\"\na {\n color: $a;\n}";
|
|
tempfile!("import_no_semicolon", "$a: red;");
|
|
|
|
drop(input);
|
|
}
|
|
|
|
#[test]
|
|
fn import_no_quotes() {
|
|
let input = "@import import_no_quotes";
|
|
|
|
assert_err!("Error: Expected string.", input);
|
|
}
|
|
|
|
#[test]
|
|
fn single_quotes_import() {
|
|
let mut fs = TestFs::new();
|
|
|
|
fs.add_file("a.scss", r#"$a: red;"#);
|
|
|
|
let input = r#"
|
|
@import 'a';
|
|
a {
|
|
color: $a;
|
|
}
|
|
"#;
|
|
|
|
assert_eq!(
|
|
"a {\n color: red;\n}\n",
|
|
&grass::from_string(input.to_string(), &grass::Options::default().fs(&fs)).expect(input)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn comma_separated_import() {
|
|
let mut fs = TestFs::new();
|
|
|
|
fs.add_file("a.scss", r#"$a: red"#);
|
|
fs.add_file("b.scss", r#"p { color: blue; }"#);
|
|
|
|
let input = r#"
|
|
@import 'a', 'b';
|
|
|
|
a {
|
|
color: $a;
|
|
}
|
|
"#;
|
|
|
|
assert_eq!(
|
|
"p {\n color: blue;\n}\n\na {\n color: red;\n}\n",
|
|
&grass::from_string(input.to_string(), &grass::Options::default().fs(&fs)).expect(input)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn comma_separated_import_order() {
|
|
let mut fs = TestFs::new();
|
|
|
|
fs.add_file("a.scss", r#"p { color: red; }"#);
|
|
fs.add_file("b.scss", r#"p { color: blue; }"#);
|
|
|
|
let input = r#"
|
|
@import "a", "b", url(third);
|
|
"#;
|
|
|
|
assert_eq!(
|
|
"@import url(third);\np {\n color: red;\n}\n\np {\n color: blue;\n}\n",
|
|
&grass::from_string(input.to_string(), &grass::Options::default().fs(&fs)).expect(input)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn comma_separated_import_order_css() {
|
|
let mut fs = TestFs::new();
|
|
|
|
fs.add_file("a.css", r#"p { color: red; }"#);
|
|
fs.add_file("b.css", r#"p { color: blue; }"#);
|
|
|
|
let input = r#"
|
|
@import "a.css", "b", url(third);
|
|
"#;
|
|
|
|
assert_eq!(
|
|
"@import \"a.css\";\n@import url(third);\np {\n color: blue;\n}\n",
|
|
&grass::from_string(input.to_string(), &grass::Options::default().fs(&fs)).expect(input)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn basic_load_path() {
|
|
tempfile!(
|
|
"basic_load_path__a.scss",
|
|
"@import \"basic_load_path__b\";\na {\n color: $a;\n}",
|
|
dir = "dir-basic_load_path__a"
|
|
);
|
|
tempfile!(
|
|
"basic_load_path__b.scss",
|
|
"$a: red;",
|
|
dir = "dir-basic_load_path__b"
|
|
);
|
|
|
|
assert_eq!(
|
|
"a {\n color: red;\n}\n",
|
|
grass::from_path(
|
|
"dir-basic_load_path__a/basic_load_path__a.scss",
|
|
&grass::Options::default().load_path(std::path::Path::new("dir-basic_load_path__b"))
|
|
)
|
|
.unwrap()
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn load_path_same_directory() {
|
|
tempfile!(
|
|
"load_path_same_directory__a.scss",
|
|
"@import \"dir-load_path_same_directory__a/load_path_same_directory__b\";\na {\n color: $a;\n}",
|
|
dir = "dir-load_path_same_directory__a"
|
|
);
|
|
tempfile!(
|
|
"load_path_same_directory__b.scss",
|
|
"$a: red;",
|
|
dir = "dir-load_path_same_directory__a"
|
|
);
|
|
|
|
assert_eq!(
|
|
"a {\n color: red;\n}\n",
|
|
grass::from_path(
|
|
"dir-load_path_same_directory__a/load_path_same_directory__a.scss",
|
|
&grass::Options::default().load_path(std::path::Path::new("."))
|
|
)
|
|
.unwrap()
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn comma_separated_import_trailing() {
|
|
let mut fs = TestFs::new();
|
|
|
|
fs.add_file("a.scss", r#"p { color: red; }"#);
|
|
fs.add_file("b.scss", r#"p { color: blue; }"#);
|
|
|
|
let input = r#"
|
|
@import "a", "b", url(third),,,,,,,,;
|
|
"#;
|
|
|
|
assert_err!("Error: Expected string.", input);
|
|
}
|
|
|
|
#[test]
|
|
fn finds_name_scss() {
|
|
let mut fs = TestFs::new();
|
|
|
|
fs.add_file("a.scss", r#"$a: red;"#);
|
|
|
|
let input = r#"
|
|
@import "a";
|
|
a {
|
|
color: $a;
|
|
}
|
|
"#;
|
|
|
|
assert_eq!(
|
|
"a {\n color: red;\n}\n",
|
|
&grass::from_string(input.to_string(), &grass::Options::default().fs(&fs)).expect(input)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn finds_underscore_name_scss() {
|
|
let mut fs = TestFs::new();
|
|
fs.add_file("_a.scss", r#"$a: red;"#);
|
|
|
|
let input = r#"
|
|
@import "a";
|
|
a {
|
|
color: $a;
|
|
}
|
|
"#;
|
|
|
|
assert_eq!(
|
|
"a {\n color: red;\n}\n",
|
|
&grass::from_string(input.to_string(), &grass::Options::default().fs(&fs)).expect(input)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn chained_imports() {
|
|
let mut fs = TestFs::new();
|
|
|
|
fs.add_file("a.scss", r#"@import "b";"#);
|
|
fs.add_file("b.scss", r#"@import "c";"#);
|
|
fs.add_file("c.scss", r#"$a: red;"#);
|
|
|
|
let input = r#"
|
|
@import "a";
|
|
a {
|
|
color: $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_plain_css() {
|
|
let mut fs = TestFs::new();
|
|
|
|
fs.add_file("a.css", r#"a { color: red; }"#);
|
|
|
|
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_import_only_scss() {
|
|
let mut fs = TestFs::new();
|
|
|
|
fs.add_file("a.import.scss", r#"a { color: red; }"#);
|
|
|
|
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();
|
|
|
|
fs.add_file("/foo/a.scss", r#"a { color: red; }"#);
|
|
|
|
let input = r#"
|
|
@import "/foo/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_explicit_file_extension() {
|
|
let mut fs = TestFs::new();
|
|
|
|
fs.add_file("a.scss", r#"a { color: red; }"#);
|
|
|
|
let input = r#"
|
|
@import "a.scss";
|
|
"#;
|
|
|
|
assert_eq!(
|
|
"a {\n color: red;\n}\n",
|
|
&grass::from_string(input.to_string(), &grass::Options::default().fs(&fs)).expect(input)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn potentially_conflicting_directory_and_file() {
|
|
tempfile!(
|
|
"index.scss",
|
|
"$a: wrong;",
|
|
dir = "potentially_conflicting_directory_and_file"
|
|
);
|
|
tempfile!(
|
|
"_potentially_conflicting_directory_and_file.scss",
|
|
"$a: right;"
|
|
);
|
|
|
|
let input = r#"
|
|
@import "potentially_conflicting_directory_and_file";
|
|
a {
|
|
color: $a;
|
|
}
|
|
"#;
|
|
|
|
assert_eq!(
|
|
"a {\n color: right;\n}\n",
|
|
&grass::from_string(input.to_string(), &grass::Options::default()).expect(input)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn finds_index_file_no_underscore() {
|
|
tempfile!(
|
|
"index.scss",
|
|
"$a: right;",
|
|
dir = "finds_index_file_no_underscore"
|
|
);
|
|
|
|
let input = r#"
|
|
@import "finds_index_file_no_underscore";
|
|
a {
|
|
color: $a;
|
|
}
|
|
"#;
|
|
|
|
assert_eq!(
|
|
"a {\n color: right;\n}\n",
|
|
&grass::from_string(input.to_string(), &grass::Options::default()).expect(input)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn finds_index_file_with_underscore() {
|
|
tempfile!(
|
|
"_index.scss",
|
|
"$a: right;",
|
|
dir = "finds_index_file_with_underscore"
|
|
);
|
|
|
|
let input = r#"
|
|
@import "finds_index_file_with_underscore";
|
|
a {
|
|
color: $a;
|
|
}
|
|
"#;
|
|
|
|
assert_eq!(
|
|
"a {\n color: right;\n}\n",
|
|
&grass::from_string(input.to_string(), &grass::Options::default()).expect(input)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn potentially_conflicting_directory_and_file_from_load_path() {
|
|
tempfile!(
|
|
"_potentially_conflicting_directory_and_file_from_load_path.scss",
|
|
"$a: right;",
|
|
dir = "potentially_conflicting_directory_and_file_from_load_path__a"
|
|
);
|
|
tempfile!(
|
|
"index.scss",
|
|
"$a: wrong;",
|
|
dir = "potentially_conflicting_directory_and_file_from_load_path__a/potentially_conflicting_directory_and_file_from_load_path"
|
|
);
|
|
|
|
let input = r#"
|
|
@import "potentially_conflicting_directory_and_file_from_load_path";
|
|
a {
|
|
color: $a;
|
|
}
|
|
"#;
|
|
|
|
assert_eq!(
|
|
"a {\n color: right;\n}\n",
|
|
&grass::from_string(
|
|
input.to_string(),
|
|
&grass::Options::default().load_path(&Path::new(
|
|
"potentially_conflicting_directory_and_file_from_load_path__a"
|
|
))
|
|
)
|
|
.expect(input)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn chained_imports_in_directory() {
|
|
let input = "@import \"chained_imports_in_directory__a\";\na {\n color: $a;\n}";
|
|
tempfile!(
|
|
"chained_imports_in_directory__a.scss",
|
|
"@import \"chained_imports_in_directory__b\";"
|
|
);
|
|
tempfile!(
|
|
"index.scss",
|
|
"@import \"../chained_imports_in_directory__c\";",
|
|
dir = "chained_imports_in_directory__b"
|
|
);
|
|
tempfile!("chained_imports_in_directory__c.scss", "$a: red;");
|
|
assert_eq!(
|
|
"a {\n color: red;\n}\n",
|
|
&grass::from_string(input.to_string(), &grass::Options::default()).expect(input)
|
|
);
|
|
}
|
|
|
|
error!(
|
|
// note: dart-sass error is "expected more input."
|
|
missing_input_after_import,
|
|
"@import", "Error: Expected string."
|
|
);
|
|
error!(
|
|
import_unquoted_http,
|
|
"@import http://foo.com/;", "Error: Expected string."
|
|
);
|
|
error!(
|
|
import_file_doesnt_exist,
|
|
"@import \"idontexist\";", "Error: Can't find stylesheet to import."
|
|
);
|
|
error!(
|
|
file_name_is_two_periods,
|
|
"@import \"foo/..\";", "Error: Can't find stylesheet to import."
|
|
);
|
|
test!(
|
|
import_beginning_with_http,
|
|
"@import \"http://foo.com/\";",
|
|
"@import \"http://foo.com/\";\n"
|
|
);
|
|
test!(
|
|
import_beginning_with_http_no_ending_slash,
|
|
"@import \"http://foo.com\";",
|
|
"@import \"http://foo.com\";\n"
|
|
);
|
|
test!(
|
|
import_beginning_with_https,
|
|
"@import \"https://foo.com/\";",
|
|
"@import \"https://foo.com/\";\n"
|
|
);
|
|
test!(
|
|
import_ending_in_css,
|
|
"@import \"foo.css\";",
|
|
"@import \"foo.css\";\n"
|
|
);
|
|
test!(import_url, "@import url(foo..);", "@import url(foo..);\n");
|
|
test!(
|
|
import_url_interpolation,
|
|
"@import url(#{1+1}..);",
|
|
"@import url(2..);\n"
|
|
);
|
|
test!(
|
|
#[ignore = "we currently place plain @import ahead of loud comments that precede it"]
|
|
import_multiline_comments_everywhere,
|
|
" /**/ @import /**/ url(foo) /**/ ;",
|
|
"/**/\n@import url(foo);\n"
|
|
);
|
|
test!(
|
|
plain_css_begins_with_two_slashes,
|
|
"@import \"//fonts.googleapis.com/css?family=Droid+Sans\";",
|
|
"@import \"//fonts.googleapis.com/css?family=Droid+Sans\";\n"
|
|
);
|
|
test!(
|
|
plain_css_retains_backslash_for_escaped_space,
|
|
r#"@import "hux\ bux.css";"#,
|
|
"@import \"hux\\ bux.css\";\n"
|
|
);
|
|
test!(
|
|
plain_css_is_moved_to_top_of_file,
|
|
"a {
|
|
color: red;
|
|
}
|
|
|
|
@import url(\"foo.css\");",
|
|
"@import url(\"foo.css\");\na {\n color: red;\n}\n"
|
|
);
|
|
test!(
|
|
many_import_conditions,
|
|
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"
|
|
);
|
|
error!(unclosed_single_quote, r#"@import '"#, "Error: Expected '.");
|
|
error!(unclosed_double_quote, r#"@import ""#, "Error: Expected \".");
|
|
|
|
// todo: edge case tests for plain css imports moved to top
|
|
// todo: test for calling paths, e.g. `grass b\index.scss`
|
|
// todo: test for absolute paths (how?)
|
|
// todo: test for @import accessing things declared beforehand
|
|
// e.g. b { @import } | $a: red; @import
|