Skip to content

Commit 8ccc104

Browse files
committed
Add test and use parametrize
2 parents ee1c3ba + 756a2ef commit 8ccc104

9 files changed

Lines changed: 383 additions & 137 deletions

File tree

.pre-commit-config.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,7 @@ repos:
5252
files: ^(CHANGES.rst|development.rst|README.rst)$
5353
language: python
5454
additional_dependencies: [pygments, restructuredtext_lint]
55+
- repo: https://github.com/elidupuis/mirrors-sass-lint
56+
rev: '5cc45653263b423398e4af2561fae362903dd45d'
57+
hooks:
58+
- id: sass-lint

CHANGES.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ Release Notes
33

44
**3.1.0 (unreleased)**
55

6+
* Stop attaching test reruns to final test report entries (`#374 <https://github.com/pytest-dev/pytest-html/issues/374>`_)
7+
8+
* Thanks to `@VladimirPodolyan <https://github.com/VladimirPodolyan>`_ for reporting and `@gnikonorov <https://github.com/gnikonorov>`_ for the fix
9+
610
* Allow for report duration formatting (`#376 <https://github.com/pytest-dev/pytest-html/issues/376>`_)
711

812
* Thanks to `@brettnolan <https://github.com/brettnolan>`_ for reporting and `@gnikonorov <https://github.com/gnikonorov>`_ for the fix

development.rst

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,24 @@ Run the following to execute the tests:
7070
7171
$ npm test
7272
73+
SASS/SCSS/CSS
74+
-------------
75+
76+
You will need `npm <https://www.npmjs.com>`_ installed to compile the CSS,
77+
which is generated via `SASS/SCSS <https://sass-lang.com/>`_.
78+
79+
Once ``npm`` is installed, you can install all needed dependencies by running:
80+
81+
.. code-block:: bash
82+
83+
$ npm install
84+
85+
Run the following to generate the CSS:
86+
87+
.. code-block:: bash
88+
89+
$ npm run build:css
90+
7391
Releasing a new version
7492
-----------------------
7593

package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@
66
"grunt-contrib-qunit": "^4.0.0",
77
"phantomjs-prebuilt": "2.1.15"
88
},
9-
"devDependencies": {},
9+
"devDependencies": {
10+
"sass": "^1.29.0"
11+
},
1012
"scripts": {
11-
"test": "grunt test"
13+
"test": "grunt test",
14+
"build:css": "sass --no-source-map --no-error-css src/layout/css/style.scss src/pytest_html/resources/style.css"
1215
}
1316
}

src/.gitattributes

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2+
# see https://github.com/github/linguist#generated-code
3+
pytest_html/resources/style.css linguist-generated

