Skip to content
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
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ instance/

# Scrapy stuff:
.scrapy
.git

# Sphinx documentation
docs/_build/
Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ shell:
$(IMAGE_NAME) /bin/bash

test:
poetry run pytest tests/unit --reruns 1
$(DOCKER_RUN) poetry run pytest tests/unit --reruns 1

test-with-xdist:
poetry run pytest tests/unit --reruns 1 -n auto
$(DOCKER_RUN) poetry run pytest tests/unit --reruns 1 -n auto

lint:
$(DOCKER_RUN) poetry run ruff check .
Expand Down
15 changes: 9 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,19 +96,22 @@ No need for custom marker definitions. Perfect for smarter filtering, reporting,

![ScreenRecording2025-06-29at1 06 02AM-ezgif com-video-to-gif-converter](https://github.com/user-attachments/assets/af40622f-f548-44a5-982b-344c74a65e13)

#### 🔍 Universal Test Search + Smart Link Navigation
#### 🔍 Universal Test Search + Smart Traceability

Whether you're trying to trace coverage or track unlinked test cases — this search has your back!
Whether you're tracing coverage, investigating failures, or tracking unlinked test cases — this search has your back!

Just start typing, and the dashboard will instantly filter tests by:

✅ Test name
✅ Test names

✅ Linked issue/documentation IDs (like JIRA, Testmo, Notion, etc.)
✅ Linked issue or documentation IDs (JIRA, Testmo, Notion, etc.)

✅ Custom URLs or keywords present in the links
✅ Custom URLs or keywords present in linked references

✅ Error messages and trace snippets to quickly group related failures

<img width="800" height="421" alt="new_search" src="https://github.com/user-attachments/assets/54858747-ab16-4d4f-baa9-0d651a1d8bac" />

![ScreenRecording2025-06-21at3 10 06PM-ezgif com-video-to-gif-converter](https://github.com/user-attachments/assets/f81c9a81-f98d-4151-ad7a-c1184cd199eb)

#### 📸 Screenshot Support: View screenshots directly in the report to understand failures faster.

Expand Down
29 changes: 18 additions & 11 deletions docs/Features/search.rst
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
Universal Test Search + Smart Link Navigation
=============================================
# Universal Test Search + Smart Traceability

The `pytest-html-plus` report comes with a powerful search bar at the top of the page, helping you find the test cases you care about in real-time.
The `pytest-html-plus` report includes a powerful search bar that instantly filters tests as you type, helping you quickly locate failures, trace coverage, and navigate linked references.

Search and Navigate:
---------------------
## Search and Navigate

Just start typing, and the dashboard will instantly filter and display tests based on:
Simply start typing, and the dashboard will filter tests in real time based on:

✅ **Test name**
✅ **Test names**

✅ **Linked external IDs** — such as JIRA, Testmo, Notion, or any quoted references you attach (e.g. `JIRA-123`, `DOC-456`, etc.)
✅ **Linked external IDs** — such as JIRA, Testmo, Notion, or any quoted references you attach (for example `JIRA-123`, `DOC-456`, etc.)

✅ **Custom URLs or keywords** — any meaningful text or link in the associated references will be indexed.
✅ **Custom URLs or keywords** — any meaningful text or link in associated references will be indexed

✅ **Error messages and trace snippets** — quickly find tests sharing similar failures, assertion messages, or exception types

.. tip::
This smart search helps you **trace coverage**, **group tests by external references**, or **isolate unlinked cases** with ease.

For example, if you tag tests with a JIRA ID or Notion doc, typing that ID in the search bar will instantly filter all associated test cases for traceability.
This smart search helps you **trace coverage**, **group tests by external references**, and **identify related failures** with ease.

## Examples

* Search `JIRA-123` to display all tests linked to that issue.
* Search `TimeoutError` to locate tests failing due to timeouts.
* Search `NoSuchElementException` to identify Selenium or Playwright locator failures.
* Search `expected 200` to find API assertions with matching error messages.

This makes it easy to investigate patterns, correlate failures, and navigate large reports efficiently.
5 changes: 3 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "pytest-html-plus"
version = "1.0.1"
version = "1.1.0"
description = "Generate Actionable, automatic screenshots, unified Mobile friendly Pytest HTML report in less than 3 seconds — no hooks, merge plugins, no config, xdist-ready."
readme = "README.md"
authors = ["reporterplus"]
Expand Down Expand Up @@ -84,7 +84,8 @@ ignore = [
"W293", # blank line whitespace — let black handle
]
exclude = [
"pytest_html_plus/generate_html_report.py"
"pytest_html_plus/generate_html_report.py",
".git"
]

[tool.ruff.lint]
Expand Down
16 changes: 12 additions & 4 deletions pytest_html_plus/generate_html_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import os
import shutil
from datetime import datetime, timezone

from html import escape
from pytest_html_plus.compute_filter_counts import compute_filter_count
from pytest_html_plus.utils import extract_error_block, extract_trace_block

Expand Down Expand Up @@ -614,7 +614,8 @@ def generate_html_report(self):
document.querySelectorAll('.test-card').forEach(card => {{
const name = card.getAttribute('data-name') || '';
const link = card.getAttribute('data-link') || '';
const isVisible = name.toLowerCase().includes(filter) || link.toLowerCase().includes(filter);
const error = card.getAttribute('data-error') || '';
const isVisible = name.toLowerCase().includes(filter) || link.toLowerCase().includes(filter) || error.toLowerCase().includes(filter);
card.style.display = isVisible ? '' : 'none';
}});
}});
Expand Down Expand Up @@ -750,7 +751,7 @@ def generate_html_report(self):
<input
type="text"
id="universal-search"
placeholder="🔍 Search by anything, testname? link ids?..."
placeholder="🔍 Search by test names, link IDs, or error logs..."
style="width: 100%; padding: 10px; margin-bottom: 20px; font-size: 16px; border: 1px solid #ccc; border-radius: 5px;"
/>
</div>
Expand Down Expand Up @@ -873,6 +874,8 @@ def generate_html_report(self):
"""

attempts_html += "</div>"

search_error = ""

if test.get("error"):
full_error = test["error"]
Expand All @@ -891,6 +894,11 @@ def generate_html_report(self):
<div class="error-content"><strong>Error:</strong> {self.generate_copy_button(error_content, "error")}
<pre>{error_content}</pre></div>
"""
search_error = (
error_content.replace("\n", " ")
.replace("\r", " ")
)[:1000]


