Skip to content

Commit

Permalink
Fix: Broken sorting for custom columns (pytest-dev#715)
Browse files Browse the repository at this point in the history
  • Loading branch information
BeyondEvil authored Aug 24, 2023
1 parent 5e3cca1 commit abde929
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 13 deletions.
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

0 comments on commit abde929

Please sign in to comment.