pytest-html-plus 1.2.0__tar.gz → 1.2.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pytest-html-plus
3
- Version: 1.2.0
3
+ Version: 1.2.1
4
4
  Summary: Generate Actionable, automatic screenshots, unified Mobile friendly Pytest HTML report in less than 3 seconds — no hooks, merge plugins, no config, xdist-ready.
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "pytest-html-plus"
3
- version = "1.2.0"
3
+ version = "1.2.1"
4
4
  description = "Generate Actionable, automatic screenshots, unified Mobile friendly Pytest HTML report in less than 3 seconds — no hooks, merge plugins, no config, xdist-ready."
5
5
  readme = "README.md"
6
6
  authors = ["reporterplus"]
@@ -78,13 +78,7 @@ html-output = "report_output_warnings"
78
78
  [tool.ruff]
79
79
  line-length = 88
80
80
  target-version = "py39"
81
- ignore = [
82
- "E501", # line too long — let black handle
83
- "W291", # trailing whitespace — let black handle
84
- "W293", # blank line whitespace — let black handle
85
- ]
86
81
  exclude = [
87
- "pytest_html_plus/generate_html_report.py",
88
82
  ".git"
89
83
  ]
90
84
 
@@ -96,4 +90,8 @@ select = [
96
90
  "I", # isort
97
91
  "UP", # pyupgrade
98
92
  ]
99
- ignore = []
93
+ ignore = [
94
+ "E501", # line too long — let black handle
95
+ "W291", # trailing whitespace — let black handle
96
+ "W293", # blank line whitespace — let black handle
97
+ ]
@@ -5,7 +5,9 @@ import os
5
5
  import shutil
6
6
  from datetime import datetime, timezone
7
7
  from html import escape
8
+
8
9
  from pytest_html_plus.compute_filter_counts import compute_filter_count
10
+ from pytest_html_plus.resolver_driver import sanitize_filename
9
11
  from pytest_html_plus.utils import extract_error_block, extract_trace_block
10
12
 
11
13
 
@@ -792,7 +794,14 @@ class JSONReporter:
792
794
  else "error" if test["status"] == "error" else "skipped"
793
795
  )
794
796
  )