flaky_badge = ""
if test.get("flaky"):
Expand Down Expand Up @@ -928,7 +936,7 @@ def generate_html_report(self):
)

html += f"""
<div class="test test-card" data-name="{test["test"]}" data-link="{",".join(test.get("links") or [])}" data-markers="{marker_str}">
<div class="test test-card" data-name="{test["test"]}" data-link="{",".join(test.get("links") or [])}" data-markers="{marker_str}" data-error="{escape(search_error)}">
<div class="header {status_class}" onclick="toggleDetails(this)">
<div class="header-section test-info">
<span class="toggle"></span>
Expand Down
4 changes: 4 additions & 0 deletions tests/unit/test_error_block.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import pytest

from pytest_html_plus.utils import extract_error_block


Expand All @@ -7,12 +9,14 @@ def test_error_block_basic():
assert extract_error_block(error) == expected


@pytest.mark.jira("https://acme.atlassian.net/browse/QA-123")
def test_error_block_spaces_and_blank_lines():
error = "\n\n E TypeError\n\n \nE IndexError\n"
expected = "E TypeError\nE IndexError"
assert extract_error_block(error) == expected


@pytest.mark.link("https://acme.atlassian.net/browse/PAY-456")
def test_error_block_no_E_lines():
error = "line 1\nline 2"
expected = "line 1\nline 2"
Expand Down
Loading