mirror of
https://github.com/denoland/deno.git
synced 2024-11-22 15:06:54 -05:00
029bdf0cd5
This commit adds a "dot" reporter to "deno test" subcommand, that can be activated using "--dot" flag. It provides a concise output using: - "." for passing test - "," for ignored test - "!" for failing test User output is silenced and not printed to the console. In non-TTY environments each result is printed on a separate line.
210 lines
5.9 KiB
Rust
210 lines
5.9 KiB
Rust
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
|
|
|
use super::fmt::format_test_error;
|
|
use super::fmt::to_relative_path_or_remote_url;
|
|
use super::*;
|
|
|
|
pub(super) fn format_test_step_ancestry(
|
|
desc: &TestStepDescription,
|
|
tests: &IndexMap<usize, TestDescription>,
|
|
test_steps: &IndexMap<usize, TestStepDescription>,
|
|
) -> String {
|
|
let root;
|
|
let mut ancestor_names = vec![];
|
|
let mut current_desc = desc;
|
|
loop {
|
|
if let Some(step_desc) = test_steps.get(¤t_desc.parent_id) {
|
|
ancestor_names.push(&step_desc.name);
|
|
current_desc = step_desc;
|
|
} else {
|
|
root = tests.get(¤t_desc.parent_id).unwrap();
|
|
break;
|
|
}
|
|
}
|
|
ancestor_names.reverse();
|
|
let mut result = String::new();
|
|
result.push_str(&root.name);
|
|
result.push_str(" ... ");
|
|
for name in ancestor_names {
|
|
result.push_str(name);
|
|
result.push_str(" ... ");
|
|
}
|
|
result.push_str(&desc.name);
|
|
result
|
|
}
|
|
|
|
pub fn format_test_for_summary(cwd: &Url, desc: &TestDescription) -> String {
|
|
format!(
|
|
"{} {}",
|
|
&desc.name,
|
|
colors::gray(format!(
|
|
"=> {}:{}:{}",
|
|
to_relative_path_or_remote_url(cwd, &desc.location.file_name),
|
|
desc.location.line_number,
|
|
desc.location.column_number
|
|
))
|
|
)
|
|
}
|
|
|
|
pub fn format_test_step_for_summary(
|
|
cwd: &Url,
|
|
desc: &TestStepDescription,
|
|
tests: &IndexMap<usize, TestDescription>,
|
|
test_steps: &IndexMap<usize, TestStepDescription>,
|
|
) -> String {
|
|
let long_name = format_test_step_ancestry(desc, tests, test_steps);
|
|
format!(
|
|
"{} {}",
|
|
long_name,
|
|
colors::gray(format!(
|
|
"=> {}:{}:{}",
|
|
to_relative_path_or_remote_url(cwd, &desc.location.file_name),
|
|
desc.location.line_number,
|
|
desc.location.column_number
|
|
))
|
|
)
|
|
}
|
|
|
|
pub(super) fn report_sigint(
|
|
cwd: &Url,
|
|
tests_pending: &HashSet<usize>,
|
|
tests: &IndexMap<usize, TestDescription>,
|
|
test_steps: &IndexMap<usize, TestStepDescription>,
|
|
) {
|
|
if tests_pending.is_empty() {
|
|
return;
|
|
}
|
|
let mut formatted_pending = BTreeSet::new();
|
|
for id in tests_pending {
|
|
if let Some(desc) = tests.get(id) {
|
|
formatted_pending.insert(format_test_for_summary(cwd, desc));
|
|
}
|
|
if let Some(desc) = test_steps.get(id) {
|
|
formatted_pending
|
|
.insert(format_test_step_for_summary(cwd, desc, tests, test_steps));
|
|
}
|
|
}
|
|
println!(
|
|
"\n{} The following tests were pending:\n",
|
|
colors::intense_blue("SIGINT")
|
|
);
|
|
for entry in formatted_pending {
|
|
println!("{}", entry);
|
|
}
|
|
println!();
|
|
}
|
|
|
|
pub(super) fn report_summary(
|
|
cwd: &Url,
|
|
summary: &TestSummary,
|
|
elapsed: &Duration,
|
|
) {
|
|
if !summary.failures.is_empty() || !summary.uncaught_errors.is_empty() {
|
|
#[allow(clippy::type_complexity)] // Type alias doesn't look better here
|
|
let mut failures_by_origin: BTreeMap<
|
|
String,
|
|
(Vec<(&TestDescription, &TestFailure)>, Option<&JsError>),
|
|
> = BTreeMap::default();
|
|
let mut failure_titles = vec![];
|
|
for (description, failure) in &summary.failures {
|
|
let (failures, _) = failures_by_origin
|
|
.entry(description.origin.clone())
|
|
.or_default();
|
|
failures.push((description, failure));
|
|
}
|
|
|
|
for (origin, js_error) in &summary.uncaught_errors {
|
|
let (_, uncaught_error) =
|
|
failures_by_origin.entry(origin.clone()).or_default();
|
|
let _ = uncaught_error.insert(js_error.as_ref());
|
|
}
|
|
|
|
// note: the trailing whitespace is intentional to get a red background
|
|
println!("\n{}\n", colors::white_bold_on_red(" ERRORS "));
|
|
for (origin, (failures, uncaught_error)) in failures_by_origin {
|
|
for (description, failure) in failures {
|
|
if !failure.hide_in_summary() {
|
|
let failure_title = format_test_for_summary(cwd, description);
|
|
println!("{}", &failure_title);
|
|
println!("{}: {}", colors::red_bold("error"), failure.to_string());
|
|
println!();
|
|
failure_titles.push(failure_title);
|
|
}
|
|
}
|
|
if let Some(js_error) = uncaught_error {
|
|
let failure_title = format!(
|
|
"{} (uncaught error)",
|
|
to_relative_path_or_remote_url(cwd, &origin)
|
|
);
|
|
println!("{}", &failure_title);
|
|
println!(
|
|
"{}: {}",
|
|
colors::red_bold("error"),
|
|
format_test_error(js_error)
|
|
);
|
|
println!("This error was not caught from a test and caused the test runner to fail on the referenced module.");
|
|
println!("It most likely originated from a dangling promise, event/timeout handler or top-level code.");
|
|
println!();
|
|
failure_titles.push(failure_title);
|
|
}
|
|
}
|
|
// note: the trailing whitespace is intentional to get a red background
|
|
println!("{}\n", colors::white_bold_on_red(" FAILURES "));
|
|
for failure_title in failure_titles {
|
|
println!("{failure_title}");
|
|
}
|
|
}
|
|
|
|
let status = if summary.has_failed() {
|
|
colors::red("FAILED").to_string()
|
|
} else {
|
|
colors::green("ok").to_string()
|
|
};
|
|
|
|
let get_steps_text = |count: usize| -> String {
|
|
if count == 0 {
|
|
String::new()
|
|
} else if count == 1 {
|
|
" (1 step)".to_string()
|
|
} else {
|
|
format!(" ({count} steps)")
|
|
}
|
|
};
|
|
|
|
let mut summary_result = String::new();
|
|
|
|
write!(
|
|
summary_result,
|
|
"{} passed{} | {} failed{}",
|
|
summary.passed,
|
|
get_steps_text(summary.passed_steps),
|
|
summary.failed,
|
|
get_steps_text(summary.failed_steps),
|
|
)
|
|
.unwrap();
|
|
|
|
let ignored_steps = get_steps_text(summary.ignored_steps);
|
|
if summary.ignored > 0 || !ignored_steps.is_empty() {
|
|
write!(
|
|
summary_result,
|
|
" | {} ignored{}",
|
|
summary.ignored, ignored_steps
|
|
)
|
|
.unwrap()
|
|
}
|
|
|
|
if summary.measured > 0 {
|
|
write!(summary_result, " | {} measured", summary.measured,).unwrap();
|
|
}
|
|
|
|
if summary.filtered_out > 0 {
|
|
write!(summary_result, " | {} filtered out", summary.filtered_out).unwrap()
|
|
};
|
|
|
|
println!(
|
|
"\n{} | {} {}\n",
|
|
status,
|
|
summary_result,
|
|
colors::gray(format!("({})", display::human_elapsed(elapsed.as_millis()))),
|
|
);
|
|
}
|