Enable variable expansion for CSS addons. (#676)
This commit is contained in:
parent
7992d4980e
commit
316246e72d
|
@ -9,28 +9,21 @@ import warnings
|
|||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from jinja2 import Environment
|
||||
from jinja2 import FileSystemLoader
|
||||
from jinja2 import select_autoescape
|
||||
|
||||
from pytest_html import __version__
|
||||
from pytest_html import extras
|
||||
from pytest_html.table import Header
|
||||
from pytest_html.table import Row
|
||||
from pytest_html.util import _ansi_styles
|
||||
from pytest_html.util import cleanup_unserializable
|
||||
|
||||
|
||||
class BaseReport:
|
||||
def __init__(self, report_path, config, report_data, default_css="style.css"):
|
||||
def __init__(self, report_path, config, report_data, template, css):
|
||||
self._report_path = Path(os.path.expandvars(report_path)).expanduser()
|
||||
self._report_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
self._resources_path = Path(__file__).parent.joinpath("resources")
|
||||
self._config = config
|
||||
self._template = _read_template([self._resources_path])
|
||||
self._css = _process_css(
|
||||
Path(self._resources_path, default_css), self._config.getoption("css")
|
||||
)
|
||||
self._template = template
|
||||
self._css = css
|
||||
self._max_asset_filename_length = int(
|
||||
config.getini("max_asset_filename_length")
|
||||
)
|
||||
|
@ -224,32 +217,6 @@ class BaseReport:
|
|||
self._generate_report()
|
||||
|
||||
|
||||
def _process_css(default_css, extra_css):
|
||||
with open(default_css, encoding="utf-8") as f:
|
||||
css = f.read()
|
||||
|
||||
# Add user-provided CSS
|
||||
for path in extra_css:
|
||||
css += "\n/******************************"
|
||||
css += "\n * CUSTOM CSS"
|
||||
css += f"\n * {path}"
|
||||
css += "\n ******************************/\n\n"
|
||||
with open(path, encoding="utf-8") as f:
|
||||
css += f.read()
|
||||
|
||||
# ANSI support
|
||||
if _ansi_styles:
|
||||
ansi_css = [
|
||||
"\n/******************************",
|
||||
" * ANSI2HTML STYLES",
|
||||
" ******************************/\n",
|
||||
]
|
||||
ansi_css.extend([str(r) for r in _ansi_styles])
|
||||
css += "\n".join(ansi_css)
|
||||
|
||||
return css
|
||||
|
||||
|
||||
def _is_error(report):
|
||||
return report.when in ["setup", "teardown"] and report.outcome == "failed"
|
||||
|
||||
|
@ -282,13 +249,3 @@ def _process_outcome(report):
|
|||
return "XFailed"
|
||||
|
||||
return report.outcome.capitalize()
|
||||
|
||||
|
||||
def _read_template(search_paths, template_name="index.jinja2"):
|
||||
env = Environment(
|
||||
loader=FileSystemLoader(search_paths),
|
||||
autoescape=select_autoescape(
|
||||
enabled_extensions=("jinja2",),
|
||||
),
|
||||
)
|
||||
return env.get_template(template_name)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
import os
|
||||
import warnings
|
||||
from pathlib import Path
|
||||
|
||||
|
@ -11,6 +12,8 @@ from pytest_html.fixtures import extras_stash_key
|
|||
from pytest_html.report import Report
|
||||
from pytest_html.report_data import ReportData
|
||||
from pytest_html.selfcontained_report import SelfContainedReport
|
||||
from pytest_html.util import _process_css
|
||||
from pytest_html.util import _read_template
|
||||
|
||||
|
||||
def pytest_addhooks(pluginmanager):
|
||||
|
@ -68,10 +71,14 @@ def pytest_addoption(parser):
|
|||
def pytest_configure(config):
|
||||
html_path = config.getoption("htmlpath")
|
||||
if html_path:
|
||||
extra_css = [
|
||||
Path(os.path.expandvars(css)).expanduser()
|
||||
for css in config.getoption("css")
|
||||
]
|
||||
missing_css_files = []
|
||||
for css_path in config.getoption("css"):
|
||||
if not Path(css_path).exists():
|
||||
missing_css_files.append(css_path)
|
||||
for css_path in extra_css:
|
||||
if not css_path.exists():
|
||||
missing_css_files.append(str(css_path))
|
||||
|
||||
if missing_css_files:
|
||||
os_error = (
|
||||
|
@ -82,11 +89,17 @@ def pytest_configure(config):
|
|||
|
||||
if not hasattr(config, "workerinput"):
|
||||
# prevent opening html_path on worker nodes (xdist)
|
||||
resources_path = Path(__file__).parent.joinpath("resources")
|
||||
default_css = Path(resources_path, "style.css")
|
||||
template = _read_template([resources_path])
|
||||
processed_css = _process_css(default_css, extra_css)
|
||||
report_data = ReportData(config)
|
||||
if config.getoption("self_contained_html"):
|
||||
html = SelfContainedReport(html_path, config, report_data)
|
||||
html = SelfContainedReport(
|
||||
html_path, config, report_data, template, processed_css
|
||||
)
|
||||
else:
|
||||
html = Report(html_path, config, report_data)
|
||||
html = Report(html_path, config, report_data, template, processed_css)
|
||||
|
||||
config.pluginmanager.register(html)
|
||||
|
||||
|
|
|
@ -10,8 +10,8 @@ from pytest_html.basereport import BaseReport
|
|||
|
||||
|
||||
class Report(BaseReport):
|
||||
def __init__(self, report_path, config, report_data):
|
||||
super().__init__(report_path, config, report_data)
|
||||
def __init__(self, report_path, config, report_data, template, css):
|
||||
super().__init__(report_path, config, report_data, template, css)
|
||||
self._assets_path = Path(self._report_path.parent, "assets")
|
||||
self._assets_path.mkdir(parents=True, exist_ok=True)
|
||||
self._css_path = Path(self._assets_path, "style.css")
|
||||
|
|
|
@ -9,8 +9,8 @@ from pytest_html.basereport import BaseReport
|
|||
|
||||
|
||||
class SelfContainedReport(BaseReport):
|
||||
def __init__(self, report_path, config, report_data):
|
||||
super().__init__(report_path, config, report_data)
|
||||
def __init__(self, report_path, config, report_data, template, css):
|
||||
super().__init__(report_path, config, report_data, template, css)
|
||||
|
||||
@property
|
||||
def css(self):
|
||||
|
|
|
@ -6,6 +6,9 @@ from functools import partial
|
|||
from typing import Any
|
||||
from typing import Dict
|
||||
|
||||
from jinja2 import Environment
|
||||
from jinja2 import FileSystemLoader
|
||||
from jinja2 import select_autoescape
|
||||
|
||||
try:
|
||||
from ansi2html import Ansi2HTMLConverter, style
|
||||
|
@ -30,3 +33,39 @@ def cleanup_unserializable(d: Dict[str, Any]) -> Dict[str, Any]:
|
|||
v = str(v)
|
||||
result[k] = v
|
||||
return result
|
||||
|
||||
|
||||
def _read_template(search_paths, template_name="index.jinja2"):
|
||||
env = Environment(
|
||||
loader=FileSystemLoader(search_paths),
|
||||
autoescape=select_autoescape(
|
||||
enabled_extensions=("jinja2",),
|
||||
),
|
||||
)
|
||||
return env.get_template(template_name)
|
||||
|
||||
|
||||
def _process_css(default_css, extra_css):
|
||||
with open(default_css, encoding="utf-8") as f:
|
||||
css = f.read()
|
||||
|
||||
# Add user-provided CSS
|
||||
for path in extra_css:
|
||||
css += "\n/******************************"
|
||||
css += "\n * CUSTOM CSS"
|
||||
css += f"\n * {path}"
|
||||
css += "\n ******************************/\n\n"
|
||||
with open(path, encoding="utf-8") as f:
|
||||
css += f.read()
|
||||
|
||||
# ANSI support
|
||||
if _ansi_styles:
|
||||
ansi_css = [
|
||||
"\n/******************************",
|
||||
" * ANSI2HTML STYLES",
|
||||
" ******************************/\n",
|
||||
]
|
||||
ansi_css.extend([str(r) for r in _ansi_styles])
|
||||
css += "\n".join(ansi_css)
|
||||
|
||||
return css
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
import importlib.resources
|
||||
import os
|
||||
import sys
|
||||
|
||||
import pkg_resources
|
||||
import pytest
|
||||
from assertpy import assert_that
|
||||
|
||||
pytest_plugins = ("pytester",)
|
||||
|
||||
|
||||
|
@ -7,6 +15,22 @@ def run(pytester, path="report.html", cmd_flags=None):
|
|||
return pytester.runpytest("--html", path, *cmd_flags)
|
||||
|
||||
|
||||
def file_content():
|
||||
try:
|
||||
return (
|
||||
importlib.resources.files("pytest_html")
|
||||
.joinpath("assets", "style.css")
|
||||
.read_bytes()
|
||||
.decode("utf-8")
|
||||
.strip()
|
||||
)
|
||||
except AttributeError:
|
||||
# Needed for python < 3.9
|
||||
return pkg_resources.resource_string(
|
||||
"pytest_html", os.path.join("assets", "style.css")
|
||||
).decode("utf-8")
|
||||
|
||||
|
||||
def test_duration_format_deprecation_warning(pytester):
|
||||
pytester.makeconftest(
|
||||
"""
|
||||
|
@ -44,3 +68,67 @@ def test_html_results_summary_hook(pytester):
|
|||
pytester.makepyfile("def test_pass(): pass")
|
||||
result = run(pytester)
|
||||
result.assert_outcomes(passed=1)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def css_file_path(pytester):
|
||||
css_one = """
|
||||
h1 {
|
||||
color: red;
|
||||
}
|
||||
"""
|
||||
css_two = """
|
||||
h2 {
|
||||
color: blue;
|
||||
}
|
||||
"""
|
||||
css_dir = pytester.path / "extra_css"
|
||||
css_dir.mkdir()
|
||||
file_path = css_dir / "one.css"
|
||||
with open(file_path, "w") as f:
|
||||
f.write(css_one)
|
||||
|
||||
pytester.makefile(".css", two=css_two)
|
||||
pytester.makepyfile("def test_pass(): pass")
|
||||
|
||||
return file_path
|
||||
|
||||
|
||||
@pytest.fixture(params=[True, False])
|
||||
def expandvar(request, css_file_path, monkeypatch):
|
||||
if request.param:
|
||||
monkeypatch.setenv("EXTRA_CSS", str(css_file_path))
|
||||
return "%EXTRA_CSS%" if sys.platform == "win32" else "${EXTRA_CSS}"
|
||||
return css_file_path
|
||||
|
||||
|
||||
def test_custom_css(pytester, css_file_path, expandvar):
|
||||
result = run(
|
||||
pytester, "report.html", cmd_flags=["--css", expandvar, "--css", "two.css"]
|
||||
)
|
||||
result.assert_outcomes(passed=1)
|
||||
|
||||
path = pytester.path.joinpath("assets", "style.css")
|
||||
|
||||
with open(str(path)) as f:
|
||||
css = f.read()
|
||||
assert_that(css).contains("* " + str(css_file_path)).contains("* two.css")
|
||||
|
||||
|
||||
def test_custom_css_selfcontained(pytester, css_file_path, expandvar):
|
||||
result = run(
|
||||
pytester,
|
||||
"report.html",
|
||||
cmd_flags=[
|
||||
"--css",
|
||||
expandvar,
|
||||
"--css",
|
||||
"two.css",
|
||||
"--self-contained-html",
|
||||
],
|
||||
)
|
||||
result.assert_outcomes(passed=1)
|
||||
|
||||
with open(pytester.path / "report.html") as f:
|
||||
html = f.read()
|
||||
assert_that(html).contains("* " + str(css_file_path)).contains("* two.css")
|
||||
|
|
Loading…
Reference in New Issue