use biome_configuration::PartialConfiguration;
use biome_console::fmt::{Formatter, Termcolor};
use biome_console::markup;
use biome_css_parser::{parse_css, CssParserOptions};
use biome_deserialize::json::deserialize_from_str;
use biome_diagnostics::display::PrintDiagnostic;
use biome_diagnostics::DiagnosticExt;
use biome_diagnostics::{print_diagnostic_to_string, termcolor};
use biome_fs::BiomePath;
use biome_rowan::SyntaxKind;
use biome_service::settings::Settings;
use biome_test_utils::has_bogus_nodes_or_empty_slots;
use std::fmt::Write;
use std::fs;
use std::path::Path;
#[derive(Copy, Clone)]
pub enum ExpectedOutcome {
Pass,
Fail,
Undefined,
}
pub fn run(test_case: &str, _snapshot_name: &str, test_directory: &str, outcome_str: &str) {
let outcome = match outcome_str {
"ok" => ExpectedOutcome::Pass,
"error" => ExpectedOutcome::Fail,
"undefined" => ExpectedOutcome::Undefined,
_ => panic!("Invalid expected outcome {outcome_str}"),
};
let test_case_path = Path::new(test_case);
let file_name = test_case_path
.file_name()
.expect("Expected test to have a file name")
.to_str()
.expect("File name to be valid UTF8");
let content = fs::read_to_string(test_case_path)
.expect("Expected test path to be a readable file in UTF8 encoding");
let mut options = CssParserOptions::default()
.allow_metavariables();
let options_path = Path::new(test_directory).join("options.json");
if options_path.exists() {
let mut options_path = BiomePath::new(&options_path);
let mut settings = Settings::default();
let (test_options, diagnostics) = deserialize_from_str::<PartialConfiguration>(
options_path.get_buffer_from_file().as_str(),
)
.consume();
settings
.merge_with_configuration(test_options.unwrap_or_default(), None, None, &[])
.unwrap();
let settings = settings.languages.css.parser;
if settings.css_modules.unwrap_or_default() {
options = options.allow_css_modules();
}
if settings.allow_wrong_line_comments.unwrap_or_default() {
options = options.allow_wrong_line_comments();
}
if !diagnostics.is_empty() {
for diagnostic in diagnostics {
println!("{:?}", print_diagnostic_to_string(&diagnostic));
}
panic!("Configuration is invalid");
}
}
let parsed = parse_css(&content, options);
let formatted_ast = format!("{:#?}", parsed.tree());
let mut snapshot = String::new();
writeln!(snapshot, "\n## Input\n\n```css\n{content}\n```\n\n").unwrap();
writeln!(
snapshot,
r#"## AST
```
{formatted_ast}
```
## CST
```
{:#?}
```
"#,
parsed.syntax()
)
.unwrap();
let diagnostics = parsed.diagnostics();
if !diagnostics.is_empty() {
let mut diagnostics_buffer = termcolor::Buffer::no_color();
let termcolor = &mut Termcolor(&mut diagnostics_buffer);
let mut formatter = Formatter::new(termcolor);
for diagnostic in diagnostics {
let error = diagnostic
.clone()
.with_file_path(file_name)
.with_file_source_code(&content);
formatter
.write_markup(markup! {
{PrintDiagnostic::verbose(&error)}
})
.expect("failed to emit diagnostic");
}
let formatted_diagnostics =
std::str::from_utf8(diagnostics_buffer.as_slice()).expect("non utf8 in error buffer");
if matches!(outcome, ExpectedOutcome::Pass) {
panic!("Expected no errors to be present in a test case that is expected to pass but the following diagnostics are present:\n{formatted_diagnostics}")
}
writeln!(snapshot, "## Diagnostics\n\n```").unwrap();
snapshot.write_str(formatted_diagnostics).unwrap();
writeln!(snapshot, "```\n").unwrap();
}
match outcome {
ExpectedOutcome::Pass => {
let missing_required = formatted_ast.contains("missing (required)");
if missing_required
|| parsed
.syntax()
.descendants()
.any(|node| node.kind().is_bogus())
{
panic!("Parsed tree of a 'OK' test case should not contain any missing required children or bogus nodes: \n {formatted_ast:#?} \n\n {formatted_ast}");
}
let syntax = parsed.syntax();
if has_bogus_nodes_or_empty_slots(&syntax) {
panic!("modified tree has bogus nodes or empty slots:\n{syntax:#?} \n\n {syntax}")
}
}
ExpectedOutcome::Fail => {
if parsed.diagnostics().is_empty() {
panic!("Failing test must have diagnostics");
}
}
_ => {}
}
insta::with_settings!({
prepend_module_to_snapshot => false,
snapshot_path => &test_directory,
}, {
insta::assert_snapshot!(file_name, snapshot);
});
}
#[ignore]
#[test]
pub fn quick_test() {
let code = r#"
.foo {
color: blue;;
}
"#;
let root = parse_css(
code,
CssParserOptions::default()
.allow_wrong_line_comments()
.allow_css_modules()
.allow_metavariables(),
);
let syntax = root.syntax();
dbg!(&syntax, root.diagnostics(), root.has_errors());
if has_bogus_nodes_or_empty_slots(&syntax) {
panic!("modified tree has bogus nodes or empty slots:\n{syntax:#?} \n\n {syntax}")
}
}