Coverage for src/mkdocs_coverage/plugin.py: 84.85%
56 statements
« prev ^ index » next coverage.py v7.5.3, created at 2024-06-11 20:45 +0200
« prev ^ index » next coverage.py v7.5.3, created at 2024-06-11 20:45 +0200
1"""This module contains the `mkdocs_coverage` plugin."""
3from __future__ import annotations
5import re
6import shutil
7import textwrap
8import warnings
9from pathlib import Path
10from typing import TYPE_CHECKING, Any
12from mkdocs.config.base import Config
13from mkdocs.config.config_options import Optional
14from mkdocs.config.config_options import Type as MkType
15from mkdocs.plugins import BasePlugin
16from mkdocs.structure.files import File, Files
18from mkdocs_coverage.loggers import get_plugin_logger
20if TYPE_CHECKING:
21 from mkdocs.config.defaults import MkDocsConfig
23log = get_plugin_logger(__name__)
26class MkDocsCoverageConfig(Config):
27 """Configuration options for the plugin."""
29 page_name = Optional(MkType(str, default=None))
30 page_path = MkType(str, default="coverage")
31 html_report_dir = MkType(str, default="htmlcov")
34class MkDocsCoveragePlugin(BasePlugin[MkDocsCoverageConfig]):
35 """The MkDocs plugin to integrate the coverage HTML report in the site."""
37 def __init__(self) -> None:
38 """Initialize the plugin."""
39 super().__init__()
40 self.page_path: str = ""
42 def on_files(self, files: Files, config: MkDocsConfig, **kwargs: Any) -> Files: # noqa: ARG002
43 """Add the coverage page to the navigation.
45 Hook for the [`on_files` event](https://www.mkdocs.org/user-guide/plugins/#on_files).
46 This hook is used to add the coverage page to the navigation, using a temporary file.
48 Arguments:
49 files: The files collection.
50 config: The MkDocs config object.
51 **kwargs: Additional arguments passed by MkDocs.
53 Returns:
54 The modified files collection.
55 """
56 page_name = self.config.page_name
57 page_path: str
58 if page_name is not None: 58 ↛ 59line 58 didn't jump to line 59, because the condition on line 58 was never true
59 warnings.warn(
60 "The 'page_name' configuration option is deprecated and will be removed in a future release. "
61 "Use the 'page_path' configuration option instead.",
62 DeprecationWarning,
63 stacklevel=1,
64 )
65 page_path = page_name
66 else:
67 page_path = self.config.page_path
68 self.page_path = page_path
69 covindex = "covindex.html" if config.use_directory_urls else f"{page_path}/covindex.html"
71 style = textwrap.dedent(
72 """
73 <style>
74 article h1, article > a, .md-sidebar--secondary {
75 display: none !important;
76 }
77 </style>
78 """,
79 )
81 iframe = textwrap.dedent(
82 f"""
83 <iframe
84 id="coviframe"
85 src="{covindex}"
86 frameborder="0"
87 scrolling="no"
88 onload="resizeIframe();"
89 width="100%">
90 </iframe>
91 """,
92 )
94 script = textwrap.dedent(
95 """
96 <script>
97 var coviframe = document.getElementById("coviframe");
99 function resizeIframe() {
100 coviframe.style.height = coviframe.contentWindow.document.documentElement.offsetHeight + 'px';
101 }
103 coviframe.contentWindow.document.body.onclick = function() {
104 coviframe.contentWindow.location.reload();
105 }
106 </script>
108 """,
109 )
110 page_contents = style + iframe + script
111 files.append(
112 File.generated(
113 config=config,
114 src_uri=page_path + ".md",
115 content=page_contents,
116 ),
117 )
118 return files
120 def on_post_build(self, config: MkDocsConfig, **kwargs: Any) -> None: # noqa: ARG002
121 """Copy the coverage HTML report into the site directory.
123 Hook for the [`on_post_build` event](https://www.mkdocs.org/user-guide/plugins/#on_post_build).
125 Rename `index.html` into `covindex.html`.
126 Replace every occurrence of `index.html` by `covindex.html` in the HTML files.
128 Arguments:
129 config: The MkDocs config object.
130 **kwargs: Additional arguments passed by MkDocs.
131 """
132 site_dir = Path(config.site_dir)
133 coverage_dir = site_dir / self.page_path
134 tmp_index = site_dir / ".coverage-tmp.html"
136 if config.use_directory_urls: 136 ↛ 139line 136 didn't jump to line 139, because the condition on line 136 was always true
137 shutil.move(str(coverage_dir / "index.html"), tmp_index)
138 else:
139 shutil.move(str(coverage_dir.with_suffix(".html")), tmp_index)
141 shutil.rmtree(str(coverage_dir), ignore_errors=True)
142 try:
143 shutil.copytree(self.config.html_report_dir, str(coverage_dir))
144 except FileNotFoundError:
145 log.warning(f"No such HTML report directory: {self.config.html_report_dir}")
146 return
148 shutil.move(str(coverage_dir / "index.html"), coverage_dir / "covindex.html")
150 if config.use_directory_urls: 150 ↛ 153line 150 didn't jump to line 153, because the condition on line 150 was always true
151 shutil.move(str(tmp_index), coverage_dir / "index.html")
152 else:
153 shutil.move(str(tmp_index), coverage_dir.with_suffix(".html"))
155 for html_file in coverage_dir.iterdir():
156 if html_file.suffix == ".html" and html_file.name != "index.html":
157 html_file.write_text(re.sub(r'href="index\.html"', 'href="covindex.html"', html_file.read_text()))