diff options
author | Ned Batchelder <ned@nedbatchelder.com> | 2019-10-12 19:37:58 -0400 |
---|---|---|
committer | Ned Batchelder <ned@nedbatchelder.com> | 2019-10-13 12:30:25 -0400 |
commit | eba18707241a052419d54cdc304aea346aa0605c (patch) | |
tree | 2e8a9c1391aa7d293bebacf5ea86169319eafda9 | |
parent | bcbdf41c6bb9d4ee30842bd14d45c7d48c2c6d01 (diff) | |
download | python-coveragepy-git-eba18707241a052419d54cdc304aea346aa0605c.tar.gz |
Better presentation of contexts. #855
-rw-r--r-- | CHANGES.rst | 5 | ||||
-rw-r--r-- | coverage/html.py | 15 | ||||
-rw-r--r-- | coverage/htmlfiles/pyfile.html | 17 | ||||
-rw-r--r-- | coverage/htmlfiles/style.scss | 95 | ||||
-rw-r--r-- | tests/test_html.py | 14 |
5 files changed, 104 insertions, 42 deletions
diff --git a/CHANGES.rst b/CHANGES.rst index eb130b3e..a5a86caa 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -20,6 +20,10 @@ development at the same time, such as 4.5.x and 5.0. Unreleased ---------- +- The HTML report has been reimplemented (no more table around the source + code). This allowed for a better presentation of the context information, + hopefully resolving `issue 855`_. + - Added sqlite3 module version information to ``coverage debug sys`` output. - Asking the HTML report to show contexts (``[html] show_contexts=True`` or @@ -27,6 +31,7 @@ Unreleased contexts measured (`issue 851`_). .. _issue 851: https://github.com/nedbat/coveragepy/issues/851 +.. _issue 855: https://github.com/nedbat/coveragepy/issues/855 .. _changes_50a8: diff --git a/coverage/html.py b/coverage/html.py index 5b4c6ca9..4a0e1a33 100644 --- a/coverage/html.py +++ b/coverage/html.py @@ -77,6 +77,8 @@ def write_html(fname, html): class HtmlDataGeneration(object): """Generate structured data to be turned into HTML reports.""" + EMPTY = "(empty)" + def __init__(self, cov): self.coverage = cov self.config = self.coverage.config @@ -121,10 +123,15 @@ class HtmlDataGeneration(object): elif lineno in analysis.statements: category = 'run' + contexts = contexts_label = None + context_list = None if category and self.config.show_contexts: - contexts = sorted(c or "(empty)" for c in contexts_by_lineno[lineno]) - else: - contexts = None + contexts = sorted(c or self.EMPTY for c in contexts_by_lineno[lineno]) + if contexts == [self.EMPTY]: + contexts_label = self.EMPTY + else: + contexts_label = "{} ctx".format(len(contexts)) + context_list = contexts lines.append(SimpleNamespace( tokens=tokens, @@ -132,6 +139,8 @@ class HtmlDataGeneration(object): category=category, statement=(lineno in analysis.statements), contexts=contexts, + contexts_label=contexts_label, + context_list=context_list, short_annotations=short_annotations, long_annotations=long_annotations, )) diff --git a/coverage/htmlfiles/pyfile.html b/coverage/htmlfiles/pyfile.html index 2154c06c..85f79f18 100644 --- a/coverage/htmlfiles/pyfile.html +++ b/coverage/htmlfiles/pyfile.html @@ -79,11 +79,18 @@ <span class="annotate long">{{line.annotate_long}}</span>{#-#} {% endif -%} {% if line.contexts -%} - <span class="context-button">{{ line.contexts|len }} ctx</span>{#-#} - <span class="context-list"> - {% for context in line.contexts -%} - <span class="context-line">{{context}}</span>{#-#} - {% endfor -%} + <span class="ctx"> + {% if line.context_list -%} + <input type="checkbox" id="ctxs{{line.number}}" /> + {% endif -%} + <label for="ctxs{{line.number}}">{{ line.contexts_label }}</label>{#-#} + {% if line.context_list -%} + <span class="ctxs"> + {% for context in line.context_list -%} + <span>{{context}}</span>{#-#} + {% endfor -%} + </span>{#-#} + {% endif -%} </span>{#-#} {% endif -%} </p> diff --git a/coverage/htmlfiles/style.scss b/coverage/htmlfiles/style.scss index 33ec38e4..3e4283e8 100644 --- a/coverage/htmlfiles/style.scss +++ b/coverage/htmlfiles/style.scss @@ -95,7 +95,7 @@ a.nav { margin: 1em 0 0 3em; } -/* Header styles */ +// Header styles #header .content { padding: 1em $left-gutter; } @@ -242,17 +242,25 @@ $mis-hover-color: mix($mis-color, #000, $hover-dark-amt); $run-hover-color: mix($run-color, #000, $hover-dark-amt); $exc-hover-color: mix($exc-color, #000, $hover-dark-amt); $par-hover-color: mix($par-color, #000, $hover-dark-amt); + +// The slim bar at the left edge of the source lines, colored by coverage. $border-indicator-width: .2em; +$context-panel-color: #aaeeff; + #source { padding: 1em 0 1em $left-gutter; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; p { - /* position relative makes position:absolute pop-ups appear in the right place. */ + // position relative makes position:absolute pop-ups appear in the right place. position: relative; white-space: pre; + * { + box-sizing: border-box; + } + .n { float: left; text-align: right; @@ -387,42 +395,69 @@ $border-indicator-width: .2em; float: right; padding-right: .5em; } - } -} + .ctx { + font-family: verdana, sans-serif; + white-space: nowrap; -// Line contexts -td.contexts { - p { - margin: 0; - padding: 0 .5em; - color: #999999; - font-family: verdana, sans-serif; - white-space: nowrap; - position: relative; - &:hover { - background: #eee; - } - span.context-list { - @extend %in-text-popup; - min-width: 30em; + input { + display: none; - span.context-line { - display: block; + & ~ label { + cursor: pointer; + border-radius: .25em; + &::before { + content: "▶ "; + } + } + + &:checked ~ label { + background: $context-panel-color; + color: #666; + border-radius: .75em .75em 0 0; + &::before { + content: "▼ "; + } + } + + & ~ .ctxs { + display: none; + } + + &:checked ~ .ctxs { + display: block; + } + } + + label { + color: #999; + position: absolute; + right: 2.5em; + display: inline-block; + text-align: right; + font-size: .8333em; // 10/12 + padding: .25em .5em; + &:hover { + background: mix($context-panel-color, #fff, 50%); + color: #666; + } + } + + .ctxs { + font-family: verdana, sans-serif; + background: $context-panel-color; + padding: .25em .5em; + border-radius: .25em; + margin-right: 1.75em; + span { + display: block; + } } - } - &:hover span.context-list { - display: block; - } - span.context-button { - display: inline-block; - cursor: pointer; - font-size: .8333em; // 10/12 - line-height: 1em; } } } + // index styles #index { td, th { diff --git a/tests/test_html.py b/tests/test_html.py index 29f81a05..c9f82062 100644 --- a/tests/test_html.py +++ b/tests/test_html.py @@ -1038,6 +1038,8 @@ assert len(math) == 18 class HtmlWithContextsTest(HtmlTestHelpers, CoverageTest): """Tests of the HTML reports with shown contexts.""" + EMPTY = coverage.html.HtmlDataGeneration.EMPTY + def html_data_from_cov(self, cov, morf): """Get HTML report data from a `Coverage` object for a morf.""" with self.assert_warnings(cov, []): @@ -1083,11 +1085,15 @@ class HtmlWithContextsTest(HtmlTestHelpers, CoverageTest): cov.set_option("html:show_contexts", True) mod = self.start_import_stop(cov, "two_tests") d = self.html_data_from_cov(cov, mod) - - context_labels = ['(empty)', 'two_tests.test_one', 'two_tests.test_two'] + from coverage.debug import pp + pp(d) + context_labels = [self.EMPTY, 'two_tests.test_one', 'two_tests.test_two'] expected_lines = [self.OUTER_LINES, self.TEST_ONE_LINES, self.TEST_TWO_LINES] for label, expected in zip(context_labels, expected_lines): - actual = [ld.number for ld in d.lines if label in (ld.contexts or ())] + actual = [ + ld.number for ld in d.lines + if label == ld.contexts_label or label in (ld.contexts or ()) + ] assert sorted(expected) == sorted(actual) def test_filtered_dynamic_contexts(self): @@ -1099,7 +1105,7 @@ class HtmlWithContextsTest(HtmlTestHelpers, CoverageTest): mod = self.start_import_stop(cov, "two_tests") d = self.html_data_from_cov(cov, mod) - context_labels = ['(empty)', 'two_tests.test_one', 'two_tests.test_two'] + context_labels = [self.EMPTY, 'two_tests.test_one', 'two_tests.test_two'] expected_lines = [[], self.TEST_ONE_LINES, []] for label, expected in zip(context_labels, expected_lines): actual = [ld.number for ld in d.lines if label in (ld.contexts or ())] |