Skip to content

Fix: Broken sorting for custom columns #715

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 17 additions & 10 deletions src/pytest_html/basereport.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"<td.*?>(.*?)</td>", 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())
Expand Down Expand Up @@ -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'<td class="col-result">{data["result"]}</td>',
f'<td class="col-name">{data["testId"]}</td>',
f'<td class="col-duration">{data["duration"]}</td>',
f'<td class="col-result">{outcome}</td>',
f'<td class="col-testId">{test_id}</td>',
f'<td class="col-duration">{duration}</td>',
f'<td class="col-links">{_process_links(links)}</td>',
]

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
Expand Down
1 change: 0 additions & 1 deletion src/pytest_html/report_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ def __init__(self, config):
self._data = {
"environment": {},
"tests": defaultdict(list),
"resultsTableRow": None,
}

collapsed = config.getini("render_collapsed")
Expand Down
2 changes: 1 addition & 1 deletion src/pytest_html/scripts/dom.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
59 changes: 59 additions & 0 deletions testing/test_e2e.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
"""
Expand All @@ -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(
'<th class="sortable alpha" data-column-type="alpha">Alpha</th>'
)

def pytest_html_results_table_row(report, cells):
data = report.nodeid.split("_")[-1]
cells.append(f'<td class="col-alpha">{data}</td>')
"""
)
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")
2 changes: 1 addition & 1 deletion testing/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")

Expand Down