Enable variable expansion for CSS addons. (#676)

This commit is contained in:
Jim Brännlund 2023-05-13 21:04:08 +02:00 committed by GitHub
parent 7992d4980e
commit 316246e72d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 152 additions and 55 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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")

View File

@ -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):

View File

@ -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

View File

@ -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")