From aff29f92cb203a4585ab3e007ff5eaccf3ed46b4 Mon Sep 17 00:00:00 2001 From: syguts Date: Thu, 21 Apr 2022 12:39:56 +0200 Subject: [PATCH 1/2] Original test cases order kept Because that sometimes somebody could want to have a possibility to keep the original order of test cases in the report, that possibility was added. The logic is control by specific flag in config file. 1. Added necessary option flag in config. 2. Added necessary logic if flag is set to True. 3. Added necessary tests for new logic. 4. Added additional section in documentation which describes how to turn on the new behaviour. --- docs/user_guide.rst | 15 +++++ src/pytest_html/html_report.py | 29 ++++++-- src/pytest_html/plugin.py | 7 ++ testing/test_pytest_html.py | 120 +++++++++++++++++++++++++++++++++ 4 files changed, 164 insertions(+), 7 deletions(-) diff --git a/docs/user_guide.rst b/docs/user_guide.rst index 57649250..b3ce5fdf 100644 --- a/docs/user_guide.rst +++ b/docs/user_guide.rst @@ -261,6 +261,21 @@ or by setting the :code:`render_collapsed` in a configuration file (pytest.ini, **NOTE:** Setting :code:`render_collapsed` will, unlike the query parameter, affect all statuses. + +Keep original test cases run order +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +By default, all rows in the **Results** table will be sorted by category. Failed test cases will be placed at the top of the **Results** table. + +This behavior can be customized by setting the :code:`keep_original_order` in a configuration file (pytest.ini, setup.cfg, etc). + +.. code-block:: ini + + [pytest] + keep_original_order = True + +**NOTE:** Setting :code:`keep_original_order` will turn off a possibility of changing order by table headers. + Controlling Test Result Visibility Via Query Params ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/src/pytest_html/html_report.py b/src/pytest_html/html_report.py index 66e10f07..09e660eb 100644 --- a/src/pytest_html/html_report.py +++ b/src/pytest_html/html_report.py @@ -36,15 +36,20 @@ def __init__(self, logfile, config): def _appendrow(self, outcome, report): result = TestResult(outcome, report, self.logfile, self.config) if result.row_table is not None: - index = bisect.bisect_right(self.results, result) - self.results.insert(index, result) tbody = html.tbody( result.row_table, class_="{} results-table-row".format(result.outcome.lower()), ) if result.row_extra is not None: tbody.append(result.row_extra) - self.test_logs.insert(index, tbody) + + if self.config.getini("keep_original_order"): + self.results.append(result) + self.test_logs.append(tbody) + else: + index = bisect.bisect_right(self.results, result) + self.results.insert(index, result) + self.test_logs.insert(index, tbody) def append_passed(self, report): if report.when == "call": @@ -145,11 +150,21 @@ def _generate_report(self, session): if i < len(outcomes): summary.append(", ") + sortable_class_attrib = "sortable" + initial_sort_class_attrib = "initial-sort" + if self.config.getini("keep_original_order"): + sortable_class_attrib = "" + initial_sort_class_attrib = "" + cells = [ - html.th("Result", class_="sortable result initial-sort", col="result"), - html.th("Test", class_="sortable", col="name"), - html.th("Duration", class_="sortable", col="duration"), - html.th("Links", class_="sortable links", col="links"), + html.th( + "Result", + class_=f"{sortable_class_attrib} result {initial_sort_class_attrib}", + col="result", + ), + html.th("Test", class_=f"{sortable_class_attrib}", col="name"), + html.th("Duration", class_=f"{sortable_class_attrib}", col="duration"), + html.th("Links", class_=f"{sortable_class_attrib} links", col="links"), ] session.config.hook.pytest_html_results_table_header(cells=cells) diff --git a/src/pytest_html/plugin.py b/src/pytest_html/plugin.py index 0034da19..80c74488 100644 --- a/src/pytest_html/plugin.py +++ b/src/pytest_html/plugin.py @@ -59,6 +59,13 @@ def pytest_addoption(parser): help="A list of regexes corresponding to environment " "table variables whose values should be redacted from the report", ) + parser.addini( + "keep_original_order", + type="bool", + default=False, + help="Keep original order of test cases run in report. " + "That option turns off a possibility of order rows by table headers.", + ) def pytest_configure(config): diff --git a/testing/test_pytest_html.py b/testing/test_pytest_html.py index b13dd4c6..c6555f53 100644 --- a/testing/test_pytest_html.py +++ b/testing/test_pytest_html.py @@ -1095,6 +1095,126 @@ def test_pass(): assert len(re.findall(collapsed_html, html)) == expected_count assert_results(html, tests=2, passed=1, failed=1) + @pytest.mark.parametrize("keep_original_order, expected_table_header_tag", [ + ( + False, + [ + { + 'html_tag': '', + 'expected_count': 4 + }, + { + 'html_tag': '', + 'expected_count': 1 + } + ] + ), + ( + True, + [ + { + 'html_tag': '', + 'expected_count': 0 + }, + { + 'html_tag': '', + 'expected_count': 0 + } + ] + ) + ], ids=( + "keep_original_order option is not set", + "keep_original_order option is set", + )) + def test_keep_original_order_option_remove_any_sort_class_from_headers( + self, testdir, keep_original_order, expected_table_header_tag + ): + testdir.makeini( + f""" + [pytest] + keep_original_order = {keep_original_order} + """ + ) + testdir.makepyfile( + """ + def test_pass1(): + assert True + + def test_fail1(): + assert False + + def test_pass2(): + assert True + + def test_fail2(): + assert False + """ + ) + result, html = run(testdir) + assert result.ret == 1 + for expect_element in expected_table_header_tag: + assert len( + re.findall(expect_element["html_tag"], html) + ) == expect_element["expected_count"] + assert_results(html, tests=4, passed=2, failed=2) + + @pytest.mark.parametrize("keep_original_order, expected_order", [ + ( + False, + [ + "test_fail1", + "test_fail2", + "test_pass1", + "test_pass2", + ] + ), + ( + True, + [ + "test_pass1", + "test_fail1", + "test_pass2", + "test_fail2", + ] + ) + ], ids=( + "keep_original_order option is not set", + "keep_original_order option is set", + )) + def test_keep_original_order_option_hold_test_run_order( + self, testdir, keep_original_order, expected_order + ): + testdir.makeini( + f""" + [pytest] + keep_original_order = {keep_original_order} + """ + ) + testdir.makepyfile( + """ + def test_pass1(): + assert True + + def test_fail1(): + assert False + + def test_pass2(): + assert True + + def test_fail2(): + assert False + """ + ) + result, html = run(testdir) + assert result.ret == 1 + result_report_test_order = re.findall('.*', html) + assert len(result_report_test_order) == len(expected_order) + for expected_test_name, result_test_name in zip( + expected_order, result_report_test_order + ): + assert expected_test_name in result_test_name + assert_results(html, tests=4, passed=2, failed=2) + def test_setup_and_teardown_in_html(self, testdir): testdir.makepyfile( """ From 840af35c24650cd7a086c888d1ddc155bb99167a Mon Sep 17 00:00:00 2001 From: syguts Date: Fri, 22 Apr 2022 11:03:30 +0200 Subject: [PATCH 2/2] Original test cases order kept 1. Code formatting fixes via black --- testing/test_pytest_html.py | 85 ++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 44 deletions(-) diff --git a/testing/test_pytest_html.py b/testing/test_pytest_html.py index c6555f53..1157d888 100644 --- a/testing/test_pytest_html.py +++ b/testing/test_pytest_html.py @@ -1095,39 +1095,31 @@ def test_pass(): assert len(re.findall(collapsed_html, html)) == expected_count assert_results(html, tests=2, passed=1, failed=1) - @pytest.mark.parametrize("keep_original_order, expected_table_header_tag", [ - ( + @pytest.mark.parametrize( + "keep_original_order, expected_table_header_tag", + [ + ( False, [ - { - 'html_tag': '', - 'expected_count': 4 - }, - { - 'html_tag': '', - 'expected_count': 1 - } - ] - ), - ( + {"html_tag": '', "expected_count": 4}, + {"html_tag": '', "expected_count": 1}, + ], + ), + ( True, [ - { - 'html_tag': '', - 'expected_count': 0 - }, - { - 'html_tag': '', - 'expected_count': 0 - } - ] - ) - ], ids=( - "keep_original_order option is not set", - "keep_original_order option is set", - )) + {"html_tag": '', "expected_count": 0}, + {"html_tag": '', "expected_count": 0}, + ], + ), + ], + ids=( + "keep_original_order option is not set", + "keep_original_order option is set", + ), + ) def test_keep_original_order_option_remove_any_sort_class_from_headers( - self, testdir, keep_original_order, expected_table_header_tag + self, testdir, keep_original_order, expected_table_header_tag ): testdir.makeini( f""" @@ -1153,36 +1145,41 @@ def test_fail2(): result, html = run(testdir) assert result.ret == 1 for expect_element in expected_table_header_tag: - assert len( - re.findall(expect_element["html_tag"], html) - ) == expect_element["expected_count"] + assert ( + len(re.findall(expect_element["html_tag"], html)) + == expect_element["expected_count"] + ) assert_results(html, tests=4, passed=2, failed=2) - @pytest.mark.parametrize("keep_original_order, expected_order", [ - ( + @pytest.mark.parametrize( + "keep_original_order, expected_order", + [ + ( False, [ "test_fail1", "test_fail2", "test_pass1", "test_pass2", - ] - ), - ( + ], + ), + ( True, [ "test_pass1", "test_fail1", "test_pass2", "test_fail2", - ] - ) - ], ids=( - "keep_original_order option is not set", - "keep_original_order option is set", - )) + ], + ), + ], + ids=( + "keep_original_order option is not set", + "keep_original_order option is set", + ), + ) def test_keep_original_order_option_hold_test_run_order( - self, testdir, keep_original_order, expected_order + self, testdir, keep_original_order, expected_order ): testdir.makeini( f""" @@ -1210,7 +1207,7 @@ def test_fail2(): result_report_test_order = re.findall('.*', html) assert len(result_report_test_order) == len(expected_order) for expected_test_name, result_test_name in zip( - expected_order, result_report_test_order + expected_order, result_report_test_order ): assert expected_test_name in result_test_name assert_results(html, tests=4, passed=2, failed=2)