diff --git a/cli/tools/jupyter/mod.rs b/cli/tools/jupyter/mod.rs index cfed1c3991..2731f58143 100644 --- a/cli/tools/jupyter/mod.rs +++ b/cli/tools/jupyter/mod.rs @@ -11,11 +11,13 @@ use crate::tools::test::TestEventWorkerSender; use crate::util::logger; use crate::CliFactory; use deno_core::anyhow::Context; +use deno_core::error::generic_error; use deno_core::error::AnyError; use deno_core::located_script_name; use deno_core::resolve_url_or_path; use deno_core::serde::Deserialize; use deno_core::serde_json; +use deno_core::url::Url; use deno_runtime::deno_io::Stdio; use deno_runtime::deno_io::StdioPipe; use deno_runtime::permissions::Permissions; @@ -129,9 +131,16 @@ pub async fn kernel( Ok(()) } } + let cwd_url = + Url::from_directory_path(cli_options.initial_cwd()).map_err(|_| { + generic_error(format!( + "Unable to construct URL from the path of cwd: {}", + cli_options.initial_cwd().to_string_lossy(), + )) + })?; repl_session.set_test_reporter_factory(Box::new(move || { Box::new( - PrettyTestReporter::new(false, true, false, true) + PrettyTestReporter::new(false, true, false, true, cwd_url.clone()) .with_writer(Box::new(TestWriter(stdio_tx.clone()))), ) })); diff --git a/cli/tools/repl/session.rs b/cli/tools/repl/session.rs index 039dc0d715..48614cfe5c 100644 --- a/cli/tools/repl/session.rs +++ b/cli/tools/repl/session.rs @@ -30,6 +30,7 @@ use deno_ast::ParsedSource; use deno_ast::SourcePos; use deno_ast::SourceRangedForSpanned; use deno_ast::SourceTextInfo; +use deno_core::error::generic_error; use deno_core::error::AnyError; use deno_core::futures::channel::mpsc::UnboundedReceiver; use deno_core::futures::FutureExt; @@ -37,6 +38,7 @@ use deno_core::futures::StreamExt; use deno_core::serde_json; use deno_core::serde_json::Value; use deno_core::unsync::spawn; +use deno_core::url::Url; use deno_core::LocalInspectorSession; use deno_core::PollEventLoopOptions; use deno_graph::source::ResolutionMode; @@ -243,6 +245,13 @@ impl ReplSession { deno_core::resolve_path("./$deno$repl.ts", cli_options.initial_cwd()) .unwrap(); + let cwd_url = + Url::from_directory_path(cli_options.initial_cwd()).map_err(|_| { + generic_error(format!( + "Unable to construct URL from the path of cwd: {}", + cli_options.initial_cwd().to_string_lossy(), + )) + })?; let ts_config_for_emit = cli_options .resolve_ts_config_for_emit(deno_config::TsConfigType::Emit)?; let emit_options = @@ -257,8 +266,14 @@ impl ReplSession { language_server, referrer, notifications: Arc::new(Mutex::new(notification_rx)), - test_reporter_factory: Box::new(|| { - Box::new(PrettyTestReporter::new(false, true, false, true)) + test_reporter_factory: Box::new(move || { + Box::new(PrettyTestReporter::new( + false, + true, + false, + true, + cwd_url.clone(), + )) }), main_module, test_event_sender, diff --git a/cli/tools/test/mod.rs b/cli/tools/test/mod.rs index 68be540783..738c8c3048 100644 --- a/cli/tools/test/mod.rs +++ b/cli/tools/test/mod.rs @@ -298,43 +298,77 @@ pub enum TestFailure { HasSanitizersAndOverlaps(IndexSet), // Long names of overlapped tests } -impl ToString for TestFailure { - fn to_string(&self) -> String { +impl std::fmt::Display for TestFailure { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - TestFailure::JsError(js_error) => format_test_error(js_error), - TestFailure::FailedSteps(1) => "1 test step failed.".to_string(), - TestFailure::FailedSteps(n) => format!("{} test steps failed.", n), - TestFailure::IncompleteSteps => "Completed while steps were still running. Ensure all steps are awaited with `await t.step(...)`.".to_string(), - TestFailure::Incomplete => "Didn't complete before parent. Await step with `await t.step(...)`.".to_string(), + TestFailure::JsError(js_error) => { + write!(f, "{}", format_test_error(js_error)) + } + TestFailure::FailedSteps(1) => write!(f, "1 test step failed."), + TestFailure::FailedSteps(n) => write!(f, "{n} test steps failed."), + TestFailure::IncompleteSteps => { + write!(f, "Completed while steps were still running. Ensure all steps are awaited with `await t.step(...)`.") + } + TestFailure::Incomplete => { + write!( + f, + "Didn't complete before parent. Await step with `await t.step(...)`." + ) + } TestFailure::Leaked(details, trailer_notes) => { - let mut string = "Leaks detected:".to_string(); + write!(f, "Leaks detected:")?; for detail in details { - string.push_str(&format!("\n - {detail}")); + write!(f, "\n - {}", detail)?; } for trailer in trailer_notes { - string.push_str(&format!("\n{trailer}")); + write!(f, "\n{}", trailer)?; } - string + Ok(()) } TestFailure::OverlapsWithSanitizers(long_names) => { - let mut string = "Started test step while another test step with sanitizers was running:".to_string(); + write!(f, "Started test step while another test step with sanitizers was running:")?; for long_name in long_names { - string.push_str(&format!("\n * {}", long_name)); + write!(f, "\n * {}", long_name)?; } - string + Ok(()) } TestFailure::HasSanitizersAndOverlaps(long_names) => { - let mut string = "Started test step with sanitizers while another test step was running:".to_string(); + write!(f, "Started test step with sanitizers while another test step was running:")?; for long_name in long_names { - string.push_str(&format!("\n * {}", long_name)); + write!(f, "\n * {}", long_name)?; } - string + Ok(()) } } } } impl TestFailure { + pub fn overview(&self) -> String { + match self { + TestFailure::JsError(js_error) => js_error.exception_message.clone(), + TestFailure::FailedSteps(1) => "1 test step failed".to_string(), + TestFailure::FailedSteps(n) => format!("{n} test steps failed"), + TestFailure::IncompleteSteps => { + "Completed while steps were still running".to_string() + } + TestFailure::Incomplete => "Didn't complete before parent".to_string(), + TestFailure::Leaked(_, _) => "Leaks detected".to_string(), + TestFailure::OverlapsWithSanitizers(_) => { + "Started test step while another test step with sanitizers was running" + .to_string() + } + TestFailure::HasSanitizersAndOverlaps(_) => { + "Started test step with sanitizers while another test step was running" + .to_string() + } + } + } + + pub fn detail(&self) -> String { + self.to_string() + } + fn format_label(&self) -> String { match self { TestFailure::Incomplete => colors::gray("INCOMPLETE").to_string(), @@ -465,6 +499,7 @@ pub struct TestSummary { #[derive(Debug, Clone)] struct TestSpecifiersOptions { + cwd: Url, concurrent_jobs: NonZeroUsize, fail_fast: Option, log_level: Option, @@ -506,23 +541,30 @@ impl TestSummary { fn get_test_reporter(options: &TestSpecifiersOptions) -> Box { let parallel = options.concurrent_jobs.get() > 1; let reporter: Box = match &options.reporter { - TestReporterConfig::Dot => Box::new(DotTestReporter::new()), + TestReporterConfig::Dot => { + Box::new(DotTestReporter::new(options.cwd.clone())) + } TestReporterConfig::Pretty => Box::new(PrettyTestReporter::new( parallel, options.log_level != Some(Level::Error), options.filter, false, + options.cwd.clone(), )), TestReporterConfig::Junit => { - Box::new(JunitTestReporter::new("-".to_string())) + Box::new(JunitTestReporter::new(options.cwd.clone(), "-".to_string())) } TestReporterConfig::Tap => Box::new(TapTestReporter::new( + options.cwd.clone(), options.concurrent_jobs > NonZeroUsize::new(1).unwrap(), )), }; if let Some(junit_path) = &options.junit_path { - let junit = Box::new(JunitTestReporter::new(junit_path.to_string())); + let junit = Box::new(JunitTestReporter::new( + options.cwd.clone(), + junit_path.to_string(), + )); return Box::new(CompoundTestReporter::new(vec![reporter, junit])); } @@ -1641,6 +1683,14 @@ pub async fn run_tests( }) .collect(), TestSpecifiersOptions { + cwd: Url::from_directory_path(cli_options.initial_cwd()).map_err( + |_| { + generic_error(format!( + "Unable to construct URL from the path of cwd: {}", + cli_options.initial_cwd().to_string_lossy(), + )) + }, + )?, concurrent_jobs: test_options.concurrent_jobs, fail_fast: test_options.fail_fast, log_level, @@ -1780,6 +1830,14 @@ pub async fn run_tests_with_watch( }) .collect(), TestSpecifiersOptions { + cwd: Url::from_directory_path(cli_options.initial_cwd()).map_err( + |_| { + generic_error(format!( + "Unable to construct URL from the path of cwd: {}", + cli_options.initial_cwd().to_string_lossy(), + )) + }, + )?, concurrent_jobs: test_options.concurrent_jobs, fail_fast: test_options.fail_fast, log_level, diff --git a/cli/tools/test/reporters/common.rs b/cli/tools/test/reporters/common.rs index 1dc8796670..e4d8d4247c 100644 --- a/cli/tools/test/reporters/common.rs +++ b/cli/tools/test/reporters/common.rs @@ -136,13 +136,8 @@ pub(super) fn report_summary( if !failure.hide_in_summary() { let failure_title = format_test_for_summary(cwd, description); writeln!(writer, "{}", &failure_title).unwrap(); - writeln!( - writer, - "{}: {}", - colors::red_bold("error"), - failure.to_string() - ) - .unwrap(); + writeln!(writer, "{}: {}", colors::red_bold("error"), failure) + .unwrap(); writeln!(writer).unwrap(); failure_titles.push(failure_title); } diff --git a/cli/tools/test/reporters/dot.rs b/cli/tools/test/reporters/dot.rs index 395b5f0b6d..d2e529a9cd 100644 --- a/cli/tools/test/reporters/dot.rs +++ b/cli/tools/test/reporters/dot.rs @@ -12,7 +12,7 @@ pub struct DotTestReporter { } impl DotTestReporter { - pub fn new() -> DotTestReporter { + pub fn new(cwd: Url) -> DotTestReporter { let console_width = if let Some(size) = crate::util::console::console_size() { size.cols as usize @@ -23,7 +23,7 @@ impl DotTestReporter { DotTestReporter { n: 0, width: console_width, - cwd: Url::from_directory_path(std::env::current_dir().unwrap()).unwrap(), + cwd, summary: TestSummary::new(), } } diff --git a/cli/tools/test/reporters/junit.rs b/cli/tools/test/reporters/junit.rs index 464f47b8dc..eea1d2aca8 100644 --- a/cli/tools/test/reporters/junit.rs +++ b/cli/tools/test/reporters/junit.rs @@ -1,20 +1,29 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::collections::VecDeque; use std::path::PathBuf; +use super::fmt::to_relative_path_or_remote_url; use super::*; pub struct JunitTestReporter { - path: String, + cwd: Url, + output_path: String, // Stores TestCases (i.e. Tests) by the Test ID cases: IndexMap, + // Stores nodes representing test cases in such a way that can be traversed + // from child to parent to build the full test name that reflects the test + // hierarchy. + test_name_tree: TestNameTree, } impl JunitTestReporter { - pub fn new(path: String) -> Self { + pub fn new(cwd: Url, output_path: String) -> Self { Self { - path, + cwd, + output_path, cases: IndexMap::new(), + test_name_tree: TestNameTree::new(), } } @@ -24,9 +33,9 @@ impl JunitTestReporter { TestResult::Ignored => quick_junit::TestCaseStatus::skipped(), TestResult::Failed(failure) => quick_junit::TestCaseStatus::NonSuccess { kind: quick_junit::NonSuccessKind::Failure, - message: Some(failure.to_string()), + message: Some(failure.overview()), ty: None, - description: None, + description: Some(failure.detail()), reruns: vec![], }, TestResult::Cancelled => quick_junit::TestCaseStatus::NonSuccess { @@ -38,6 +47,24 @@ impl JunitTestReporter { }, } } + + fn convert_step_status( + status: &TestStepResult, + ) -> quick_junit::TestCaseStatus { + match status { + TestStepResult::Ok => quick_junit::TestCaseStatus::success(), + TestStepResult::Ignored => quick_junit::TestCaseStatus::skipped(), + TestStepResult::Failed(failure) => { + quick_junit::TestCaseStatus::NonSuccess { + kind: quick_junit::NonSuccessKind::Failure, + message: Some(failure.overview()), + ty: None, + description: Some(failure.detail()), + reruns: vec![], + } + } + } + } } impl TestReporter for JunitTestReporter { @@ -46,11 +73,10 @@ impl TestReporter for JunitTestReporter { description.name.clone(), quick_junit::TestCaseStatus::skipped(), ); - let file_name = description.location.file_name.clone(); - let file_name = file_name.strip_prefix("file://").unwrap_or(&file_name); - case - .extra - .insert(String::from("filename"), String::from(file_name)); + case.classname = Some(to_relative_path_or_remote_url( + &self.cwd, + &description.location.file_name, + )); case.extra.insert( String::from("line"), description.location.line_number.to_string(), @@ -60,6 +86,8 @@ impl TestReporter for JunitTestReporter { description.location.column_number.to_string(), ); self.cases.insert(description.id, case); + + self.test_name_tree.add_node(description.clone().into()); } fn report_plan(&mut self, _plan: &TestPlan) {} @@ -89,7 +117,29 @@ impl TestReporter for JunitTestReporter { fn report_uncaught_error(&mut self, _origin: &str, _error: Box) {} - fn report_step_register(&mut self, _description: &TestStepDescription) {} + fn report_step_register(&mut self, description: &TestStepDescription) { + self.test_name_tree.add_node(description.clone().into()); + let test_case_name = + self.test_name_tree.construct_full_test_name(description.id); + + let mut case = quick_junit::TestCase::new( + test_case_name, + quick_junit::TestCaseStatus::skipped(), + ); + case.classname = Some(to_relative_path_or_remote_url( + &self.cwd, + &description.location.file_name, + )); + case.extra.insert( + String::from("line"), + description.location.line_number.to_string(), + ); + case.extra.insert( + String::from("col"), + description.location.column_number.to_string(), + ); + self.cases.insert(description.id, case); + } fn report_step_wait(&mut self, _description: &TestStepDescription) {} @@ -97,43 +147,13 @@ impl TestReporter for JunitTestReporter { &mut self, description: &TestStepDescription, result: &TestStepResult, - _elapsed: u64, + elapsed: u64, _tests: &IndexMap, - test_steps: &IndexMap, + _test_steps: &IndexMap, ) { - let status = match result { - TestStepResult::Ok => "passed", - TestStepResult::Ignored => "skipped", - TestStepResult::Failed(_) => "failure", - }; - - let root_id: usize; - let mut name = String::new(); - { - let mut ancestors = vec![]; - let mut current_desc = description; - loop { - if let Some(d) = test_steps.get(¤t_desc.parent_id) { - ancestors.push(&d.name); - current_desc = d; - } else { - root_id = current_desc.parent_id; - break; - } - } - ancestors.reverse(); - for n in ancestors { - name.push_str(n); - name.push_str(" ... "); - } - name.push_str(&description.name); - } - - if let Some(case) = self.cases.get_mut(&root_id) { - case.add_property(quick_junit::Property::new( - format!("step[{}]", status), - name, - )); + if let Some(case) = self.cases.get_mut(&description.id) { + case.status = Self::convert_step_status(result); + case.set_time(Duration::from_millis(elapsed)); } } @@ -167,44 +187,239 @@ impl TestReporter for JunitTestReporter { &mut self, elapsed: &Duration, tests: &IndexMap, - _test_steps: &IndexMap, + test_steps: &IndexMap, ) -> anyhow::Result<()> { let mut suites: IndexMap = IndexMap::new(); for (id, case) in &self.cases { - if let Some(test) = tests.get(id) { - suites - .entry(test.location.file_name.clone()) - .and_modify(|s| { - s.add_test_case(case.clone()); - }) - .or_insert_with(|| { - quick_junit::TestSuite::new(test.location.file_name.clone()) - .add_test_case(case.clone()) - .to_owned() - }); - } + let abs_filename = match (tests.get(id), test_steps.get(id)) { + (Some(test), _) => &test.location.file_name, + (_, Some(step)) => &step.location.file_name, + (None, None) => { + unreachable!("Unknown test ID '{id}' provided"); + } + }; + + let filename = to_relative_path_or_remote_url(&self.cwd, abs_filename); + + suites + .entry(filename.clone()) + .and_modify(|s| { + s.add_test_case(case.clone()); + }) + .or_insert_with(|| { + let mut suite = quick_junit::TestSuite::new(filename); + suite.add_test_case(case.clone()); + suite + }); } let mut report = quick_junit::Report::new("deno test"); - report.set_time(*elapsed).add_test_suites( - suites - .values() - .cloned() - .collect::>(), - ); + report + .set_time(*elapsed) + .add_test_suites(suites.into_values()); - if self.path == "-" { + if self.output_path == "-" { report .serialize(std::io::stdout()) .with_context(|| "Failed to write JUnit report to stdout")?; } else { - let file = crate::util::fs::create_file(&PathBuf::from(&self.path)) - .context("Failed to open JUnit report file.")?; + let file = + crate::util::fs::create_file(&PathBuf::from(&self.output_path)) + .context("Failed to open JUnit report file.")?; report.serialize(file).with_context(|| { - format!("Failed to write JUnit report to {}", self.path) + format!("Failed to write JUnit report to {}", self.output_path) })?; } Ok(()) } } + +#[derive(Debug, Default)] +struct TestNameTree(IndexMap); + +impl TestNameTree { + fn new() -> Self { + // Pre-allocate some space to avoid excessive reallocations. + Self(IndexMap::with_capacity(256)) + } + + fn add_node(&mut self, node: TestNameTreeNode) { + self.0.insert(node.id, node); + } + + /// Constructs the full test name by traversing the tree from the specified + /// node as a child to its parent nodes. + /// If the provided ID is not found in the tree, or the tree is broken (e.g. + /// a child node refers to a parent node that doesn't exist), this method + /// just panics. + fn construct_full_test_name(&self, id: usize) -> String { + let mut current_id = Some(id); + let mut name_pieces = VecDeque::new(); + + loop { + let Some(id) = current_id else { + break; + }; + + let Some(node) = self.0.get(&id) else { + // The ID specified as a parent node by the child node should exist in + // the tree, but it doesn't. In this case we give up constructing the + // full test name. + unreachable!("Unregistered test ID '{id}' provided"); + }; + + name_pieces.push_front(node.test_name.as_str()); + current_id = node.parent_id; + } + + if name_pieces.is_empty() { + unreachable!("Unregistered test ID '{id}' provided"); + } + + let v: Vec<_> = name_pieces.into(); + v.join(" > ") + } +} + +#[derive(Debug)] +struct TestNameTreeNode { + id: usize, + parent_id: Option, + test_name: String, +} + +impl From for TestNameTreeNode { + fn from(description: TestDescription) -> Self { + Self { + id: description.id, + parent_id: None, + test_name: description.name, + } + } +} + +impl From for TestNameTreeNode { + fn from(description: TestStepDescription) -> Self { + Self { + id: description.id, + parent_id: Some(description.parent_id), + test_name: description.name, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn construct_full_test_name_one_node() { + let mut tree = TestNameTree::new(); + tree.add_node(TestNameTreeNode { + id: 0, + parent_id: None, + test_name: "root".to_string(), + }); + + assert_eq!(tree.construct_full_test_name(0), "root".to_string()); + } + + #[test] + fn construct_full_test_name_two_level_hierarchy() { + let mut tree = TestNameTree::new(); + tree.add_node(TestNameTreeNode { + id: 0, + parent_id: None, + test_name: "root".to_string(), + }); + tree.add_node(TestNameTreeNode { + id: 1, + parent_id: Some(0), + test_name: "child".to_string(), + }); + + assert_eq!(tree.construct_full_test_name(0), "root".to_string()); + assert_eq!(tree.construct_full_test_name(1), "root > child".to_string()); + } + + #[test] + fn construct_full_test_name_three_level_hierarchy() { + let mut tree = TestNameTree::new(); + tree.add_node(TestNameTreeNode { + id: 0, + parent_id: None, + test_name: "root".to_string(), + }); + tree.add_node(TestNameTreeNode { + id: 1, + parent_id: Some(0), + test_name: "child".to_string(), + }); + tree.add_node(TestNameTreeNode { + id: 2, + parent_id: Some(1), + test_name: "grandchild".to_string(), + }); + + assert_eq!(tree.construct_full_test_name(0), "root".to_string()); + assert_eq!(tree.construct_full_test_name(1), "root > child".to_string()); + assert_eq!( + tree.construct_full_test_name(2), + "root > child > grandchild".to_string() + ); + } + + #[test] + fn construct_full_test_name_one_root_two_chains() { + // 0 + // / \ + // 1 2 + // / \ + // 3 4 + let mut tree = TestNameTree::new(); + tree.add_node(TestNameTreeNode { + id: 0, + parent_id: None, + test_name: "root".to_string(), + }); + tree.add_node(TestNameTreeNode { + id: 1, + parent_id: Some(0), + test_name: "child 1".to_string(), + }); + tree.add_node(TestNameTreeNode { + id: 2, + parent_id: Some(0), + test_name: "child 2".to_string(), + }); + tree.add_node(TestNameTreeNode { + id: 3, + parent_id: Some(1), + test_name: "grandchild 1".to_string(), + }); + tree.add_node(TestNameTreeNode { + id: 4, + parent_id: Some(1), + test_name: "grandchild 2".to_string(), + }); + + assert_eq!(tree.construct_full_test_name(0), "root".to_string()); + assert_eq!( + tree.construct_full_test_name(1), + "root > child 1".to_string(), + ); + assert_eq!( + tree.construct_full_test_name(2), + "root > child 2".to_string(), + ); + assert_eq!( + tree.construct_full_test_name(3), + "root > child 1 > grandchild 1".to_string(), + ); + assert_eq!( + tree.construct_full_test_name(4), + "root > child 1 > grandchild 2".to_string(), + ); + } +} diff --git a/cli/tools/test/reporters/pretty.rs b/cli/tools/test/reporters/pretty.rs index 1cd1f084fb..f9121a4825 100644 --- a/cli/tools/test/reporters/pretty.rs +++ b/cli/tools/test/reporters/pretty.rs @@ -28,6 +28,7 @@ impl PrettyTestReporter { echo_output: bool, filter: bool, repl: bool, + cwd: Url, ) -> PrettyTestReporter { PrettyTestReporter { parallel, @@ -37,7 +38,7 @@ impl PrettyTestReporter { filter, repl, scope_test_id: None, - cwd: Url::from_directory_path(std::env::current_dir().unwrap()).unwrap(), + cwd, did_have_user_output: false, started_tests: false, ended_tests: false, diff --git a/cli/tools/test/reporters/tap.rs b/cli/tools/test/reporters/tap.rs index 610f0bec9d..0758686f03 100644 --- a/cli/tools/test/reporters/tap.rs +++ b/cli/tools/test/reporters/tap.rs @@ -23,9 +23,9 @@ pub struct TapTestReporter { } impl TapTestReporter { - pub fn new(is_concurrent: bool) -> TapTestReporter { + pub fn new(cwd: Url, is_concurrent: bool) -> TapTestReporter { TapTestReporter { - cwd: Url::from_directory_path(std::env::current_dir().unwrap()).unwrap(), + cwd, is_concurrent, header: false, planned: 0, diff --git a/tests/integration/test_tests.rs b/tests/integration/test_tests.rs index d5768b5bad..33abdf22c4 100644 --- a/tests/integration/test_tests.rs +++ b/tests/integration/test_tests.rs @@ -283,6 +283,18 @@ itest!(junit { output: "test/pass.junit.out", }); +itest!(junit_nested { + args: "test --reporter junit test/nested_failures.ts", + output: "test/nested_failures.junit.out", + exit_code: 1, +}); + +itest!(junit_multiple_test_files { + args: "test --reporter junit test/pass.ts test/fail.ts", + output: "test/junit_multiple_test_files.junit.out", + exit_code: 1, +}); + #[test] fn junit_path() { let context = TestContextBuilder::new().use_temp_cwd().build(); diff --git a/tests/testdata/test/junit_multiple_test_files.junit.out b/tests/testdata/test/junit_multiple_test_files.junit.out new file mode 100644 index 0000000000..bf6f3eaa4c --- /dev/null +++ b/tests/testdata/test/junit_multiple_test_files.junit.out @@ -0,0 +1,102 @@ +Check file:///[WILDCARD]/test/pass.ts +Check file:///[WILDCARD]/test/fail.ts + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Error + throw new Error(); + ^ + at file:///[WILDCARD]/test/fail.ts:2:9 + + + Error + throw new Error(); + ^ + at file:///[WILDCARD]/test/fail.ts:5:9 + + + Error + throw new Error(); + ^ + at file:///[WILDCARD]/test/fail.ts:8:9 + + + Error + throw new Error(); + ^ + at file:///[WILDCARD]/test/fail.ts:11:9 + + + Error + throw new Error(); + ^ + at file:///[WILDCARD]/test/fail.ts:14:9 + + + Error + throw new Error(); + ^ + at file:///[WILDCARD]/test/fail.ts:17:9 + + + Error + throw new Error(); + ^ + at file:///[WILDCARD]/test/fail.ts:20:9 + + + Error + throw new Error(); + ^ + at file:///[WILDCARD]/test/fail.ts:23:9 + + + Error + throw new Error(); + ^ + at file:///[WILDCARD]/test/fail.ts:26:9 + + + Error + throw new Error(); + ^ + at file:///[WILDCARD]/test/fail.ts:29:9 + + + +error: Test failed diff --git a/tests/testdata/test/nested_failures.junit.out b/tests/testdata/test/nested_failures.junit.out new file mode 100644 index 0000000000..3e4d3c0d4b --- /dev/null +++ b/tests/testdata/test/nested_failures.junit.out @@ -0,0 +1,47 @@ +Check file:///[WILDCARD]/test/nested_failures.ts + + + + + 1 test step failed. + + + 2 test steps failed. + + + + + + + Error: Fail. + throw new Error("Fail."); + ^ + at file:///[WILDCARD]/test/nested_failures.ts:4:11 + [WILDCARD] + + + 1 test step failed. + + + + + Error: Fail. + throw new Error("Fail."); + ^ + at file:///[WILDCARD]/test/nested_failures.ts:12:13 + [WILDCARD] + + + Error: Fail. + throw new Error("Fail."); + ^ + at file:///[WILDCARD]/test/nested_failures.ts:16:11 + [WILDCARD] + + + + + + + +error: Test failed diff --git a/tests/testdata/test/nested_failures.ts b/tests/testdata/test/nested_failures.ts new file mode 100644 index 0000000000..128e48aef7 --- /dev/null +++ b/tests/testdata/test/nested_failures.ts @@ -0,0 +1,23 @@ +Deno.test("parent 1", async (t) => { + await t.step("child 1", () => {}); + await t.step("child 2", () => { + throw new Error("Fail."); + }); +}); + +Deno.test("parent 2", async (t) => { + await t.step("child 1", async (t) => { + await t.step("grandchild 1", () => {}); + await t.step("grandchild 2", () => { + throw new Error("Fail."); + }); + }); + await t.step("child 2", () => { + throw new Error("Fail."); + }); +}); + +Deno.test("parent 3", async (t) => { + await t.step("child 1", () => {}); + await t.step("child 2", () => {}); +}); diff --git a/tests/testdata/test/pass.junit.out b/tests/testdata/test/pass.junit.out index b652dbf85a..af9fd6f6b8 100644 --- a/tests/testdata/test/pass.junit.out +++ b/tests/testdata/test/pass.junit.out @@ -1,38 +1,38 @@ -Check [WILDCARD]/testdata/test/pass.ts +Check file:///[WILDCARD]/test/pass.ts - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +