diff --git a/cli/tools/coverage/reporter.rs b/cli/tools/coverage/reporter.rs index fe44fa4d0f..6b0e5c885e 100644 --- a/cli/tools/coverage/reporter.rs +++ b/cli/tools/coverage/reporter.rs @@ -475,8 +475,14 @@ impl HtmlCoverageReporter { format!("Coverage report for {node}") }; let title = title.replace(std::path::MAIN_SEPARATOR, "/"); + let breadcrumbs_parts = node + .split(std::path::MAIN_SEPARATOR) + .filter(|s| !s.is_empty()) + .collect::>(); let head = self.create_html_head(&title); - let header = self.create_html_header(&title, stats); + let breadcrumb_navigation = + self.create_breadcrumbs_navigation(&breadcrumbs_parts, is_dir); + let header = self.create_html_header(&breadcrumb_navigation, stats); let footer = self.create_html_footer(timestamp); format!( " @@ -513,7 +519,7 @@ impl HtmlCoverageReporter { /// Creates header part of the contents for html report. pub fn create_html_header( &self, - title: &str, + breadcrumb_navigation: &str, stats: &CoverageStats, ) -> String { let CoverageStats { @@ -531,7 +537,7 @@ impl HtmlCoverageReporter { format!( "
-

{title}

+

{breadcrumb_navigation}

{branch_percent:.2}% @@ -681,4 +687,47 @@ impl HtmlCoverageReporter { " ) } + + pub fn create_breadcrumbs_navigation( + &self, + breadcrumbs_parts: &[&str], + is_dir: bool, + ) -> String { + let mut breadcrumbs_html = Vec::new(); + let root_repeats = if is_dir { + breadcrumbs_parts.len() + } else { + breadcrumbs_parts.len() - 1 + }; + + let mut root_url = "../".repeat(root_repeats); + root_url += "index.html"; + breadcrumbs_html.push(format!("All files")); + + for (index, breadcrumb) in breadcrumbs_parts.iter().enumerate() { + let mut full_url = "../".repeat(breadcrumbs_parts.len() - (index + 1)); + + if index == breadcrumbs_parts.len() - 1 { + breadcrumbs_html.push(breadcrumb.to_string()); + continue; + } + + if is_dir { + full_url += "index.html"; + } else { + full_url += breadcrumb; + if index != breadcrumbs_parts.len() - 1 { + full_url += "/index.html"; + } + } + + breadcrumbs_html.push(format!("{breadcrumb}")) + } + + if breadcrumbs_parts.is_empty() { + return String::from("All files"); + } + + breadcrumbs_html.into_iter().collect::>().join(" / ") + } } diff --git a/tests/integration/coverage_tests.rs b/tests/integration/coverage_tests.rs index c5f8b14534..9509ddcb78 100644 --- a/tests/integration/coverage_tests.rs +++ b/tests/integration/coverage_tests.rs @@ -516,7 +516,7 @@ fn test_html_reporter() { output.assert_matches_text("HTML coverage report has been generated at [WILDCARD]/cov/html/index.html\n"); let index_html = tempdir.join("html").join("index.html").read_to_string(); - assert_contains!(index_html, "

Coverage report for all files

"); + assert_contains!(index_html, "

All files

"); assert_contains!(index_html, "baz/"); assert_contains!(index_html, "href='baz/index.html'"); assert_contains!(index_html, "foo.ts"); @@ -525,13 +525,19 @@ fn test_html_reporter() { assert_contains!(index_html, "href='bar.ts.html'"); let foo_ts_html = tempdir.join("html").join("foo.ts.html").read_to_string(); - assert_contains!(foo_ts_html, "

Coverage report for foo.ts

"); + assert_contains!( + foo_ts_html, + "

All files / foo.ts

" + ); // Check that line count has correct title attribute assert_contains!(foo_ts_html, "x1"); assert_contains!(foo_ts_html, "x3"); let bar_ts_html = tempdir.join("html").join("bar.ts.html").read_to_string(); - assert_contains!(bar_ts_html, "

Coverage report for bar.ts

"); + assert_contains!( + bar_ts_html, + "

All files / bar.ts

" + ); // Check in source code is escaped to <T> assert_contains!(bar_ts_html, "<T>"); // Check that line anchors are correctly referenced by line number links @@ -543,7 +549,10 @@ fn test_html_reporter() { .join("baz") .join("index.html") .read_to_string(); - assert_contains!(baz_index_html, "

Coverage report for baz/

"); + assert_contains!( + baz_index_html, + "

All files / baz

" + ); assert_contains!(baz_index_html, "qux.ts"); assert_contains!(baz_index_html, "href='qux.ts.html'"); assert_contains!(baz_index_html, "quux.ts"); @@ -554,7 +563,7 @@ fn test_html_reporter() { .join("baz") .join("qux.ts.html") .read_to_string(); - assert_contains!(baz_qux_ts_html, "

Coverage report for baz/qux.ts

"); + assert_contains!(baz_qux_ts_html, "

All files / baz / qux.ts

"); let baz_quux_ts_html = tempdir .join("html") @@ -563,7 +572,7 @@ fn test_html_reporter() { .read_to_string(); assert_contains!( baz_quux_ts_html, - "

Coverage report for baz/quux.ts

" + "

All files / baz / quux.ts

" ); }