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

1"""This module contains the `mkdocs_coverage` plugin.""" 

2 

3from __future__ import annotations 

4 

5import re 

6import shutil 

7import textwrap 

8import warnings 

9from pathlib import Path 

10from typing import TYPE_CHECKING, Any 

11 

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 

17 

18from mkdocs_coverage.loggers import get_plugin_logger 

19 

20if TYPE_CHECKING: 

21 from mkdocs.config.defaults import MkDocsConfig 

22 

23log = get_plugin_logger(__name__) 

24 

25 

26class MkDocsCoverageConfig(Config): 

27 """Configuration options for the plugin.""" 

28 

29 page_name = Optional(MkType(str, default=None)) 

30 page_path = MkType(str, default="coverage") 

31 html_report_dir = MkType(str, default="htmlcov") 

32 

33 

34class MkDocsCoveragePlugin(BasePlugin[MkDocsCoverageConfig]): 

35 """The MkDocs plugin to integrate the coverage HTML report in the site.""" 

36 

37 def __init__(self) -> None: 

38 """Initialize the plugin.""" 

39 super().__init__() 

40 self.page_path: str = "" 

41 

42 def on_files(self, files: Files, config: MkDocsConfig, **kwargs: Any) -> Files: # noqa: ARG002 

43 """Add the coverage page to the navigation. 

44 

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. 

47 

48 Arguments: 

49 files: The files collection. 

50 config: The MkDocs config object. 

51 **kwargs: Additional arguments passed by MkDocs. 

52 

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" 

70 

71 style = textwrap.dedent( 

72 """ 

73 <style> 

74 article h1, article > a, .md-sidebar--secondary { 

75 display: none !important; 

76 } 

77 </style> 

78 """, 

79 ) 

80 

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 ) 

93 

94 script = textwrap.dedent( 

95 """ 

96 <script> 

97 var coviframe = document.getElementById("coviframe"); 

98 

99 function resizeIframe() { 

100 coviframe.style.height = coviframe.contentWindow.document.documentElement.offsetHeight + 'px'; 

101 } 

102 

103 coviframe.contentWindow.document.body.onclick = function() { 

104 coviframe.contentWindow.location.reload(); 

105 } 

106 </script> 

107 

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 

119 

120 def on_post_build(self, config: MkDocsConfig, **kwargs: Any) -> None: # noqa: ARG002 

121 """Copy the coverage HTML report into the site directory. 

122 

123 Hook for the [`on_post_build` event](https://www.mkdocs.org/user-guide/plugins/#on_post_build). 

124 

125 Rename `index.html` into `covindex.html`. 

126 Replace every occurrence of `index.html` by `covindex.html` in the HTML files. 

127 

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" 

135 

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) 

140 

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 

147 

148 shutil.move(str(coverage_dir / "index.html"), coverage_dir / "covindex.html") 

149 

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

154 

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