use std::path::{Path, PathBuf}; use serde::{de::DeserializeOwned, Deserialize}; use serde_json::Value; use oxc_span::SourceType; use oxc_tasks_common::BabelOptions; use crate::{ project_root, suite::{Case, Suite, TestResult}, }; const FIXTURES_PATH: &str = "tasks/coverage/babel/packages/babel-parser/test/fixtures"; /// output.json #[derive(Debug, Default, Clone, Deserialize)] pub struct BabelOutput { pub errors: Option>, } pub struct BabelSuite { test_root: PathBuf, test_cases: Vec, } impl BabelSuite { pub fn new() -> Self { Self { test_root: project_root().join(FIXTURES_PATH), test_cases: vec![] } } } impl Suite for BabelSuite { fn get_test_root(&self) -> &Path { &self.test_root } fn skip_test_path(&self, path: &Path) -> bool { let not_supported_directory = [ "experimental", "es2022", "record-and-tuple", "es-record", "es-tuple", "with-pipeline", "v8intrinsic", "async-do-expression", "export-ns-from", ] .iter() .any(|p| path.to_string_lossy().contains(p)); let incorrect_extension = path.extension().map_or(true, |ext| ext == "json" || ext == "md"); not_supported_directory || incorrect_extension } fn save_test_cases(&mut self, tests: Vec) { self.test_cases = tests; } fn get_test_cases(&self) -> &Vec { &self.test_cases } fn get_test_cases_mut(&mut self) -> &mut Vec { &mut self.test_cases } } pub struct BabelCase { path: PathBuf, code: String, source_type: SourceType, options: BabelOptions, should_fail: bool, result: TestResult, } impl BabelCase { pub fn set_result(&mut self, result: TestResult) { self.result = result; } pub fn source_type(&self) -> SourceType { self.source_type } fn read_file(path: &Path, file_name: &'static str) -> Option where T: DeserializeOwned, { let file = path.with_file_name(file_name); if file.exists() { let file = std::fs::File::open(file).unwrap(); let reader = std::io::BufReader::new(file); let json: serde_json::Result = serde_json::from_reader(reader); return json.ok(); } None } fn read_output_json(path: &Path) -> Option { let dir = project_root().join(FIXTURES_PATH).join(path); if let Some(json) = Self::read_file::(&dir, "output.json") { return Some(json); } Self::read_file::(&dir, "output.extended.json") } // it is an error if: // * its output.json contains an errors field // * the directory contains a options.json with a "throws" field fn determine_should_fail(path: &Path, options: &BabelOptions) -> bool { let output_json = Self::read_output_json(path); if let Some(output_json) = output_json { return output_json.errors.map_or(false, |errors| !errors.is_empty()); } if options.throws.is_some() { return true; } // both files doesn't exist true } } impl Case for BabelCase { /// # Panics fn new(path: PathBuf, code: String) -> Self { let dir = project_root().join(FIXTURES_PATH).join(&path); let options = BabelOptions::from_path(dir.parent().unwrap()); let source_type = SourceType::from_path(&path) .unwrap() .with_script(true) .with_jsx(options.is_jsx()) .with_typescript(options.is_typescript()) .with_typescript_definition(options.is_typescript_definition()) .with_module(options.is_module()); let should_fail = Self::determine_should_fail(&path, &options); Self { path, code, source_type, options, should_fail, result: TestResult::ToBeRun } } fn code(&self) -> &str { &self.code } fn path(&self) -> &Path { &self.path } fn allow_return_outside_function(&self) -> bool { self.options.allow_return_outside_function } fn test_result(&self) -> &TestResult { &self.result } fn should_fail(&self) -> bool { self.should_fail } fn skip_test_case(&self) -> bool { let not_supported_plugins = ["async-do-expression", "flow", "placeholders", "decorators-legacy", "recordAndTuple"]; let has_not_supported_plugins = self .options .plugins .iter() .filter_map(Value::as_str) .any(|p| not_supported_plugins.contains(&p)); has_not_supported_plugins || self.options.allow_await_outside_function || self.options.allow_undeclared_exports } fn run(&mut self) { let source_type = self.source_type(); self.result = self.execute(source_type); } }