File: render.py

package info (click to toggle)
junit2html 31.0.5-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 608 kB
  • sloc: xml: 3,208; python: 1,075; makefile: 6; sh: 5
file content (143 lines) | stat: -rw-r--r-- 4,290 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
"""
Render junit reports as HTML
"""
import os
import importlib.util
from typing import TYPE_CHECKING

if TYPE_CHECKING: # pragma: no cover
    from os import PathLike
    from typing import Optional, List, Dict

from jinja2 import Environment, PackageLoader, select_autoescape, FileSystemLoader, BaseLoader


class LoaderFactory:
    """Obtain jinja template loaders"""

    DEFAULT = "default"
    pkgname = "junit2htmlreport"

    THEME_DIR_ENV = "JUNIT2HTML_THEMES"
    THEME_FILES = ["base.html", "matrix.html", "report.html", "styles.css"]

    def __init__(self):
        self.baseloaders: "Dict[str, BaseLoader]" = {
            self.DEFAULT: PackageLoader(self.pkgname, os.path.join("templates", self.DEFAULT)),
        }

        spec = importlib.util.find_spec(self.pkgname)
        if spec and spec.origin:
            pkgdir = os.path.dirname(spec.origin)
            templates_dir = os.path.join(pkgdir, "templates")
            for name in os.listdir(templates_dir):
                if name not in self.baseloaders.keys():
                    self.baseloaders[name] = PackageLoader(self.pkgname, os.path.join("templates", name))

        extra_themes = os.getenv(self.THEME_DIR_ENV, None)
        if extra_themes and os.path.isdir(extra_themes):
            for root, folders, filenames in os.walk(os.path.abspath(extra_themes)):
                if filenames:
                    theme = True
                    for item in self.THEME_FILES:
                        if item not in filenames:
                            theme = False
                    if theme:
                        theme_name = os.path.basename(root)
                        self.baseloaders[theme_name] = FileSystemLoader(root)

        self.selected = self.DEFAULT

    def add_filesystem_loader(self, name: str, path: "PathLike"):
        self.baseloaders[name] = FileSystemLoader(path)

    def select_loader(self, name: str) -> None:
        if name in self.baseloaders.keys():
            self.selected = name

    def get_loader(self) -> BaseLoader:
        return self.baseloaders[self.selected]

    @property
    def styles(self) -> list:
        return sorted(list(self.baseloaders.keys()))


class JunitLoaderBase:
    """Contains a jinja template BaseLoader"""
    def __init__(self):
        self._loader_factory: "Optional[LoaderFactory]" = None

    @property
    def loader_factory(self) -> "LoaderFactory":
        if self._loader_factory is None:
            return LoaderFactory()
        return self._loader_factory

    @loader_factory.setter
    def loader_factory(self, value):
        self._loader_factory = value


class ReportContainer(JunitLoaderBase):
    """
    Hold one or more reports
    """
    reports: "Dict[str, Junit]"

    def __init__(self):
        super().__init__()
        self.reports = {}

    def add_report(self, filename: str) -> None:
        raise NotImplementedError()

    def failures(self):
        """
        Return all the failed test cases
        :return:
        """
        found: "List[Case]" = []
        for report in self.reports:
            for suite in self.reports[report].suites:
                found.extend(suite.failed())

        return found

    def skips(self):
        """
        Return all the skipped test cases
        :return:
        """
        found: "List[Case]" = []
        for report in self.reports:
            for suite in self.reports[report].suites:
                found.extend(suite.skipped())
        return found




class HTMLReport:

    def __init__(self, show_toc: bool=True):
        self.show_toc = show_toc
        self.title: str = ""
        self.report: "Optional[Junit]" = None

    def load(self, report: "Junit", title: str="JUnit2HTML Report"):
        self.report = report
        self.title = title

    def __iter__(self):
        if self.report is None:
            raise Exception("A report must be loaded through `load(...)` first.")

        return self.report.__iter__()

    def __str__(self) -> str:
        loader = self.report.loader_factory.get_loader()
        env = Environment(loader=loader, autoescape=select_autoescape(["html"]))
        template = env.get_template("report.html")
        return template.render(report=self, title=self.title, show_toc=self.show_toc)