config_options_tests.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791
  1. import os
  2. import sys
  3. import unittest
  4. from unittest.mock import patch
  5. import mkdocs
  6. from mkdocs.config import config_options
  7. from mkdocs.config.base import Config
  8. class OptionallyRequiredTest(unittest.TestCase):
  9. def test_empty(self):
  10. option = config_options.OptionallyRequired()
  11. value = option.validate(None)
  12. self.assertEqual(value, None)
  13. self.assertEqual(option.is_required(), False)
  14. def test_required(self):
  15. option = config_options.OptionallyRequired(required=True)
  16. self.assertRaises(config_options.ValidationError,
  17. option.validate, None)
  18. self.assertEqual(option.is_required(), True)
  19. def test_required_no_default(self):
  20. option = config_options.OptionallyRequired(required=True)
  21. value = option.validate(2)
  22. self.assertEqual(2, value)
  23. def test_default(self):
  24. option = config_options.OptionallyRequired(default=1)
  25. value = option.validate(None)
  26. self.assertEqual(1, value)
  27. def test_replace_default(self):
  28. option = config_options.OptionallyRequired(default=1)
  29. value = option.validate(2)
  30. self.assertEqual(2, value)
  31. class TypeTest(unittest.TestCase):
  32. def test_single_type(self):
  33. option = config_options.Type(str)
  34. value = option.validate("Testing")
  35. self.assertEqual(value, "Testing")
  36. def test_multiple_types(self):
  37. option = config_options.Type((list, tuple))
  38. value = option.validate([1, 2, 3])
  39. self.assertEqual(value, [1, 2, 3])
  40. value = option.validate((1, 2, 3))
  41. self.assertEqual(value, (1, 2, 3))
  42. self.assertRaises(config_options.ValidationError,
  43. option.validate, {'a': 1})
  44. def test_length(self):
  45. option = config_options.Type(str, length=7)
  46. value = option.validate("Testing")
  47. self.assertEqual(value, "Testing")
  48. self.assertRaises(config_options.ValidationError,
  49. option.validate, "Testing Long")
  50. class ChoiceTest(unittest.TestCase):
  51. def test_valid_choice(self):
  52. option = config_options.Choice(('python', 'node'))
  53. value = option.validate('python')
  54. self.assertEqual(value, 'python')
  55. def test_invalid_choice(self):
  56. option = config_options.Choice(('python', 'node'))
  57. self.assertRaises(
  58. config_options.ValidationError, option.validate, 'go')
  59. def test_invalid_choices(self):
  60. self.assertRaises(ValueError, config_options.Choice, '')
  61. self.assertRaises(ValueError, config_options.Choice, [])
  62. self.assertRaises(ValueError, config_options.Choice, 5)
  63. class IpAddressTest(unittest.TestCase):
  64. def test_valid_address(self):
  65. addr = '127.0.0.1:8000'
  66. option = config_options.IpAddress()
  67. value = option.validate(addr)
  68. self.assertEqual(str(value), addr)
  69. self.assertEqual(value.host, '127.0.0.1')
  70. self.assertEqual(value.port, 8000)
  71. def test_valid_IPv6_address(self):
  72. addr = '::1:8000'
  73. option = config_options.IpAddress()
  74. value = option.validate(addr)
  75. self.assertEqual(str(value), addr)
  76. self.assertEqual(value.host, '::1')
  77. self.assertEqual(value.port, 8000)
  78. def test_named_address(self):
  79. addr = 'localhost:8000'
  80. option = config_options.IpAddress()
  81. value = option.validate(addr)
  82. self.assertEqual(str(value), addr)
  83. self.assertEqual(value.host, 'localhost')
  84. self.assertEqual(value.port, 8000)
  85. def test_default_address(self):
  86. addr = '127.0.0.1:8000'
  87. option = config_options.IpAddress(default=addr)
  88. value = option.validate(None)
  89. self.assertEqual(str(value), addr)
  90. self.assertEqual(value.host, '127.0.0.1')
  91. self.assertEqual(value.port, 8000)
  92. def test_IP_normalization(self):
  93. addr = '127.000.000.001:8000'
  94. option = config_options.IpAddress(default=addr)
  95. value = option.validate(None)
  96. self.assertEqual(str(value), '127.0.0.1:8000')
  97. self.assertEqual(value.host, '127.0.0.1')
  98. self.assertEqual(value.port, 8000)
  99. def test_invalid_address_range(self):
  100. option = config_options.IpAddress()
  101. self.assertRaises(
  102. config_options.ValidationError,
  103. option.validate, '277.0.0.1:8000'
  104. )
  105. def test_invalid_address_format(self):
  106. option = config_options.IpAddress()
  107. self.assertRaises(
  108. config_options.ValidationError,
  109. option.validate, '127.0.0.18000'
  110. )
  111. def test_invalid_address_type(self):
  112. option = config_options.IpAddress()
  113. self.assertRaises(
  114. config_options.ValidationError,
  115. option.validate, 123
  116. )
  117. def test_invalid_address_port(self):
  118. option = config_options.IpAddress()
  119. self.assertRaises(
  120. config_options.ValidationError,
  121. option.validate, '127.0.0.1:foo'
  122. )
  123. def test_invalid_address_missing_port(self):
  124. option = config_options.IpAddress()
  125. self.assertRaises(
  126. config_options.ValidationError,
  127. option.validate, '127.0.0.1'
  128. )
  129. def test_unsupported_address(self):
  130. option = config_options.IpAddress()
  131. value = option.validate('0.0.0.0:8000')
  132. option.post_validation({'dev_addr': value}, 'dev_addr')
  133. self.assertEqual(len(option.warnings), 1)
  134. def test_unsupported_IPv6_address(self):
  135. option = config_options.IpAddress()
  136. value = option.validate(':::8000')
  137. option.post_validation({'dev_addr': value}, 'dev_addr')
  138. self.assertEqual(len(option.warnings), 1)
  139. def test_invalid_IPv6_address(self):
  140. # The server will error out with this so we treat it as invalid.
  141. option = config_options.IpAddress()
  142. self.assertRaises(
  143. config_options.ValidationError,
  144. option.validate, '[::1]:8000'
  145. )
  146. class URLTest(unittest.TestCase):
  147. def test_valid_url(self):
  148. url = "https://mkdocs.org"
  149. option = config_options.URL()
  150. value = option.validate(url)
  151. self.assertEqual(value, url)
  152. def test_invalid_url(self):
  153. option = config_options.URL()
  154. self.assertRaises(config_options.ValidationError,
  155. option.validate, "www.mkdocs.org")
  156. def test_invalid(self):
  157. option = config_options.URL()
  158. self.assertRaises(config_options.ValidationError,
  159. option.validate, 1)
  160. class RepoURLTest(unittest.TestCase):
  161. def test_repo_name_github(self):
  162. option = config_options.RepoURL()
  163. config = {'repo_url': "https://github.com/mkdocs/mkdocs"}
  164. option.post_validation(config, 'repo_url')
  165. self.assertEqual(config['repo_name'], "GitHub")
  166. def test_repo_name_bitbucket(self):
  167. option = config_options.RepoURL()
  168. config = {'repo_url': "https://bitbucket.org/gutworth/six/"}
  169. option.post_validation(config, 'repo_url')
  170. self.assertEqual(config['repo_name'], "Bitbucket")
  171. def test_repo_name_gitlab(self):
  172. option = config_options.RepoURL()
  173. config = {'repo_url': "https://gitlab.com/gitlab-org/gitlab-ce/"}
  174. option.post_validation(config, 'repo_url')
  175. self.assertEqual(config['repo_name'], "GitLab")
  176. def test_repo_name_custom(self):
  177. option = config_options.RepoURL()
  178. config = {'repo_url': "https://launchpad.net/python-tuskarclient"}
  179. option.post_validation(config, 'repo_url')
  180. self.assertEqual(config['repo_name'], "Launchpad")
  181. def test_edit_uri_github(self):
  182. option = config_options.RepoURL()
  183. config = {'repo_url': "https://github.com/mkdocs/mkdocs"}
  184. option.post_validation(config, 'repo_url')
  185. self.assertEqual(config['edit_uri'], 'edit/master/docs/')
  186. def test_edit_uri_bitbucket(self):
  187. option = config_options.RepoURL()
  188. config = {'repo_url': "https://bitbucket.org/gutworth/six/"}
  189. option.post_validation(config, 'repo_url')
  190. self.assertEqual(config['edit_uri'], 'src/default/docs/')
  191. def test_edit_uri_gitlab(self):
  192. option = config_options.RepoURL()
  193. config = {'repo_url': "https://gitlab.com/gitlab-org/gitlab-ce/"}
  194. option.post_validation(config, 'repo_url')
  195. self.assertEqual(config['edit_uri'], 'edit/master/docs/')
  196. def test_edit_uri_custom(self):
  197. option = config_options.RepoURL()
  198. config = {'repo_url': "https://launchpad.net/python-tuskarclient"}
  199. option.post_validation(config, 'repo_url')
  200. self.assertEqual(config.get('edit_uri'), '')
  201. def test_repo_name_custom_and_empty_edit_uri(self):
  202. option = config_options.RepoURL()
  203. config = {'repo_url': "https://github.com/mkdocs/mkdocs",
  204. 'repo_name': 'mkdocs'}
  205. option.post_validation(config, 'repo_url')
  206. self.assertEqual(config.get('edit_uri'), 'edit/master/docs/')
  207. class DirTest(unittest.TestCase):
  208. def test_valid_dir(self):
  209. d = os.path.dirname(__file__)
  210. option = config_options.Dir(exists=True)
  211. value = option.validate(d)
  212. self.assertEqual(d, value)
  213. def test_missing_dir(self):
  214. d = os.path.join("not", "a", "real", "path", "I", "hope")
  215. option = config_options.Dir()
  216. value = option.validate(d)
  217. self.assertEqual(os.path.abspath(d), value)
  218. def test_missing_dir_but_required(self):
  219. d = os.path.join("not", "a", "real", "path", "I", "hope")
  220. option = config_options.Dir(exists=True)
  221. self.assertRaises(config_options.ValidationError,
  222. option.validate, d)
  223. def test_file(self):
  224. d = __file__
  225. option = config_options.Dir(exists=True)
  226. self.assertRaises(config_options.ValidationError,
  227. option.validate, d)
  228. def test_incorrect_type_attribute_error(self):
  229. option = config_options.Dir()
  230. self.assertRaises(config_options.ValidationError,
  231. option.validate, 1)
  232. def test_incorrect_type_type_error(self):
  233. option = config_options.Dir()
  234. self.assertRaises(config_options.ValidationError,
  235. option.validate, [])
  236. def test_dir_unicode(self):
  237. cfg = Config(
  238. [('dir', config_options.Dir())],
  239. config_file_path=os.path.join(os.path.abspath('.'), 'mkdocs.yml'),
  240. )
  241. test_config = {
  242. 'dir': 'юникод'
  243. }
  244. cfg.load_dict(test_config)
  245. fails, warns = cfg.validate()
  246. self.assertEqual(len(fails), 0)
  247. self.assertEqual(len(warns), 0)
  248. self.assertIsInstance(cfg['dir'], str)
  249. def test_dir_filesystemencoding(self):
  250. cfg = Config(
  251. [('dir', config_options.Dir())],
  252. config_file_path=os.path.join(os.path.abspath('.'), 'mkdocs.yml'),
  253. )
  254. test_config = {
  255. 'dir': 'Übersicht'.encode(encoding=sys.getfilesystemencoding())
  256. }
  257. cfg.load_dict(test_config)
  258. fails, warns = cfg.validate()
  259. # str does not include byte strings so validation fails
  260. self.assertEqual(len(fails), 1)
  261. self.assertEqual(len(warns), 0)
  262. def test_dir_bad_encoding_fails(self):
  263. cfg = Config(
  264. [('dir', config_options.Dir())],
  265. config_file_path=os.path.join(os.path.abspath('.'), 'mkdocs.yml'),
  266. )
  267. test_config = {
  268. 'dir': 'юникод'.encode(encoding='ISO 8859-5')
  269. }
  270. cfg.load_dict(test_config)
  271. fails, warns = cfg.validate()
  272. self.assertEqual(len(fails), 1)
  273. self.assertEqual(len(warns), 0)
  274. def test_config_dir_prepended(self):
  275. base_path = os.path.abspath('.')
  276. cfg = Config(
  277. [('dir', config_options.Dir())],
  278. config_file_path=os.path.join(base_path, 'mkdocs.yml'),
  279. )
  280. test_config = {
  281. 'dir': 'foo'
  282. }
  283. cfg.load_dict(test_config)
  284. fails, warns = cfg.validate()
  285. self.assertEqual(len(fails), 0)
  286. self.assertEqual(len(warns), 0)
  287. self.assertIsInstance(cfg['dir'], str)
  288. self.assertEqual(cfg['dir'], os.path.join(base_path, 'foo'))
  289. def test_dir_is_config_dir_fails(self):
  290. cfg = Config(
  291. [('dir', config_options.Dir())],
  292. config_file_path=os.path.join(os.path.abspath('.'), 'mkdocs.yml'),
  293. )
  294. test_config = {
  295. 'dir': '.'
  296. }
  297. cfg.load_dict(test_config)
  298. fails, warns = cfg.validate()
  299. self.assertEqual(len(fails), 1)
  300. self.assertEqual(len(warns), 0)
  301. class SiteDirTest(unittest.TestCase):
  302. def validate_config(self, config):
  303. """ Given a config with values for site_dir and doc_dir, run site_dir post_validation. """
  304. site_dir = config_options.SiteDir()
  305. docs_dir = config_options.Dir()
  306. fname = os.path.join(os.path.abspath('..'), 'mkdocs.yml')
  307. config['docs_dir'] = docs_dir.validate(config['docs_dir'])
  308. config['site_dir'] = site_dir.validate(config['site_dir'])
  309. schema = [
  310. ('site_dir', site_dir),
  311. ('docs_dir', docs_dir),
  312. ]
  313. cfg = Config(schema, fname)
  314. cfg.load_dict(config)
  315. failed, warned = cfg.validate()
  316. if failed:
  317. raise config_options.ValidationError(failed)
  318. return True
  319. def test_doc_dir_in_site_dir(self):
  320. j = os.path.join
  321. # The parent dir is not the same on every system, so use the actual dir name
  322. parent_dir = mkdocs.__file__.split(os.sep)[-3]
  323. test_configs = (
  324. {'docs_dir': j('site', 'docs'), 'site_dir': 'site'},
  325. {'docs_dir': 'docs', 'site_dir': '.'},
  326. {'docs_dir': '.', 'site_dir': '.'},
  327. {'docs_dir': 'docs', 'site_dir': ''},
  328. {'docs_dir': '', 'site_dir': ''},
  329. {'docs_dir': j('..', parent_dir, 'docs'), 'site_dir': 'docs'},
  330. {'docs_dir': 'docs', 'site_dir': '/'}
  331. )
  332. for test_config in test_configs:
  333. self.assertRaises(config_options.ValidationError,
  334. self.validate_config, test_config)
  335. def test_site_dir_in_docs_dir(self):
  336. j = os.path.join
  337. test_configs = (
  338. {'docs_dir': 'docs', 'site_dir': j('docs', 'site')},
  339. {'docs_dir': '.', 'site_dir': 'site'},
  340. {'docs_dir': '', 'site_dir': 'site'},
  341. {'docs_dir': '/', 'site_dir': 'site'},
  342. )
  343. for test_config in test_configs:
  344. self.assertRaises(config_options.ValidationError,
  345. self.validate_config, test_config)
  346. def test_common_prefix(self):
  347. """ Legitimate settings with common prefixes should not fail validation. """
  348. test_configs = (
  349. {'docs_dir': 'docs', 'site_dir': 'docs-site'},
  350. {'docs_dir': 'site-docs', 'site_dir': 'site'},
  351. )
  352. for test_config in test_configs:
  353. assert self.validate_config(test_config)
  354. class ThemeTest(unittest.TestCase):
  355. def test_theme_as_string(self):
  356. option = config_options.Theme()
  357. value = option.validate("mkdocs")
  358. self.assertEqual({'name': 'mkdocs'}, value)
  359. def test_uninstalled_theme_as_string(self):
  360. option = config_options.Theme()
  361. self.assertRaises(config_options.ValidationError,
  362. option.validate, "mkdocs2")
  363. def test_theme_default(self):
  364. option = config_options.Theme(default='mkdocs')
  365. value = option.validate(None)
  366. self.assertEqual({'name': 'mkdocs'}, value)
  367. def test_theme_as_simple_config(self):
  368. config = {
  369. 'name': 'mkdocs'
  370. }
  371. option = config_options.Theme()
  372. value = option.validate(config)
  373. self.assertEqual(config, value)
  374. def test_theme_as_complex_config(self):
  375. config = {
  376. 'name': 'mkdocs',
  377. 'custom_dir': 'custom',
  378. 'static_templates': ['sitemap.html'],
  379. 'show_sidebar': False
  380. }
  381. option = config_options.Theme()
  382. value = option.validate(config)
  383. self.assertEqual(config, value)
  384. def test_theme_name_is_none(self):
  385. config = {
  386. 'name': None
  387. }
  388. option = config_options.Theme()
  389. value = option.validate(config)
  390. self.assertEqual(config, value)
  391. def test_theme_config_missing_name(self):
  392. config = {
  393. 'custom_dir': 'custom',
  394. }
  395. option = config_options.Theme()
  396. self.assertRaises(config_options.ValidationError,
  397. option.validate, config)
  398. def test_uninstalled_theme_as_config(self):
  399. config = {
  400. 'name': 'mkdocs2'
  401. }
  402. option = config_options.Theme()
  403. self.assertRaises(config_options.ValidationError,
  404. option.validate, config)
  405. def test_theme_invalid_type(self):
  406. config = ['mkdocs2']
  407. option = config_options.Theme()
  408. self.assertRaises(config_options.ValidationError,
  409. option.validate, config)
  410. class NavTest(unittest.TestCase):
  411. def test_old_format(self):
  412. option = config_options.Nav()
  413. self.assertRaises(
  414. config_options.ValidationError,
  415. option.validate,
  416. [['index.md', ], ]
  417. )
  418. def test_provided_dict(self):
  419. option = config_options.Nav()
  420. value = option.validate([
  421. 'index.md',
  422. {"Page": "page.md"}
  423. ])
  424. self.assertEqual(['index.md', {'Page': 'page.md'}], value)
  425. option.post_validation({'extra_stuff': []}, 'extra_stuff')
  426. def test_provided_empty(self):
  427. option = config_options.Nav()
  428. value = option.validate([])
  429. self.assertEqual(None, value)
  430. option.post_validation({'extra_stuff': []}, 'extra_stuff')
  431. def test_invalid_type(self):
  432. option = config_options.Nav()
  433. self.assertRaises(config_options.ValidationError,
  434. option.validate, {})
  435. def test_invalid_config(self):
  436. option = config_options.Nav()
  437. self.assertRaises(config_options.ValidationError,
  438. option.validate, [[], 1])
  439. class PrivateTest(unittest.TestCase):
  440. def test_defined(self):
  441. option = config_options.Private()
  442. self.assertRaises(config_options.ValidationError,
  443. option.validate, 'somevalue')
  444. class MarkdownExtensionsTest(unittest.TestCase):
  445. @patch('markdown.Markdown')
  446. def test_simple_list(self, mockMd):
  447. option = config_options.MarkdownExtensions()
  448. config = {
  449. 'markdown_extensions': ['foo', 'bar']
  450. }
  451. config['markdown_extensions'] = option.validate(config['markdown_extensions'])
  452. option.post_validation(config, 'markdown_extensions')
  453. self.assertEqual({
  454. 'markdown_extensions': ['foo', 'bar'],
  455. 'mdx_configs': {}
  456. }, config)
  457. @patch('markdown.Markdown')
  458. def test_list_dicts(self, mockMd):
  459. option = config_options.MarkdownExtensions()
  460. config = {
  461. 'markdown_extensions': [
  462. {'foo': {'foo_option': 'foo value'}},
  463. {'bar': {'bar_option': 'bar value'}},
  464. {'baz': None}
  465. ]
  466. }
  467. config['markdown_extensions'] = option.validate(config['markdown_extensions'])
  468. option.post_validation(config, 'markdown_extensions')
  469. self.assertEqual({
  470. 'markdown_extensions': ['foo', 'bar', 'baz'],
  471. 'mdx_configs': {
  472. 'foo': {'foo_option': 'foo value'},
  473. 'bar': {'bar_option': 'bar value'}
  474. }
  475. }, config)
  476. @patch('markdown.Markdown')
  477. def test_mixed_list(self, mockMd):
  478. option = config_options.MarkdownExtensions()
  479. config = {
  480. 'markdown_extensions': [
  481. 'foo',
  482. {'bar': {'bar_option': 'bar value'}}
  483. ]
  484. }
  485. config['markdown_extensions'] = option.validate(config['markdown_extensions'])
  486. option.post_validation(config, 'markdown_extensions')
  487. self.assertEqual({
  488. 'markdown_extensions': ['foo', 'bar'],
  489. 'mdx_configs': {
  490. 'bar': {'bar_option': 'bar value'}
  491. }
  492. }, config)
  493. @patch('markdown.Markdown')
  494. def test_builtins(self, mockMd):
  495. option = config_options.MarkdownExtensions(builtins=['meta', 'toc'])
  496. config = {
  497. 'markdown_extensions': ['foo', 'bar']
  498. }
  499. config['markdown_extensions'] = option.validate(config['markdown_extensions'])
  500. option.post_validation(config, 'markdown_extensions')
  501. self.assertEqual({
  502. 'markdown_extensions': ['meta', 'toc', 'foo', 'bar'],
  503. 'mdx_configs': {}
  504. }, config)
  505. def test_duplicates(self):
  506. option = config_options.MarkdownExtensions(builtins=['meta', 'toc'])
  507. config = {
  508. 'markdown_extensions': ['meta', 'toc']
  509. }
  510. config['markdown_extensions'] = option.validate(config['markdown_extensions'])
  511. option.post_validation(config, 'markdown_extensions')
  512. self.assertEqual({
  513. 'markdown_extensions': ['meta', 'toc'],
  514. 'mdx_configs': {}
  515. }, config)
  516. def test_builtins_config(self):
  517. option = config_options.MarkdownExtensions(builtins=['meta', 'toc'])
  518. config = {
  519. 'markdown_extensions': [
  520. {'toc': {'permalink': True}}
  521. ]
  522. }
  523. config['markdown_extensions'] = option.validate(config['markdown_extensions'])
  524. option.post_validation(config, 'markdown_extensions')
  525. self.assertEqual({
  526. 'markdown_extensions': ['meta', 'toc'],
  527. 'mdx_configs': {'toc': {'permalink': True}}
  528. }, config)
  529. @patch('markdown.Markdown')
  530. def test_configkey(self, mockMd):
  531. option = config_options.MarkdownExtensions(configkey='bar')
  532. config = {
  533. 'markdown_extensions': [
  534. {'foo': {'foo_option': 'foo value'}}
  535. ]
  536. }
  537. config['markdown_extensions'] = option.validate(config['markdown_extensions'])
  538. option.post_validation(config, 'markdown_extensions')
  539. self.assertEqual({
  540. 'markdown_extensions': ['foo'],
  541. 'bar': {
  542. 'foo': {'foo_option': 'foo value'}
  543. }
  544. }, config)
  545. def test_none(self):
  546. option = config_options.MarkdownExtensions(default=[])
  547. config = {
  548. 'markdown_extensions': None
  549. }
  550. config['markdown_extensions'] = option.validate(config['markdown_extensions'])
  551. option.post_validation(config, 'markdown_extensions')
  552. self.assertEqual({
  553. 'markdown_extensions': [],
  554. 'mdx_configs': {}
  555. }, config)
  556. @patch('markdown.Markdown')
  557. def test_not_list(self, mockMd):
  558. option = config_options.MarkdownExtensions()
  559. self.assertRaises(config_options.ValidationError,
  560. option.validate, 'not a list')
  561. @patch('markdown.Markdown')
  562. def test_invalid_config_option(self, mockMd):
  563. option = config_options.MarkdownExtensions()
  564. config = {
  565. 'markdown_extensions': [
  566. {'foo': 'not a dict'}
  567. ]
  568. }
  569. self.assertRaises(
  570. config_options.ValidationError,
  571. option.validate, config['markdown_extensions']
  572. )
  573. @patch('markdown.Markdown')
  574. def test_invalid_config_item(self, mockMd):
  575. option = config_options.MarkdownExtensions()
  576. config = {
  577. 'markdown_extensions': [
  578. ['not a dict']
  579. ]
  580. }
  581. self.assertRaises(
  582. config_options.ValidationError,
  583. option.validate, config['markdown_extensions']
  584. )
  585. @patch('markdown.Markdown')
  586. def test_invalid_dict_item(self, mockMd):
  587. option = config_options.MarkdownExtensions()
  588. config = {
  589. 'markdown_extensions': [
  590. {'key1': 'value', 'key2': 'too many keys'}
  591. ]
  592. }
  593. self.assertRaises(
  594. config_options.ValidationError,
  595. option.validate, config['markdown_extensions']
  596. )
  597. def test_unknown_extension(self):
  598. option = config_options.MarkdownExtensions()
  599. config = {
  600. 'markdown_extensions': ['unknown']
  601. }
  602. self.assertRaises(
  603. config_options.ValidationError,
  604. option.validate, config['markdown_extensions']
  605. )