config_tests.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. #!/usr/bin/env python
  2. import os
  3. import tempfile
  4. import unittest
  5. from tempfile import TemporaryDirectory
  6. import mkdocs
  7. from mkdocs import config
  8. from mkdocs.config import config_options
  9. from mkdocs.exceptions import ConfigurationError
  10. from mkdocs.tests.base import dedent
  11. class ConfigTests(unittest.TestCase):
  12. def test_missing_config_file(self):
  13. def load_missing_config():
  14. config.load_config(config_file='bad_filename.yaml')
  15. self.assertRaises(ConfigurationError, load_missing_config)
  16. def test_missing_site_name(self):
  17. c = config.Config(schema=config.DEFAULT_SCHEMA)
  18. c.load_dict({})
  19. errors, warnings = c.validate()
  20. self.assertEqual(len(errors), 1)
  21. self.assertEqual(errors[0][0], 'site_name')
  22. self.assertEqual(str(errors[0][1]), 'Required configuration not provided.')
  23. self.assertEqual(len(warnings), 0)
  24. def test_empty_config(self):
  25. def load_empty_config():
  26. config.load_config(config_file='/dev/null')
  27. self.assertRaises(ConfigurationError, load_empty_config)
  28. def test_nonexistant_config(self):
  29. def load_empty_config():
  30. config.load_config(config_file='/path/that/is/not/real')
  31. self.assertRaises(ConfigurationError, load_empty_config)
  32. def test_invalid_config(self):
  33. file_contents = dedent("""
  34. - ['index.md', 'Introduction']
  35. - ['index.md', 'Introduction']
  36. - ['index.md', 'Introduction']
  37. """)
  38. config_file = tempfile.NamedTemporaryFile('w', delete=False)
  39. try:
  40. config_file.write(file_contents)
  41. config_file.flush()
  42. config_file.close()
  43. self.assertRaises(
  44. ConfigurationError,
  45. config.load_config, config_file=open(config_file.name, 'rb')
  46. )
  47. finally:
  48. os.remove(config_file.name)
  49. def test_config_option(self):
  50. """
  51. Users can explicitly set the config file using the '--config' option.
  52. Allows users to specify a config other than the default `mkdocs.yml`.
  53. """
  54. expected_result = {
  55. 'site_name': 'Example',
  56. 'pages': [
  57. {'Introduction': 'index.md'}
  58. ],
  59. }
  60. file_contents = dedent("""
  61. site_name: Example
  62. pages:
  63. - 'Introduction': 'index.md'
  64. """)
  65. with TemporaryDirectory() as temp_path:
  66. os.mkdir(os.path.join(temp_path, 'docs'))
  67. config_path = os.path.join(temp_path, 'mkdocs.yml')
  68. config_file = open(config_path, 'w')
  69. config_file.write(file_contents)
  70. config_file.flush()
  71. config_file.close()
  72. result = config.load_config(config_file=config_file.name)
  73. self.assertEqual(result['site_name'], expected_result['site_name'])
  74. self.assertEqual(result['pages'], expected_result['pages'])
  75. def test_theme(self):
  76. with TemporaryDirectory() as mytheme, TemporaryDirectory() as custom:
  77. configs = [
  78. dict(), # default theme
  79. {"theme": "readthedocs"}, # builtin theme
  80. {"theme": {'name': 'readthedocs'}}, # builtin as complex
  81. {"theme": {'name': None, 'custom_dir': mytheme}}, # custom only as complex
  82. {"theme": {'name': 'readthedocs', 'custom_dir': custom}}, # builtin and custom as complex
  83. { # user defined variables
  84. 'theme': {
  85. 'name': 'mkdocs',
  86. 'static_templates': ['foo.html'],
  87. 'show_sidebar': False,
  88. 'some_var': 'bar'
  89. }
  90. }
  91. ]
  92. mkdocs_dir = os.path.abspath(os.path.dirname(mkdocs.__file__))
  93. mkdocs_templates_dir = os.path.join(mkdocs_dir, 'templates')
  94. theme_dir = os.path.abspath(os.path.join(mkdocs_dir, 'themes'))
  95. results = (
  96. {
  97. 'dirs': [os.path.join(theme_dir, 'mkdocs'), mkdocs_templates_dir],
  98. 'static_templates': ['404.html', 'sitemap.xml'],
  99. 'vars': {
  100. 'include_search_page': False,
  101. 'search_index_only': False,
  102. 'highlightjs': True,
  103. 'hljs_style': 'github',
  104. 'hljs_languages': [],
  105. 'navigation_depth': 2,
  106. 'nav_style': 'primary',
  107. 'shortcuts': {'help': 191, 'next': 78, 'previous': 80, 'search': 83}
  108. }
  109. }, {
  110. 'dirs': [os.path.join(theme_dir, 'readthedocs'), mkdocs_templates_dir],
  111. 'static_templates': ['404.html', 'sitemap.xml'],
  112. 'vars': {
  113. 'include_search_page': True,
  114. 'search_index_only': False,
  115. 'highlightjs': True,
  116. 'hljs_languages': [],
  117. 'include_homepage_in_sidebar': True,
  118. 'prev_next_buttons_location': 'bottom',
  119. 'navigation_depth': 4,
  120. 'sticky_navigation': True,
  121. 'titles_only': False,
  122. 'collapse_navigation': True
  123. }
  124. }, {
  125. 'dirs': [os.path.join(theme_dir, 'readthedocs'), mkdocs_templates_dir],
  126. 'static_templates': ['404.html', 'sitemap.xml'],
  127. 'vars': {
  128. 'include_search_page': True,
  129. 'search_index_only': False,
  130. 'highlightjs': True,
  131. 'hljs_languages': [],
  132. 'include_homepage_in_sidebar': True,
  133. 'prev_next_buttons_location': 'bottom',
  134. 'navigation_depth': 4,
  135. 'sticky_navigation': True,
  136. 'titles_only': False,
  137. 'collapse_navigation': True
  138. }
  139. }, {
  140. 'dirs': [mytheme, mkdocs_templates_dir],
  141. 'static_templates': ['sitemap.xml'],
  142. 'vars': {}
  143. }, {
  144. 'dirs': [custom, os.path.join(theme_dir, 'readthedocs'), mkdocs_templates_dir],
  145. 'static_templates': ['404.html', 'sitemap.xml'],
  146. 'vars': {
  147. 'include_search_page': True,
  148. 'search_index_only': False,
  149. 'highlightjs': True,
  150. 'hljs_languages': [],
  151. 'include_homepage_in_sidebar': True,
  152. 'prev_next_buttons_location': 'bottom',
  153. 'navigation_depth': 4,
  154. 'sticky_navigation': True,
  155. 'titles_only': False,
  156. 'collapse_navigation': True
  157. }
  158. }, {
  159. 'dirs': [os.path.join(theme_dir, 'mkdocs'), mkdocs_templates_dir],
  160. 'static_templates': ['404.html', 'sitemap.xml', 'foo.html'],
  161. 'vars': {
  162. 'show_sidebar': False,
  163. 'some_var': 'bar',
  164. 'include_search_page': False,
  165. 'search_index_only': False,
  166. 'highlightjs': True,
  167. 'hljs_style': 'github',
  168. 'hljs_languages': [],
  169. 'navigation_depth': 2,
  170. 'nav_style': 'primary',
  171. 'shortcuts': {'help': 191, 'next': 78, 'previous': 80, 'search': 83}
  172. }
  173. }
  174. )
  175. for config_contents, result in zip(configs, results):
  176. c = config.Config(schema=(('theme', config_options.Theme(default='mkdocs')),))
  177. c.load_dict(config_contents)
  178. errors, warnings = c.validate()
  179. self.assertEqual(len(errors), 0)
  180. self.assertEqual(c['theme'].dirs, result['dirs'])
  181. self.assertEqual(c['theme'].static_templates, set(result['static_templates']))
  182. self.assertEqual({k: c['theme'][k] for k in iter(c['theme'])}, result['vars'])
  183. def test_empty_nav(self):
  184. conf = config.Config(schema=config.DEFAULT_SCHEMA)
  185. conf.load_dict({
  186. 'site_name': 'Example',
  187. 'config_file_path': os.path.join(os.path.abspath('.'), 'mkdocs.yml')
  188. })
  189. conf.validate()
  190. self.assertEqual(conf['nav'], None)
  191. def test_copy_pages_to_nav(self):
  192. # TODO: remove this when pages config setting is fully deprecated.
  193. conf = config.Config(schema=config.DEFAULT_SCHEMA)
  194. conf.load_dict({
  195. 'site_name': 'Example',
  196. 'pages': ['index.md', 'about.md'],
  197. 'config_file_path': os.path.join(os.path.abspath('.'), 'mkdocs.yml')
  198. })
  199. conf.validate()
  200. self.assertEqual(conf['nav'], ['index.md', 'about.md'])
  201. def test_dont_overwrite_nav_with_pages(self):
  202. # TODO: remove this when pages config setting is fully deprecated.
  203. conf = config.Config(schema=config.DEFAULT_SCHEMA)
  204. conf.load_dict({
  205. 'site_name': 'Example',
  206. 'pages': ['index.md', 'about.md'],
  207. 'nav': ['foo.md', 'bar.md'],
  208. 'config_file_path': os.path.join(os.path.abspath('.'), 'mkdocs.yml')
  209. })
  210. conf.validate()
  211. self.assertEqual(conf['nav'], ['foo.md', 'bar.md'])
  212. def test_doc_dir_in_site_dir(self):
  213. j = os.path.join
  214. test_configs = (
  215. {'docs_dir': j('site', 'docs'), 'site_dir': 'site'},
  216. {'docs_dir': 'docs', 'site_dir': '.'},
  217. {'docs_dir': '.', 'site_dir': '.'},
  218. {'docs_dir': 'docs', 'site_dir': ''},
  219. {'docs_dir': '', 'site_dir': ''},
  220. {'docs_dir': 'docs', 'site_dir': 'docs'},
  221. )
  222. conf = {
  223. 'config_file_path': j(os.path.abspath('..'), 'mkdocs.yml')
  224. }
  225. for test_config in test_configs:
  226. patch = conf.copy()
  227. patch.update(test_config)
  228. # Same as the default schema, but don't verify the docs_dir exists.
  229. c = config.Config(schema=(
  230. ('docs_dir', config_options.Dir(default='docs')),
  231. ('site_dir', config_options.SiteDir(default='site')),
  232. ('config_file_path', config_options.Type(str))
  233. ))
  234. c.load_dict(patch)
  235. errors, warnings = c.validate()
  236. self.assertEqual(len(errors), 1)
  237. self.assertEqual(warnings, [])