src/layout/css/style.scss

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
body {
2+
font-family: Helvetica, Arial, sans-serif;
3+
font-size: 12px;
4+
/* do not increase min-width as some may use split screens */
5+
min-width: 800px;
6+
color: #999;
7+
}
8+
9+
h1 {
10+
font-size: 24px;
11+
color: black;
12+
}
13+
14+
h2 {
15+
font-size: 16px;
16+
color: black;
17+
}
18+
19+
p {
20+
color: black;
21+
}
22+
23+
a {
24+
color: #999;
25+
}
26+
27+
table {
28+
border-collapse: collapse;
29+
}
30+
31+
/******************************
32+
* SUMMARY INFORMATION
33+
******************************/
34+
35+
#environment td {
36+
padding: 5px;
37+
border: 1px solid #E6E6E6;
38+
}
39+
40+
#environment tr:nth-child(odd) {
41+
background-color: #f6f6f6;
42+
}
43+
44+
/******************************
45+
* TEST RESULT COLORS
46+
******************************/
47+
span.passed, .passed .col-result {
48+
color: green;
49+
}
50+
span.skipped, span.xfailed, span.rerun, .skipped .col-result, .xfailed .col-result, .rerun .col-result {
51+
color: orange;
52+
}
53+
span.error, span.failed, span.xpassed, .error .col-result, .failed .col-result, .xpassed .col-result {
54+
color: red;
55+
}
56+
57+
58+
/******************************
59+
* RESULTS TABLE
60+
*
61+
* 1. Table Layout
62+
* 2. Extra
63+
* 3. Sorting items
64+
*
65+
******************************/
66+
67+
/*------------------
68+
* 1. Table Layout
69+
*------------------*/
70+
71+
#results-table {
72+
border: 1px solid #e6e6e6;
73+
color: #999;
74+
font-size: 12px;
75+
width: 100%
76+
}
77+
78+
#results-table th, #results-table td {
79+
padding: 5px;
80+
border: 1px solid #E6E6E6;
81+
text-align: left
82+
}
83+
#results-table th {
84+
font-weight: bold
85+
}
86+
87+
/*------------------
88+
* 2. Extra
89+
*------------------*/
90+
91+
.log:only-child {
92+
height: inherit
93+
}
94+
.log {
95+
background-color: #e6e6e6;
96+
border: 1px solid #e6e6e6;
97+
color: black;
98+
display: block;
99+
font-family: "Courier New", Courier, monospace;
100+
height: 230px;
101+
overflow-y: scroll;
102+
padding: 5px;
103+
white-space: pre-wrap
104+
}
105+
div.image {
106+
border: 1px solid #e6e6e6;
107+
float: right;
108+
height: 240px;
109+
margin-left: 5px;
110+
overflow: hidden;
111+
width: 320px
112+
}
113+
div.image img {
114+
width: 320px
115+
}
116+
div.video {
117+
border: 1px solid #e6e6e6;
118+
float: right;
119+
height: 240px;
120+
margin-left: 5px;
121+
overflow: hidden;
122+
width: 320px
123+
}
124+
div.video video {
125+
overflow: hidden;
126+
width: 320px;
127+
height: 240px;
128+
}
129+
.collapsed {
130+
display: none;
131+
}
132+
.expander::after {
133+
content: " (show details)";
134+
color: #BBB;
135+
font-style: italic;
136+
cursor: pointer;
137+
}
138+
.collapser::after {
139+
content: " (hide details)";
140+
color: #BBB;
141+
font-style: italic;
142+
cursor: pointer;
143+
}
144+
145+
/*------------------
146+
* 3. Sorting items
147+
*------------------*/
148+
.sortable {
149+
cursor: pointer;
150+
}
151+
152+
.sort-icon {
153+
font-size: 0px;
154+
float: left;
155+
margin-right: 5px;
156+
margin-top: 5px;
157+
/*triangle*/
158+
width: 0;
159+
height: 0;
160+
border-left: 8px solid transparent;
161+
border-right: 8px solid transparent;
162+
}
163+
164+
.inactive .sort-icon {
165+
/*finish triangle*/
166+
border-top: 8px solid #E6E6E6;
167+
}
168+
169+
.asc.active .sort-icon {
170+
/*finish triangle*/
171+
border-bottom: 8px solid #999;
172+
}
173+
174+
.desc.active .sort-icon {
175+
/*finish triangle*/
176+
border-top: 8px solid #999;
177+
}

src/pytest_html/plugin.py

Lines changed: 51 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ def __init__(self, outcome, report, logfile, config):
166166
if getattr(report, "when", "call") != "call":
167167
self.test_id = "::".join([report.nodeid, report.when])
168168
self.time = getattr(report, "duration", 0.0)
169-
self.formatted_time = getattr(report, "formatted_duration", 0.0)
169+
self.formatted_time = self._format_time(report)
170170
self.outcome = outcome
171171
self.additional_html = []
172172
self.links_html = []
@@ -292,6 +292,37 @@ def append_extra_html(self, extra, extra_index, test_index):
292292
)
293293
self.links_html.append(" ")
294294

295+
def _format_time(self, report):
296+
# parse the report duration into its display version and return
297+
# it to the caller
298+
duration = getattr(report, "duration", None)
299+
if duration is None:
300+
return ""
301+
302+
duration_formatter = getattr(report, "duration_formatter", None)
303+
string_duration = str(duration)
304+
if duration_formatter is None:
305+
if "." in string_duration:
306+
split_duration = string_duration.split(".")
307+
split_duration[1] = split_duration[1][0:2]
308+
309+
string_duration = ".".join(split_duration)
310+
311+
return string_duration
312+
else:
313+
# support %f, since time.strftime doesn't support it out of the box
314+
# keep a precision of 2 for legacy reasons
315+
formatted_milliseconds = "00"
316+
if "." in string_duration:
317+
milliseconds = string_duration.split(".")[1]
318+
formatted_milliseconds = milliseconds[0:2]
319+
320+
duration_formatter = duration_formatter.replace(
321+
"%f", formatted_milliseconds
322+
)
323+
duration_as_gmtime = time.gmtime(report.duration)
324+
return time.strftime(duration_formatter, duration_as_gmtime)
325+
295326
def _populate_html_log_div(self, log, report):
296327
if report.longrepr:
297328
# longreprtext is only filled out on failure by pytest
@@ -434,6 +465,10 @@ def append_failed(self, report):
434465
self.errors += 1
435466
self._appendrow("Error", report)
436467