795
- screenshot_path = self.find_screenshot_and_copy(test["test"])
797
+ # NOTE: Parammetrized test name may contains unsupport filesystem-safe chars
798
+ # Use test_name will not match the screenshot.
799
+ # 1. use test["screenshot"] which is the the screenshot path will lost the wild pattern
800
+ # *2. use sanitize_filename which replace non-alphanumeric will match the screenshot filename pattern
801
+ # 3. try to split parametrized will be hard
802
+ screenshot_path = self.find_screenshot_and_copy(
803
+ sanitize_filename(test["test"])
804
+ )
796
805
  screenshot_html = (
797
806
  f'<div class="details-screenshot"><img src="{screenshot_path}" alt="Screenshot" onclick="toggleFullscreen(this)"></div>'
798
807
  if screenshot_path
@@ -847,7 +856,7 @@ class JSONReporter:
847
856
 
848
857
  error_block = (
849
858
  f'<div class="error-content"><strong>Error:</strong> '
850
- f'{self.generate_copy_button(error, "error")}'
859
+ f"{self.generate_copy_button(error, 'error')}"
851
860
  f"<pre>{error}</pre></div>"
852
861
  if error
853
862
  else ""
@@ -855,7 +864,7 @@ class JSONReporter:
855
864
 
856
865
  trace_block = (
857
866
  f'<div class="trace-content"><strong>Trace:</strong> '
858
- f'{self.generate_copy_button(trace, "trace")}'
867
+ f"{self.generate_copy_button(trace, 'trace')}"
859
868
  f"<pre>{trace}</pre></div>"
860
869
  if trace
861
870
  else ""
@@ -874,7 +883,7 @@ class JSONReporter:
874
883
  """
875
884
 
876
885
  attempts_html += "</div>"
877
-
886
+
878
887
  search_error = ""
879
888
 
880
889
  if test.get("error"):
@@ -895,10 +904,8 @@ class JSONReporter:
895
904
  <pre>{error_content}</pre></div>
896
905
  """
897
906
  search_error = (
898
- error_content.replace("\n", " ")
899
- .replace("\r", " ")
907
+ error_content.replace("\n", " ").replace("\r", " ")
900
908
  )[:1000]
901
-
902
909
 
903
910
  flaky_badge = ""
904
911
  if test.get("flaky"):
@@ -935,17 +942,18 @@ class JSONReporter:
935
942
  '<span style="display:inline-block; min-width:45px;"></span>'
936
943
  )
937
944
 
945
+ escaped_test_name = escape(test["test"])
938
946
  html += f"""
939
- <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)}">
947
+ <div class="test test-card" data-name="{escaped_test_name}" data-link="{",".join(test.get("links") or [])}" data-markers="{marker_str}" data-error="{escape(search_error)}">
940
948
  <div class="header {status_class}" onclick="toggleDetails(this)">
941
949
  <div class="header-section test-info">
942
950
  <span class="toggle"></span>
943
- <strong>{test["test"]}</strong>
951
+ <strong>{escaped_test_name}</strong>
944
952
  <span>— {test["status"].upper()}</span>
945
953
  </div>
946
954
  <div class="header-section meta">
947
955
  <span class="nodeid-badge" style="display: flex; align-items: center; gap: 6px;">
948
- <code style="font-size: 0.6em; color: #555;">{test["nodeid"]}</code>
956
+ <code style="font-size: 0.6em; color: #555;">{escape(test["nodeid"], quote=True)}</code>
949
957
  {self.generate_copy_button(test["nodeid"], "nodeid")}
950
958
  </span>
951
959
  <span class="worker-id" style="background: #ddd; border-radius: 3px; padding: 2px 5px; font-size: 0.85em; font-weight: bold;">{test["worker"]}</span>
@@ -990,20 +998,21 @@ class JSONReporter:
990
998
  "#e6f4ea" if failed_tests == 0 and error_tests == 0 else "#fdecea"
991
999
  };
992
1000
  border: 1px solid {
993
- "#2f7a33" if failed_tests == 0 and
994
- error_tests == 0
995
- else "#a83232"
1001
+ "#2f7a33" if failed_tests == 0 and error_tests == 0 else "#a83232"
996
1002
  };
997
1003
  border-radius: 5px; margin-bottom: 1rem;">
998
1004
  {
999
1005
  "<strong>Bingo!</strong> All your tests passed!"
1000
1006
  if failed_tests == 0 and error_tests == 0
1001
- else (f"Total tests: {total_tests}, "
1002
- f"Failures: {failed_tests}, "
1003
- f"Errors: {error_tests}.")
1007
+ else (
1008
+ f"Total tests: {total_tests}, "
1009
+ f"Failures: {failed_tests}, "
1010
+ f"Errors: {error_tests}."
1011
+ )
1004
1012
  }
1005
- The slowest test was <strong>{slowest_test_name}</strong> at {
1006
- slowest_test_duration:.2f}s.
1013
+ The slowest test was <strong>{
1014
+ escape(slowest_test_name, quote=True)
1015
+ }</strong> at {slowest_test_duration:.2f}s.
1007
1016
  </div>
1008
1017
  """
1009
1018
 
@@ -247,7 +247,6 @@ def pytest_runtest_makereport(item, call):
247
247
  screenshot_path = take_screenshot_generic(screenshot_path, item, driver)
248
248
 
249
249
  worker_id = os.getenv("PYTEST_XDIST_WORKER") or "main"
250
- test_name = "".join(c if c.isalnum() else "_" for c in item.name)
251
250
 
252
251
  status = report.outcome
253
252
  if report.when in ("setup", "teardown") and report.failed:
@@ -256,14 +255,13 @@ def pytest_runtest_makereport(item, call):
256
255
  output_policy = config.getoption(OUTPUT_OPTION)
257
256
  is_expected_failure = report.skipped and hasattr(report, "wasxfail")
258
257
  include_output = output_policy == "all" or (
259
- output_policy == "failed-only"
260
- and (report.failed or is_expected_failure)
258
+ output_policy == "failed-only" and (report.failed or is_expected_failure)
261
259
  )
262
260
  stdout = getattr(report, "capstdout", "") if include_output else ""
263
261
  stderr = getattr(report, "capstderr", "") if include_output else ""
264
262
 
265
263
  reporter.log_result(
266
- test_name=test_name,
264
+ test_name=item.name,
267
265
  nodeid=item.nodeid,
268
266
  status=status,
269
267
  duration=report.duration,