From 1fe1561a7e0cc9f968d2f2c1f13dea5cf2b3387f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jim=20Br=C3=A4nnlund?= Date: Mon, 14 Aug 2023 15:55:53 +0200 Subject: [PATCH] Fix: Broken sorting for custom columns --- src/pytest_html/basereport.py | 27 ++++++++++------ src/pytest_html/report_data.py | 1 - src/pytest_html/scripts/dom.js | 2 +- testing/test_e2e.py | 59 ++++++++++++++++++++++++++++++++++ testing/test_integration.py | 2 +- 5 files changed, 78 insertions(+), 13 deletions(-) diff --git a/src/pytest_html/basereport.py b/src/pytest_html/basereport.py index dde691b5..300fb6cf 100644 --- a/src/pytest_html/basereport.py +++ b/src/pytest_html/basereport.py @@ -149,6 +149,15 @@ def _run_count(self): return f"{counts}/{self._report.collected_items} {'tests' if plural else 'test'} done." + def _hydrate_data(self, data, cells): + for index, cell in enumerate(cells): + # extract column name and data if column is sortable + if "sortable" in self._report.table_header[index]: + name_match = re.search(r"col-(\w+)", cell) + data_match = re.search(r"(.*?)", cell) + if name_match and data_match: + data[name_match.group(1)] = data_match.group(1) + @pytest.hookimpl(trylast=True) def pytest_sessionstart(self, session): self._report.set_data("environment", self._generate_environment()) @@ -193,35 +202,33 @@ def pytest_runtest_logreport(self, report): ) outcome = _process_outcome(report) - data = { - "result": outcome, - "duration": _format_duration(report.duration), - } + duration = _format_duration(report.duration) self._report.total_duration += report.duration test_id = report.nodeid if report.when != "call": test_id += f"::{report.when}" - data["testId"] = test_id - data["extras"] = self._process_extras(report, test_id) + data = { + "extras": self._process_extras(report, test_id), + } links = [ extra for extra in data["extras"] if extra["format_type"] in ["json", "text", "url"] ] cells = [ - f'{data["result"]}', - f'{data["testId"]}', - f'{data["duration"]}', + f'{outcome}', + f'{test_id}', + f'{duration}', f'{_process_links(links)}', ] - self._config.hook.pytest_html_results_table_row(report=report, cells=cells) if not cells: return cells = _fix_py(cells) + self._hydrate_data(data, cells) data["resultsTableRow"] = cells # don't count passed setups and teardowns diff --git a/src/pytest_html/report_data.py b/src/pytest_html/report_data.py index 2cd01705..b3a70809 100644 --- a/src/pytest_html/report_data.py +++ b/src/pytest_html/report_data.py @@ -41,7 +41,6 @@ def __init__(self, config): self._data = { "environment": {}, "tests": defaultdict(list), - "resultsTableRow": None, } collapsed = config.getini("render_collapsed") diff --git a/src/pytest_html/scripts/dom.js b/src/pytest_html/scripts/dom.js index febcd196..6bcb9d00 100644 --- a/src/pytest_html/scripts/dom.js +++ b/src/pytest_html/scripts/dom.js @@ -36,7 +36,7 @@ const dom = { return envRow }, - getResultTBody: ({ testId, id, log, duration, extras, resultsTableRow, tableHtml, result, collapsed }) => { + getResultTBody: ({ testId, id, log, extras, resultsTableRow, tableHtml, result, collapsed }) => { const resultBody = templateResult.content.cloneNode(true) resultBody.querySelector('tbody').classList.add(result.toLowerCase()) resultBody.querySelector('tbody').id = testId diff --git a/testing/test_e2e.py b/testing/test_e2e.py index de09990f..dbca2074 100644 --- a/testing/test_e2e.py +++ b/testing/test_e2e.py @@ -54,6 +54,23 @@ def _encode_query_params(params): return urllib.parse.urlencode(params) +def _parse_result_table(driver): + table = driver.find_element(By.ID, "results-table") + headers = table.find_elements(By.CSS_SELECTOR, "thead th") + rows = table.find_elements(By.CSS_SELECTOR, "tbody tr.collapsible") + table_data = [] + for row in rows: + data_dict = {} + + cells = row.find_elements(By.TAG_NAME, "td") + for header, cell in zip(headers, cells): + data_dict[header.text.lower()] = cell.text + + table_data.append(data_dict) + + return table_data + + def test_visible(pytester, path, driver): pytester.makepyfile( """ @@ -76,3 +93,45 @@ def test_pass_two(): pass ) result = driver.find_elements(By.CSS_SELECTOR, "tr.collapsible") assert_that(result).is_length(0) + + +def test_custom_sorting(pytester, path, driver): + pytester.makeconftest( + """ + def pytest_html_results_table_header(cells): + cells.append( + 'Alpha' + ) + + def pytest_html_results_table_row(report, cells): + data = report.nodeid.split("_")[-1] + cells.append(f'{data}') + """ + ) + pytester.makepyfile( + """ + def test_AAA(): pass + def test_BBB(): pass + """ + ) + query_params = _encode_query_params({"sort": "alpha"}) + driver.get(f"file:///reports{path()}?{query_params}") + WebDriverWait(driver, 5).until( + ec.visibility_of_all_elements_located((By.CSS_SELECTOR, "#results-table")) + ) + + rows = _parse_result_table(driver) + assert_that(rows).is_length(2) + assert_that(rows[0]["test"]).contains("AAA") + assert_that(rows[0]["alpha"]).is_equal_to("AAA") + assert_that(rows[1]["test"]).contains("BBB") + assert_that(rows[1]["alpha"]).is_equal_to("BBB") + + driver.find_element(By.CSS_SELECTOR, "th[data-column-type='alpha']").click() + # we might need some wait here to ensure sorting happened + rows = _parse_result_table(driver) + assert_that(rows).is_length(2) + assert_that(rows[0]["test"]).contains("BBB") + assert_that(rows[0]["alpha"]).is_equal_to("BBB") + assert_that(rows[1]["test"]).contains("AAA") + assert_that(rows[1]["alpha"]).is_equal_to("AAA") diff --git a/testing/test_integration.py b/testing/test_integration.py index c2e147ae..1bc272bd 100644 --- a/testing/test_integration.py +++ b/testing/test_integration.py @@ -358,7 +358,7 @@ def test_function(arg): page = run(pytester) assert_results(page, error=1, total_tests=0) - col_name = get_text(page, "td[class='col-name']") + col_name = get_text(page, "td[class='col-testId']") assert_that(col_name).contains("::setup") assert_that(get_log(page)).contains("ValueError")