summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNed Batchelder <ned@nedbatchelder.com>2021-10-23 19:01:27 -0400
committerNed Batchelder <ned@nedbatchelder.com>2021-10-23 20:01:48 -0400
commitbd929f953734f3f5d6cf3e0534aa573b44fc45e2 (patch)
treef4acb62cc7983c64dfdf3488e8bc9e82d1c350d4
parentc8ac44828eddfcb444dcdcfc7668370054e96cb9 (diff)
downloadpython-coveragepy-git-bd929f953734f3f5d6cf3e0534aa573b44fc45e2.tar.gz
fix(html): fix a few problems with the html report
- highlights weren't showing - anchored lines were not visible - some j/k motions were broken - clicking the big buttons at the top didn't work
-rw-r--r--coverage/htmlfiles/coverage_html.js30
-rw-r--r--coverage/htmlfiles/pyfile.html4
-rw-r--r--coverage/htmlfiles/style.css8
-rw-r--r--coverage/htmlfiles/style.scss18
-rw-r--r--tests/gold/html/styled/style.css8
-rw-r--r--tests/goldtest.py17
-rw-r--r--tests/test_html.py22
7 files changed, 64 insertions, 43 deletions
diff --git a/coverage/htmlfiles/coverage_html.js b/coverage/htmlfiles/coverage_html.js
index edd0782e..d031e595 100644
--- a/coverage/htmlfiles/coverage_html.js
+++ b/coverage/htmlfiles/coverage_html.js
@@ -19,9 +19,10 @@ function debounce(callback, wait) {
};
function checkVisible(element) {
- var rect = element.getBoundingClientRect();
- var viewHeight = Math.max(document.documentElement.clientHeight, window.innerHeight);
- return !(rect.bottom < 0 || rect.top - viewHeight >= 0);
+ const rect = element.getBoundingClientRect();
+ const viewBottom = Math.max(document.documentElement.clientHeight, window.innerHeight);
+ const viewTop = 30;
+ return !(rect.bottom < viewTop || rect.top >= viewBottom);
}
// Helpers for table sorting
@@ -204,7 +205,7 @@ coverage.pyfile_ready = function () {
// If we're directed to a particular line number, highlight the line.
var frag = location.hash;
if (frag.length > 2 && frag[1] === 't') {
- document.getElementById(frag.substring(1)).classList.add("highlight");
+ document.querySelector(frag).closest(".n").classList.add("highlight");
coverage.set_sel(parseInt(frag.substr(2), 10));
} else {
coverage.set_sel(0);
@@ -245,7 +246,7 @@ coverage.pyfile_ready = function () {
};
coverage.toggle_lines = function (event) {
- const btn = event.target;
+ const btn = event.target.closest("button");
const category = btn.value
const show = !btn.classList.contains("show_" + category);
coverage.set_line_visibilty(category, show);
@@ -271,7 +272,7 @@ coverage.set_line_visibilty = function (category, should_show) {
// Return the nth line div.
coverage.line_elt = function (n) {
- return document.getElementById("t" + n);
+ return document.getElementById("t" + n)?.closest("p");
};
// Set the selection. b and e are line numbers.
@@ -295,7 +296,7 @@ coverage.to_first_chunk = function () {
// Return a string indicating what kind of chunk this line belongs to,
// or null if not a chunk.
coverage.chunk_indicator = function (line_elt) {
- const classes = line_elt.className;
+ const classes = line_elt?.className;
if (!classes) {
return null;
}
@@ -344,11 +345,11 @@ coverage.to_prev_chunk = function () {
// Find the end of the prev colored chunk.
var probe = c.sel_begin-1;
var probe_line = c.line_elt(probe);
- if (probe_line.length === 0) {
+ if (!probe_line) {
return;
}
var chunk_indicator = c.chunk_indicator(probe_line);
- while (probe > 0 && !chunk_indicator) {
+ while (probe > 1 && !chunk_indicator) {
probe--;
probe_line = c.line_elt(probe);
if (!probe_line) {
@@ -364,6 +365,9 @@ coverage.to_prev_chunk = function () {
var prev_indicator = chunk_indicator;
while (prev_indicator === chunk_indicator) {
probe--;
+ if (probe <= 0) {
+ return;
+ }
probe_line = c.line_elt(probe);
prev_indicator = c.chunk_indicator(probe_line);
}
@@ -430,7 +434,7 @@ coverage.to_prev_chunk_nicely = function () {
coverage.select_line_or_chunk = function (lineno) {
var c = coverage;
var probe_line = c.line_elt(lineno);
- if (probe_line.length === 0) {
+ if (!probe_line) {
return;
}
var the_indicator = c.chunk_indicator(probe_line);
@@ -442,7 +446,7 @@ coverage.select_line_or_chunk = function (lineno) {
while (probe > 0 && indicator === the_indicator) {
probe--;
probe_line = c.line_elt(probe);
- if (probe_line.length === 0) {
+ if (!probe_line) {
break;
}
indicator = c.chunk_indicator(probe_line);
@@ -469,7 +473,7 @@ coverage.show_selection = function () {
// Highlight the lines in the chunk
document.querySelectorAll("#source .highlight").forEach(e => e.classList.remove("highlight"));
for (let probe = coverage.sel_begin; probe < coverage.sel_end; probe++) {
- coverage.line_elt(probe).classList.add("highlight");
+ coverage.line_elt(probe).querySelector(".n").classList.add("highlight");
}
coverage.scroll_to_selection();
@@ -479,7 +483,7 @@ coverage.scroll_to_selection = function () {
// Scroll the page if the chunk isn't fully visible.
if (coverage.selection_ends_on_screen() < 2) {
const element = coverage.line_elt(coverage.sel_begin);
- coverage.scroll_window(element.offsetTop - 30);
+ coverage.scroll_window(element.offsetTop - 60);
}
};
diff --git a/coverage/htmlfiles/pyfile.html b/coverage/htmlfiles/pyfile.html
index 523f8483..1f7b6bd2 100644
--- a/coverage/htmlfiles/pyfile.html
+++ b/coverage/htmlfiles/pyfile.html
@@ -75,8 +75,8 @@
<main id="source">
{% for line in lines -%}
{% joined %}
- <p id="t{{line.number}}" class="{{line.css_class}}">
- <span class="n"><a href="#t{{line.number}}">{{line.number}}</a></span>
+ <p class="{{line.css_class}}">
+ <span class="n"><a id="t{{line.number}}" href="#t{{line.number}}">{{line.number}}</a></span>
<span class="t">{{line.html}}&nbsp;</span>
{% if line.context_list %}
<input type="checkbox" id="ctxs{{line.number}}" />
diff --git a/coverage/htmlfiles/style.css b/coverage/htmlfiles/style.css
index 2cabc74c..3b4643a1 100644
--- a/coverage/htmlfiles/style.css
+++ b/coverage/htmlfiles/style.css
@@ -140,7 +140,7 @@ kbd { border: 1px solid black; border-color: #888 #333 #333 #888; padding: .1em
#source { padding: 1em 0 1em 3.5rem; font-family: SFMono-Regular, Menlo, Monaco, Consolas, monospace; }
-#source p { margin-top: -4em; padding-top: 4em; position: relative; white-space: pre; }
+#source p { position: relative; white-space: pre; }
#source p * { box-sizing: border-box; }
@@ -148,7 +148,9 @@ kbd { border: 1px solid black; border-color: #888 #333 #333 #888; padding: .1em
@media (prefers-color-scheme: dark) { #source p .n { color: #777; } }
-#source p .n a { text-decoration: none; color: #999; }
+#source p .n.highlight { background: #ffdd00; }
+
+#source p .n a { margin-top: -4em; padding-top: 4em; text-decoration: none; color: #999; }
@media (prefers-color-scheme: dark) { #source p .n a { color: #777; } }
@@ -156,8 +158,6 @@ kbd { border: 1px solid black; border-color: #888 #333 #333 #888; padding: .1em
@media (prefers-color-scheme: dark) { #source p .n a:hover { color: #777; } }
-#source p.highlight .n { background: #ffdd00; }
-
#source p .t { display: inline-block; width: 100%; box-sizing: border-box; margin-left: -.5em; padding-left: 0.3em; border-left: 0.2em solid #fff; }
@media (prefers-color-scheme: dark) { #source p .t { border-color: #1e1e1e; } }
diff --git a/coverage/htmlfiles/style.scss b/coverage/htmlfiles/style.scss
index 775cdcb6..e4826f31 100644
--- a/coverage/htmlfiles/style.scss
+++ b/coverage/htmlfiles/style.scss
@@ -395,11 +395,6 @@ $border-indicator-width: .2em;
font-family: $font-code;
p {
- // These two lines make anchors to the line scroll the line to be
- // visible beneath the fixed-position header.
- margin-top: -4em;
- padding-top: 4em;
-
// position relative makes position:absolute pop-ups appear in the right place.
position: relative;
white-space: pre;
@@ -418,7 +413,16 @@ $border-indicator-width: .2em;
color: $light-gray4;
@include color-dark($dark-gray4);
+ &.highlight {
+ background: #ffdd00;
+ }
+
a {
+ // These two lines make anchors to the line scroll the line to be
+ // visible beneath the fixed-position header.
+ margin-top: -4em;
+ padding-top: 4em;
+
text-decoration: none;
color: $light-gray4;
@include color-dark($dark-gray4);
@@ -430,10 +434,6 @@ $border-indicator-width: .2em;
}
}
- &.highlight .n {
- background: #ffdd00;
- }
-
.t {
display: inline-block;
width: 100%;
diff --git a/tests/gold/html/styled/style.css b/tests/gold/html/styled/style.css
index 9cbe5fad..910a779e 100644
--- a/tests/gold/html/styled/style.css
+++ b/tests/gold/html/styled/style.css
@@ -140,7 +140,7 @@ kbd { border: 1px solid black; border-color: #888 #333 #333 #888; padding: .1em
#source { padding: 1em 0 1em 3.5rem; font-family: SFMono-Regular, Menlo, Monaco, Consolas, monospace; }
-#source p { margin-top: -4em; padding-top: 4em; position: relative; white-space: pre; }
+#source p { position: relative; white-space: pre; }
#source p * { box-sizing: border-box; }
@@ -148,7 +148,9 @@ kbd { border: 1px solid black; border-color: #888 #333 #333 #888; padding: .1em
@media (prefers-color-scheme: dark) { #source p .n { color: #777; } }
-#source p .n a { text-decoration: none; color: #999; }
+#source p .n.highlight { background: #ffdd00; }
+
+#source p .n a { margin-top: -4em; padding-top: 4em; text-decoration: none; color: #999; }
@media (prefers-color-scheme: dark) { #source p .n a { color: #777; } }
@@ -156,8 +158,6 @@ kbd { border: 1px solid black; border-color: #888 #333 #333 #888; padding: .1em
@media (prefers-color-scheme: dark) { #source p .n a:hover { color: #777; } }
-#source p.highlight .n { background: #ffdd00; }
-
#source p .t { display: inline-block; width: 100%; box-sizing: border-box; margin-left: -.5em; padding-left: 0.3em; border-left: 0.2em solid #fff; }
@media (prefers-color-scheme: dark) { #source p .t { border-color: #1e1e1e; } }
diff --git a/tests/goldtest.py b/tests/goldtest.py
index 6d9ae4b0..57fa5c06 100644
--- a/tests/goldtest.py
+++ b/tests/goldtest.py
@@ -134,12 +134,27 @@ def contains(filename, *strlist):
missing in `filename`.
"""
+ __tracebackhide__ = True # pytest, please don't show me this function.
with open(filename) as fobj:
text = fobj.read()
for s in strlist:
assert s in text, f"Missing content in {filename}: {s!r}"
+def contains_rx(filename, *rxlist):
+ """Check that the file has lines that re.search all of the regexes.
+
+ An assert will be raised if one of the regexes in `rxlist` doesn't match
+ any lines in `filename`.
+
+ """
+ __tracebackhide__ = True # pytest, please don't show me this function.
+ with open(filename) as fobj:
+ lines = fobj.readlines()
+ for rx in rxlist:
+ assert any(re.search(rx, line) for line in lines), f"Missing rx in {filename}: {rx!r}"
+
+
def contains_any(filename, *strlist):
"""Check that the file contains at least one of a list of strings.
@@ -147,6 +162,7 @@ def contains_any(filename, *strlist):
`filename`.
"""
+ __tracebackhide__ = True # pytest, please don't show me this function.
with open(filename) as fobj:
text = fobj.read()
for s in strlist:
@@ -165,6 +181,7 @@ def doesnt_contain(filename, *strlist):
`filename`.
"""
+ __tracebackhide__ = True # pytest, please don't show me this function.
with open(filename) as fobj:
text = fobj.read()
for s in strlist:
diff --git a/tests/test_html.py b/tests/test_html.py
index 84cc2d4c..849be567 100644
--- a/tests/test_html.py
+++ b/tests/test_html.py
@@ -23,7 +23,7 @@ from coverage.report import get_analysis_to_report
from tests.coveragetest import CoverageTest, TESTS_DIR
from tests.goldtest import gold_path
-from tests.goldtest import compare, contains, doesnt_contain, contains_any
+from tests.goldtest import compare, contains, contains_rx, doesnt_contain, contains_any
from tests.helpers import assert_coverage_warnings, change_dir
@@ -893,14 +893,14 @@ assert len(math) == 18
if env.PYBEHAVIOR.pep626:
cov.html_report(partial, directory="out/partial_626")
compare_html(gold_path("html/partial_626"), "out/partial_626")
- contains(
+ contains_rx(
"out/partial_626/partial_py.html",
- '<p id="t4" class="par run show_par">',
- '<p id="t7" class="run">',
+ r'<p class="par run show_par">.* id="t4"',
+ r'<p class="run">.* id="t7"',
# The "if 0" and "if 1" statements are marked as run.
- '<p id="t10" class="run">',
+ r'<p class="run">.* id="t10"',
# The "raise ZeroDivisionError" is excluded by regex in the .ini.
- '<p id="t17" class="exc show_exc">',
+ r'<p class="exc show_exc">.* id="t17"',
)
contains(
"out/partial_626/index.html",
@@ -910,14 +910,14 @@ assert len(math) == 18
else:
cov.html_report(partial, directory="out/partial")
compare_html(gold_path("html/partial"), "out/partial")
- contains(
+ contains_rx(
"out/partial/partial_py.html",
- '<p id="t4" class="par run show_par">',
- '<p id="t7" class="run">',
+ r'<p class="par run show_par">.* id="t4"',
+ r'<p class="run">.* id="t7"',
# The "if 0" and "if 1" statements are optimized away.
- '<p id="t10" class="pln">',
+ r'<p class="pln">.* id="t10"',
# The "raise ZeroDivisionError" is excluded by regex in the .ini.
- '<p id="t17" class="exc show_exc">',
+ r'<p class="exc show_exc">.* id="t17"',
)
contains(
"out/partial/index.html",