diff --git a/README.rst b/README.rst index 449cd815..12b94c16 100644 --- a/README.rst +++ b/README.rst @@ -259,6 +259,28 @@ additional HTML and log output with a notice that the log is empty: del data[:] data.append(html.div('No log output captured.', class_='empty log')) +Modifying the whole test document +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Finally, for major customizations, you may manipulate and update the resulting +html document prior to finalization by hooking :code:`pytest_html_report_final_dom`. + +.. code-block:: python + + import pytest + + def pytest_html_report_final_dom(session, document): + # session is a `_pytest.main.Session` object + from importlib.resources import read_text + js = read_text(".", "fancy.js") + for node in list(iter(document)): + if isinstance(node, html.head): + node.extend([html.script(raw(js), type="text/javascript")]) + + + + + Display options --------------- diff --git a/pytest_html/hooks.py b/pytest_html/hooks.py index 7b75120b..7fe435e9 100644 --- a/pytest_html/hooks.py +++ b/pytest_html/hooks.py @@ -21,3 +21,7 @@ def pytest_html_results_table_row(report, cells): def pytest_html_results_table_html(report, data): """ Called after building results table additional HTML. """ + + +def pytest_html_report_final_dom(session, document): + """ Called after the entire document has been created. """ diff --git a/pytest_html/plugin.py b/pytest_html/plugin.py index e159648a..7f66dc0e 100644 --- a/pytest_html/plugin.py +++ b/pytest_html/plugin.py @@ -544,31 +544,42 @@ def generate_summary_item(self): session.config.hook.pytest_html_report_title(report=self) + summary_prefix, summary_postfix = [], [] + session.config.hook.pytest_html_results_summary( + prefix=summary_prefix, summary=summary, postfix=summary_postfix + ) + summary = [html.h2("Summary"), *summary_prefix, *summary, *summary_postfix] + body = html.body( html.script(raw(main_js)), - html.h1(self.title), - html.p( - "Report generated on {} at {} by ".format( - generated.strftime("%d-%b-%Y"), generated.strftime("%H:%M:%S") + html.div( + html.div( + html.h1(self.title), + html.p( + "Report generated on {} at {} by ".format( + generated.strftime("%d-%b-%Y"), + generated.strftime("%H:%M:%S"), + ), + html.a("pytest-html", href=__pypi_url__), + f" v{__version__}", + ), + html.div( + *self._generate_environment(session.config), + class_="content-environment", + ), + html.div(*summary, class_="content-summary"), + class_="content-header", ), - html.a("pytest-html", href=__pypi_url__), - f" v{__version__}", + html.div(*results, class_="content-results", id="results-container"), + class_="content", ), onLoad="init()", ) - body.extend(self._generate_environment(session.config)) - - summary_prefix, summary_postfix = [], [] - session.config.hook.pytest_html_results_summary( - prefix=summary_prefix, summary=summary, postfix=summary_postfix - ) - body.extend([html.h2("Summary")] + summary_prefix + summary + summary_postfix) - - body.extend(results) - doc = html.html(head, body) + session.config.hook.pytest_html_report_final_dom(session=session, document=doc) + unicode_doc = "\n{}".format(doc.unicode(indent=2)) # Fix encoding issues, e.g. with surrogates diff --git a/pytest_html/resources/main.js b/pytest_html/resources/main.js index c06cb455..018f8132 100644 --- a/pytest_html/resources/main.js +++ b/pytest_html/resources/main.js @@ -141,7 +141,7 @@ function sort_table(clicked, key_func) { sorted_rows.forEach(function(elem) { parent.appendChild(elem); }); - document.getElementsByTagName("BODY")[0].appendChild(parent); + document.getElementById("results-container").appendChild(parent); } function sort(items, key_func, reversed) { diff --git a/testing/js_test_report.html b/testing/js_test_report.html index 25a539e3..6a0b72cb 100644 --- a/testing/js_test_report.html +++ b/testing/js_test_report.html @@ -14,51 +14,53 @@
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ResultTestDurationLinks
Rerunrerun.py::test_rexample_11.00
-
@pytest.mark.flaky(reruns=5)
def test_example():
import random
> assert random.choice([True, False])
E assert False
E + where False = <bound method Random.choice of <random.Random object at 0x7fe80b85f420>>([True, False])
E + where <bound method Random.choice of <random.Random object at 0x7fe80b85f420>> = <module 'random' from '/usr/local/Cellar/python/2.7.12/Frameworks/Python.framework/Versions/2.7/lib/python2.7/random.pyc'>.choice

rerun.py:6: AssertionError
Passedrerun.py::test_example_20.00
-
No log output captured.
-
Passedrerun.py::test_example_30.00
-
No log output captured.
-
-
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ResultTestDurationLinks
Rerunrerun.py::test_rexample_11.00
+
@pytest.mark.flaky(reruns=5)
def test_example():
import random
> assert random.choice([True, False])
E assert False
E + where False = <bound method Random.choice of <random.Random object at 0x7fe80b85f420>>([True, False])
E + where <bound method Random.choice of <random.Random object at 0x7fe80b85f420>> = <module 'random' from '/usr/local/Cellar/python/2.7.12/Frameworks/Python.framework/Versions/2.7/lib/python2.7/random.pyc'>.choice

rerun.py:6: AssertionError
Passedrerun.py::test_example_20.00
+
No log output captured.
+
Passedrerun.py::test_example_30.00
+
No log output captured.
+
+
+