468+
def append_rerun(self, report):
469+
self.rerun += 1
470+
self._appendrow("Rerun", report)
471+
437472
def append_skipped(self, report):
438473
if hasattr(report, "wasxfail"):
439474
self.xfailed += 1
@@ -442,11 +477,6 @@ def append_skipped(self, report):
442477
self.skipped += 1
443478
self._appendrow("Skipped", report)
444479

445-
def append_other(self, report):
446-
# For now, the only "other" the plugin give support is rerun
447-
self.rerun += 1
448-
self._appendrow("Rerun", report)
449-
450480
def _generate_report(self, session):
451481
suite_stop_time = time.time()
452482
suite_time_delta = suite_stop_time - self.suite_start_time
@@ -613,32 +643,6 @@ def generate_summary_item(self):
613643
unicode_doc = unicode_doc.encode("utf-8", errors="xmlcharrefreplace")
614644
return unicode_doc.decode("utf-8")
615645

616-
def _format_duration(self, report):
617-
# parse the report duration into its display version and return it to the caller
618-
duration_formatter = getattr(report, "duration_formatter", None)
619-
string_duration = str(report.duration)
620-
if duration_formatter is None:
621-
if "." in string_duration:
622-
split_duration = string_duration.split(".")
623-
split_duration[1] = split_duration[1][0:2]
624-
625-
string_duration = ".".join(split_duration)
626-
627-
return string_duration
628-
else:
629-
# support %f, since time.strftime doesn't support it out of the box
630-
# keep a precision of 2 for legacy reasons
631-
formatted_milliseconds = "00"
632-
if "." in string_duration:
633-
milliseconds = string_duration.split(".")[1]
634-
formatted_milliseconds = milliseconds[0:2]
635-
636-
duration_formatter = duration_formatter.replace(
637-
"%f", formatted_milliseconds
638-
)
639-
duration_as_gmtime = time.gmtime(report.duration)
640-
return time.strftime(duration_formatter, duration_as_gmtime)
641-
642646
def _generate_environment(self, config):
643647
if not hasattr(config, "_metadata") or config._metadata is None:
644648
return []
@@ -694,22 +698,23 @@ def _post_process_reports(self):
694698
# through them all to figure out the outcome, xfail, duration,
695699
# extras, and when it swapped from pass
696700
for test_report in test_reports:
697-
full_text += test_report.longreprtext
698-
extras.extend(getattr(test_report, "extra", []))
699-
duration += getattr(test_report, "duration", 0.0)
701+
if test_report.outcome == "rerun":
702+
# reruns are separate test runs for all intensive purposes
703+
self.append_rerun(test_report)
704+
else:
705+
full_text += test_report.longreprtext
706+
extras.extend(getattr(test_report, "extra", []))
707+
duration += getattr(test_report, "duration", 0.0)
700708

701-
if (
702-
test_report.outcome not in ("passed", "rerun")
703-
and outcome == "passed"
704-
):
705-
outcome = test_report.outcome
706-
failure_when = test_report.when
709+
if (
710+
test_report.outcome not in ("passed", "rerun")
711+
and outcome == "passed"
712+
):
713+
outcome = test_report.outcome
714+
failure_when = test_report.when
707715

708-
if hasattr(test_report, "wasxfail"):
709-
wasxfail = True
710-
711-
if test_report.outcome == "rerun":
712-
self.append_other(test_report)
716+
if hasattr(test_report, "wasxfail"):
717+
wasxfail = True
713718

714719
# the following test_report.<X> = settings come at the end of us
715720
# looping through all test_reports that make up a single
@@ -724,7 +729,6 @@ def _post_process_reports(self):
724729
test_report.longrepr = full_text
725730
test_report.extra = extras
726731
test_report.duration = duration
727-
test_report.formatted_duration = self._format_duration(test_report)
728732

729733
if wasxfail:
730734
test_report.wasxfail = True
@@ -737,9 +741,6 @@ def _post_process_reports(self):
737741
test_report.when = failure_when
738742
self.append_failed(test_report)
739743

740-
# we don't append other here since the only case supported
741-
# for append_other is rerun, which is handled in the loop above
742-
743744
def pytest_runtest_logreport(self, report):
744745
self.reports[report.nodeid].append(report)
745746

0 commit comments

Comments
 (0)