added podman, json and yaml

This commit is contained in:
2022-11-27 19:11:46 +01:00
parent 01135dea09
commit 5226e858bb
790 changed files with 114578 additions and 16 deletions

View File

@ -0,0 +1,754 @@
import os
import sys
import unittest
from unittest import mock
from mkdocs.structure.files import File, Files, _filter_paths, _sort_files, get_files
from mkdocs.tests.base import PathAssertionMixin, load_config, tempdir
class TestFiles(PathAssertionMixin, unittest.TestCase):
def test_file_eq(self):
file = File('a.md', '/path/to/docs', '/path/to/site', use_directory_urls=False)
self.assertTrue(
file == File('a.md', '/path/to/docs', '/path/to/site', use_directory_urls=False)
)
def test_file_ne(self):
file = File('a.md', '/path/to/docs', '/path/to/site', use_directory_urls=False)
# Different filename
self.assertTrue(
file != File('b.md', '/path/to/docs', '/path/to/site', use_directory_urls=False)
)
# Different src_path
self.assertTrue(
file != File('a.md', '/path/to/other', '/path/to/site', use_directory_urls=False)
)
# Different URL
self.assertTrue(
file != File('a.md', '/path/to/docs', '/path/to/site', use_directory_urls=True)
)
@unittest.skipUnless(sys.platform.startswith("win"), "requires Windows")
def test_src_path_windows(self):
f = File('foo\\a.md', '/path/to/docs', '/path/to/site', use_directory_urls=False)
self.assertEqual(f.src_uri, 'foo/a.md')
self.assertEqual(f.src_path, 'foo\\a.md')
f.src_uri = 'foo/b.md'
self.assertEqual(f.src_uri, 'foo/b.md')
self.assertEqual(f.src_path, 'foo\\b.md')
f.src_path = 'foo/c.md'
self.assertEqual(f.src_uri, 'foo/c.md')
self.assertEqual(f.src_path, 'foo\\c.md')
f.src_path = 'foo\\d.md'
self.assertEqual(f.src_uri, 'foo/d.md')
self.assertEqual(f.src_path, 'foo\\d.md')
f.src_uri = 'foo\\e.md'
self.assertEqual(f.src_uri, 'foo\\e.md')
self.assertEqual(f.src_path, 'foo\\e.md')
def test_sort_files(self):
self.assertEqual(
_sort_files(['b.md', 'bb.md', 'a.md', 'index.md', 'aa.md']),
['index.md', 'a.md', 'aa.md', 'b.md', 'bb.md'],
)
self.assertEqual(
_sort_files(['b.md', 'index.html', 'a.md', 'index.md']),
['index.html', 'index.md', 'a.md', 'b.md'],
)
self.assertEqual(
_sort_files(['a.md', 'index.md', 'b.md', 'index.html']),
['index.md', 'index.html', 'a.md', 'b.md'],
)
self.assertEqual(
_sort_files(['.md', '_.md', 'a.md', 'index.md', '1.md']),
['index.md', '.md', '1.md', '_.md', 'a.md'],
)
self.assertEqual(
_sort_files(['a.md', 'b.md', 'a.md']),
['a.md', 'a.md', 'b.md'],
)
self.assertEqual(
_sort_files(['A.md', 'B.md', 'README.md']),
['README.md', 'A.md', 'B.md'],
)
def test_md_file(self):
f = File('foo.md', '/path/to/docs', '/path/to/site', use_directory_urls=False)
self.assertEqual(f.src_uri, 'foo.md')
self.assertPathsEqual(f.abs_src_path, '/path/to/docs/foo.md')
self.assertEqual(f.dest_uri, 'foo.html')
self.assertPathsEqual(f.abs_dest_path, '/path/to/site/foo.html')
self.assertEqual(f.url, 'foo.html')
self.assertEqual(f.name, 'foo')
self.assertTrue(f.is_documentation_page())
self.assertFalse(f.is_static_page())
self.assertFalse(f.is_media_file())
self.assertFalse(f.is_javascript())
self.assertFalse(f.is_css())
def test_md_file_use_directory_urls(self):
f = File('foo.md', '/path/to/docs', '/path/to/site', use_directory_urls=True)
self.assertEqual(f.src_uri, 'foo.md')
self.assertPathsEqual(f.abs_src_path, '/path/to/docs/foo.md')
self.assertEqual(f.dest_uri, 'foo/index.html')
self.assertPathsEqual(f.abs_dest_path, '/path/to/site/foo/index.html')
self.assertEqual(f.url, 'foo/')
self.assertEqual(f.name, 'foo')
self.assertTrue(f.is_documentation_page())
self.assertFalse(f.is_static_page())
self.assertFalse(f.is_media_file())
self.assertFalse(f.is_javascript())
self.assertFalse(f.is_css())
def test_md_file_nested(self):
f = File('foo/bar.md', '/path/to/docs', '/path/to/site', use_directory_urls=False)
self.assertEqual(f.src_uri, 'foo/bar.md')
self.assertPathsEqual(f.abs_src_path, '/path/to/docs/foo/bar.md')
self.assertEqual(f.dest_uri, 'foo/bar.html')
self.assertPathsEqual(f.abs_dest_path, '/path/to/site/foo/bar.html')
self.assertEqual(f.url, 'foo/bar.html')
self.assertEqual(f.name, 'bar')
self.assertTrue(f.is_documentation_page())
self.assertFalse(f.is_static_page())
self.assertFalse(f.is_media_file())
self.assertFalse(f.is_javascript())
self.assertFalse(f.is_css())
def test_md_file_nested_use_directory_urls(self):
f = File('foo/bar.md', '/path/to/docs', '/path/to/site', use_directory_urls=True)
self.assertEqual(f.src_uri, 'foo/bar.md')
self.assertPathsEqual(f.abs_src_path, '/path/to/docs/foo/bar.md')
self.assertEqual(f.dest_uri, 'foo/bar/index.html')
self.assertPathsEqual(f.abs_dest_path, '/path/to/site/foo/bar/index.html')
self.assertEqual(f.url, 'foo/bar/')
self.assertEqual(f.name, 'bar')
self.assertTrue(f.is_documentation_page())
self.assertFalse(f.is_static_page())
self.assertFalse(f.is_media_file())
self.assertFalse(f.is_javascript())
self.assertFalse(f.is_css())
def test_md_index_file(self):
f = File('index.md', '/path/to/docs', '/path/to/site', use_directory_urls=False)
self.assertEqual(f.src_uri, 'index.md')
self.assertPathsEqual(f.abs_src_path, '/path/to/docs/index.md')
self.assertEqual(f.dest_uri, 'index.html')
self.assertPathsEqual(f.abs_dest_path, '/path/to/site/index.html')
self.assertEqual(f.url, 'index.html')
self.assertEqual(f.name, 'index')
self.assertTrue(f.is_documentation_page())
self.assertFalse(f.is_static_page())
self.assertFalse(f.is_media_file())
self.assertFalse(f.is_javascript())
self.assertFalse(f.is_css())
def test_md_readme_index_file(self):
f = File('README.md', '/path/to/docs', '/path/to/site', use_directory_urls=False)
self.assertEqual(f.src_uri, 'README.md')
self.assertPathsEqual(f.abs_src_path, '/path/to/docs/README.md')
self.assertEqual(f.dest_uri, 'index.html')
self.assertPathsEqual(f.abs_dest_path, '/path/to/site/index.html')
self.assertEqual(f.url, 'index.html')
self.assertEqual(f.name, 'index')
self.assertTrue(f.is_documentation_page())
self.assertFalse(f.is_static_page())
self.assertFalse(f.is_media_file())
self.assertFalse(f.is_javascript())
self.assertFalse(f.is_css())
def test_md_index_file_use_directory_urls(self):
f = File('index.md', '/path/to/docs', '/path/to/site', use_directory_urls=True)
self.assertEqual(f.src_uri, 'index.md')
self.assertPathsEqual(f.abs_src_path, '/path/to/docs/index.md')
self.assertEqual(f.dest_uri, 'index.html')
self.assertPathsEqual(f.abs_dest_path, '/path/to/site/index.html')
self.assertEqual(f.url, './')
self.assertEqual(f.name, 'index')
self.assertTrue(f.is_documentation_page())
self.assertFalse(f.is_static_page())
self.assertFalse(f.is_media_file())
self.assertFalse(f.is_javascript())
self.assertFalse(f.is_css())
def test_md_readme_index_file_use_directory_urls(self):
f = File('README.md', '/path/to/docs', '/path/to/site', use_directory_urls=True)
self.assertEqual(f.src_uri, 'README.md')
self.assertPathsEqual(f.abs_src_path, '/path/to/docs/README.md')
self.assertEqual(f.dest_uri, 'index.html')
self.assertPathsEqual(f.abs_dest_path, '/path/to/site/index.html')
self.assertEqual(f.url, './')
self.assertEqual(f.name, 'index')
self.assertTrue(f.is_documentation_page())
self.assertFalse(f.is_static_page())
self.assertFalse(f.is_media_file())
self.assertFalse(f.is_javascript())
self.assertFalse(f.is_css())
def test_md_index_file_nested(self):
f = File('foo/index.md', '/path/to/docs', '/path/to/site', use_directory_urls=False)
self.assertEqual(f.src_uri, 'foo/index.md')
self.assertPathsEqual(f.abs_src_path, '/path/to/docs/foo/index.md')
self.assertEqual(f.dest_uri, 'foo/index.html')
self.assertPathsEqual(f.abs_dest_path, '/path/to/site/foo/index.html')
self.assertEqual(f.url, 'foo/index.html')
self.assertEqual(f.name, 'index')
self.assertTrue(f.is_documentation_page())
self.assertFalse(f.is_static_page())
self.assertFalse(f.is_media_file())
self.assertFalse(f.is_javascript())
self.assertFalse(f.is_css())
def test_md_index_file_nested_use_directory_urls(self):
f = File('foo/index.md', '/path/to/docs', '/path/to/site', use_directory_urls=True)
self.assertEqual(f.src_uri, 'foo/index.md')
self.assertPathsEqual(f.abs_src_path, '/path/to/docs/foo/index.md')
self.assertEqual(f.dest_uri, 'foo/index.html')
self.assertPathsEqual(f.abs_dest_path, '/path/to/site/foo/index.html')
self.assertEqual(f.url, 'foo/')
self.assertEqual(f.name, 'index')
self.assertTrue(f.is_documentation_page())
self.assertFalse(f.is_static_page())
self.assertFalse(f.is_media_file())
self.assertFalse(f.is_javascript())
self.assertFalse(f.is_css())
def test_static_file(self):
f = File('foo/bar.html', '/path/to/docs', '/path/to/site', use_directory_urls=False)
self.assertEqual(f.src_uri, 'foo/bar.html')
self.assertPathsEqual(f.abs_src_path, '/path/to/docs/foo/bar.html')
self.assertEqual(f.dest_uri, 'foo/bar.html')
self.assertPathsEqual(f.abs_dest_path, '/path/to/site/foo/bar.html')
self.assertEqual(f.url, 'foo/bar.html')
self.assertEqual(f.name, 'bar')
self.assertFalse(f.is_documentation_page())
self.assertTrue(f.is_static_page())
self.assertFalse(f.is_media_file())
self.assertFalse(f.is_javascript())
self.assertFalse(f.is_css())
def test_static_file_use_directory_urls(self):
f = File('foo/bar.html', '/path/to/docs', '/path/to/site', use_directory_urls=True)
self.assertEqual(f.src_uri, 'foo/bar.html')
self.assertPathsEqual(f.abs_src_path, '/path/to/docs/foo/bar.html')
self.assertEqual(f.dest_uri, 'foo/bar.html')
self.assertPathsEqual(f.abs_dest_path, '/path/to/site/foo/bar.html')
self.assertEqual(f.url, 'foo/bar.html')
self.assertEqual(f.name, 'bar')
self.assertFalse(f.is_documentation_page())
self.assertTrue(f.is_static_page())
self.assertFalse(f.is_media_file())
self.assertFalse(f.is_javascript())
self.assertFalse(f.is_css())
def test_media_file(self):
f = File('foo/bar.jpg', '/path/to/docs', '/path/to/site', use_directory_urls=False)
self.assertEqual(f.src_uri, 'foo/bar.jpg')
self.assertPathsEqual(f.abs_src_path, '/path/to/docs/foo/bar.jpg')
self.assertEqual(f.dest_uri, 'foo/bar.jpg')
self.assertPathsEqual(f.abs_dest_path, '/path/to/site/foo/bar.jpg')
self.assertEqual(f.url, 'foo/bar.jpg')
self.assertEqual(f.name, 'bar')
self.assertFalse(f.is_documentation_page())
self.assertFalse(f.is_static_page())
self.assertTrue(f.is_media_file())
self.assertFalse(f.is_javascript())
self.assertFalse(f.is_css())
def test_media_file_use_directory_urls(self):
f = File('foo/bar.jpg', '/path/to/docs', '/path/to/site', use_directory_urls=True)
self.assertEqual(f.src_uri, 'foo/bar.jpg')
self.assertPathsEqual(f.abs_src_path, '/path/to/docs/foo/bar.jpg')
self.assertEqual(f.dest_uri, 'foo/bar.jpg')
self.assertPathsEqual(f.abs_dest_path, '/path/to/site/foo/bar.jpg')
self.assertEqual(f.url, 'foo/bar.jpg')
self.assertEqual(f.name, 'bar')
self.assertFalse(f.is_documentation_page())
self.assertFalse(f.is_static_page())
self.assertTrue(f.is_media_file())
self.assertFalse(f.is_javascript())
self.assertFalse(f.is_css())
def test_javascript_file(self):
f = File('foo/bar.js', '/path/to/docs', '/path/to/site', use_directory_urls=False)
self.assertEqual(f.src_uri, 'foo/bar.js')
self.assertPathsEqual(f.abs_src_path, '/path/to/docs/foo/bar.js')
self.assertEqual(f.dest_uri, 'foo/bar.js')
self.assertPathsEqual(f.abs_dest_path, '/path/to/site/foo/bar.js')
self.assertEqual(f.url, 'foo/bar.js')
self.assertEqual(f.name, 'bar')
self.assertFalse(f.is_documentation_page())
self.assertFalse(f.is_static_page())
self.assertTrue(f.is_media_file())
self.assertTrue(f.is_javascript())
self.assertFalse(f.is_css())
def test_javascript_file_use_directory_urls(self):
f = File('foo/bar.js', '/path/to/docs', '/path/to/site', use_directory_urls=True)
self.assertEqual(f.src_uri, 'foo/bar.js')
self.assertPathsEqual(f.abs_src_path, '/path/to/docs/foo/bar.js')
self.assertEqual(f.dest_uri, 'foo/bar.js')
self.assertPathsEqual(f.abs_dest_path, '/path/to/site/foo/bar.js')
self.assertEqual(f.url, 'foo/bar.js')
self.assertEqual(f.name, 'bar')
self.assertFalse(f.is_documentation_page())
self.assertFalse(f.is_static_page())
self.assertTrue(f.is_media_file())
self.assertTrue(f.is_javascript())
self.assertFalse(f.is_css())
def test_css_file(self):
f = File('foo/bar.css', '/path/to/docs', '/path/to/site', use_directory_urls=False)
self.assertEqual(f.src_uri, 'foo/bar.css')
self.assertPathsEqual(f.abs_src_path, '/path/to/docs/foo/bar.css')
self.assertEqual(f.dest_uri, 'foo/bar.css')
self.assertPathsEqual(f.abs_dest_path, '/path/to/site/foo/bar.css')
self.assertEqual(f.url, 'foo/bar.css')
self.assertEqual(f.name, 'bar')
self.assertFalse(f.is_documentation_page())
self.assertFalse(f.is_static_page())
self.assertTrue(f.is_media_file())
self.assertFalse(f.is_javascript())
self.assertTrue(f.is_css())
def test_css_file_use_directory_urls(self):
f = File('foo/bar.css', '/path/to/docs', '/path/to/site', use_directory_urls=True)
self.assertEqual(f.src_uri, 'foo/bar.css')
self.assertPathsEqual(f.abs_src_path, '/path/to/docs/foo/bar.css')
self.assertEqual(f.dest_uri, 'foo/bar.css')
self.assertPathsEqual(f.abs_dest_path, '/path/to/site/foo/bar.css')
self.assertEqual(f.url, 'foo/bar.css')
self.assertEqual(f.name, 'bar')
self.assertFalse(f.is_documentation_page())
self.assertFalse(f.is_static_page())
self.assertTrue(f.is_media_file())
self.assertFalse(f.is_javascript())
self.assertTrue(f.is_css())
def test_file_name_with_space(self):
f = File('foo bar.md', '/path/to/docs', '/path/to/site', use_directory_urls=False)
self.assertEqual(f.src_uri, 'foo bar.md')
self.assertPathsEqual(f.abs_src_path, '/path/to/docs/foo bar.md')
self.assertEqual(f.dest_uri, 'foo bar.html')
self.assertPathsEqual(f.abs_dest_path, '/path/to/site/foo bar.html')
self.assertEqual(f.url, 'foo%20bar.html')
self.assertEqual(f.name, 'foo bar')
def test_files(self):
fs = [
File('index.md', '/path/to/docs', '/path/to/site', use_directory_urls=True),
File('foo/bar.md', '/path/to/docs', '/path/to/site', use_directory_urls=True),
File('foo/bar.html', '/path/to/docs', '/path/to/site', use_directory_urls=True),
File('foo/bar.jpg', '/path/to/docs', '/path/to/site', use_directory_urls=True),
File('foo/bar.js', '/path/to/docs', '/path/to/site', use_directory_urls=True),
File('foo/bar.css', '/path/to/docs', '/path/to/site', use_directory_urls=True),
]
files = Files(fs)
self.assertEqual([f for f in files], fs)
self.assertEqual(len(files), 6)
self.assertEqual(files.documentation_pages(), [fs[0], fs[1]])
self.assertEqual(files.static_pages(), [fs[2]])
self.assertEqual(files.media_files(), [fs[3], fs[4], fs[5]])
self.assertEqual(files.javascript_files(), [fs[4]])
self.assertEqual(files.css_files(), [fs[5]])
self.assertEqual(files.get_file_from_path('foo/bar.jpg'), fs[3])
self.assertEqual(files.get_file_from_path('foo/bar.jpg'), fs[3])
self.assertEqual(files.get_file_from_path('missing.jpg'), None)
self.assertTrue(fs[2].src_uri in files.src_uris)
self.assertTrue(fs[2].src_uri in files.src_uris)
extra_file = File('extra.md', '/path/to/docs', '/path/to/site', use_directory_urls=True)
self.assertFalse(extra_file.src_uri in files.src_uris)
files.append(extra_file)
self.assertEqual(len(files), 7)
self.assertTrue(extra_file.src_uri in files.src_uris)
self.assertEqual(files.documentation_pages(), [fs[0], fs[1], extra_file])
@tempdir(
files=[
'favicon.ico',
'index.md',
]
)
@tempdir(
files=[
'base.html',
'favicon.ico',
'style.css',
'foo.md',
'README',
'.ignore.txt',
'.ignore/file.txt',
'foo/.ignore.txt',
'foo/.ignore/file.txt',
]
)
def test_add_files_from_theme(self, tdir, ddir):
config = load_config(docs_dir=ddir, theme={'name': None, 'custom_dir': tdir})
env = config.theme.get_env()
files = get_files(config)
self.assertEqual(
[file.src_path for file in files],
['index.md', 'favicon.ico'],
)
files.add_files_from_theme(env, config)
self.assertEqual(
[file.src_path for file in files],
['index.md', 'favicon.ico', 'style.css'],
)
# Ensure theme file does not override docs_dir file
self.assertEqual(
files.get_file_from_path('favicon.ico').abs_src_path,
os.path.normpath(os.path.join(ddir, 'favicon.ico')),
)
def test_filter_paths(self):
# Root level file
self.assertFalse(_filter_paths('foo.md', 'foo.md', False, ['bar.md']))
self.assertTrue(_filter_paths('foo.md', 'foo.md', False, ['foo.md']))
# Nested file
self.assertFalse(_filter_paths('foo.md', 'baz/foo.md', False, ['bar.md']))
self.assertTrue(_filter_paths('foo.md', 'baz/foo.md', False, ['foo.md']))
# Wildcard
self.assertFalse(_filter_paths('foo.md', 'foo.md', False, ['*.txt']))
self.assertTrue(_filter_paths('foo.md', 'foo.md', False, ['*.md']))
# Root level dir
self.assertFalse(_filter_paths('bar', 'bar', True, ['/baz']))
self.assertFalse(_filter_paths('bar', 'bar', True, ['/baz/']))
self.assertTrue(_filter_paths('bar', 'bar', True, ['/bar']))
self.assertTrue(_filter_paths('bar', 'bar', True, ['/bar/']))
# Nested dir
self.assertFalse(_filter_paths('bar', 'foo/bar', True, ['/bar']))
self.assertFalse(_filter_paths('bar', 'foo/bar', True, ['/bar/']))
self.assertTrue(_filter_paths('bar', 'foo/bar', True, ['bar/']))
# Files that look like dirs (no extension). Note that `is_dir` is `False`.
self.assertFalse(_filter_paths('bar', 'bar', False, ['bar/']))
self.assertFalse(_filter_paths('bar', 'foo/bar', False, ['bar/']))
def test_get_relative_url_use_directory_urls(self):
to_files = [
'index.md',
'foo/index.md',
'foo/bar/index.md',
'foo/bar/baz/index.md',
'foo.md',
'foo/bar.md',
'foo/bar/baz.md',
]
to_file_urls = [
'./',
'foo/',
'foo/bar/',
'foo/bar/baz/',
'foo/',
'foo/bar/',
'foo/bar/baz/',
]
from_file = File('img.jpg', '/path/to/docs', '/path/to/site', use_directory_urls=True)
expected = [
'img.jpg', # img.jpg relative to .
'../img.jpg', # img.jpg relative to foo/
'../../img.jpg', # img.jpg relative to foo/bar/
'../../../img.jpg', # img.jpg relative to foo/bar/baz/
'../img.jpg', # img.jpg relative to foo
'../../img.jpg', # img.jpg relative to foo/bar
'../../../img.jpg', # img.jpg relative to foo/bar/baz
]
for i, filename in enumerate(to_files):
file = File(filename, '/path/to/docs', '/path/to/site', use_directory_urls=True)
self.assertEqual(from_file.url, 'img.jpg')
self.assertEqual(file.url, to_file_urls[i])
self.assertEqual(from_file.url_relative_to(file.url), expected[i])
self.assertEqual(from_file.url_relative_to(file), expected[i])
from_file = File('foo/img.jpg', '/path/to/docs', '/path/to/site', use_directory_urls=True)
expected = [
'foo/img.jpg', # foo/img.jpg relative to .
'img.jpg', # foo/img.jpg relative to foo/
'../img.jpg', # foo/img.jpg relative to foo/bar/
'../../img.jpg', # foo/img.jpg relative to foo/bar/baz/
'img.jpg', # foo/img.jpg relative to foo
'../img.jpg', # foo/img.jpg relative to foo/bar
'../../img.jpg', # foo/img.jpg relative to foo/bar/baz
]
for i, filename in enumerate(to_files):
file = File(filename, '/path/to/docs', '/path/to/site', use_directory_urls=True)
self.assertEqual(from_file.url, 'foo/img.jpg')
self.assertEqual(file.url, to_file_urls[i])
self.assertEqual(from_file.url_relative_to(file.url), expected[i])
self.assertEqual(from_file.url_relative_to(file), expected[i])
from_file = File('index.html', '/path/to/docs', '/path/to/site', use_directory_urls=True)
expected = [
'./', # . relative to .
'../', # . relative to foo/
'../../', # . relative to foo/bar/
'../../../', # . relative to foo/bar/baz/
'../', # . relative to foo
'../../', # . relative to foo/bar
'../../../', # . relative to foo/bar/baz
]
for i, filename in enumerate(to_files):
file = File(filename, '/path/to/docs', '/path/to/site', use_directory_urls=True)
self.assertEqual(from_file.url, './')
self.assertEqual(file.url, to_file_urls[i])
self.assertEqual(from_file.url_relative_to(file.url), expected[i])
self.assertEqual(from_file.url_relative_to(file), expected[i])
from_file = File('file.md', '/path/to/docs', '/path/to/site', use_directory_urls=True)
expected = [
'file/', # file relative to .
'../file/', # file relative to foo/
'../../file/', # file relative to foo/bar/
'../../../file/', # file relative to foo/bar/baz/
'../file/', # file relative to foo
'../../file/', # file relative to foo/bar
'../../../file/', # file relative to foo/bar/baz
]
for i, filename in enumerate(to_files):
file = File(filename, '/path/to/docs', '/path/to/site', use_directory_urls=True)
self.assertEqual(from_file.url, 'file/')
self.assertEqual(file.url, to_file_urls[i])
self.assertEqual(from_file.url_relative_to(file.url), expected[i])
self.assertEqual(from_file.url_relative_to(file), expected[i])
def test_get_relative_url(self):
to_files = [
'index.md',
'foo/index.md',
'foo/bar/index.md',
'foo/bar/baz/index.md',
'foo.md',
'foo/bar.md',
'foo/bar/baz.md',
]
to_file_urls = [
'index.html',
'foo/index.html',
'foo/bar/index.html',
'foo/bar/baz/index.html',
'foo.html',
'foo/bar.html',
'foo/bar/baz.html',
]
from_file = File('img.jpg', '/path/to/docs', '/path/to/site', use_directory_urls=False)
expected = [
'img.jpg', # img.jpg relative to .
'../img.jpg', # img.jpg relative to foo/
'../../img.jpg', # img.jpg relative to foo/bar/
'../../../img.jpg', # img.jpg relative to foo/bar/baz/
'img.jpg', # img.jpg relative to foo.html
'../img.jpg', # img.jpg relative to foo/bar.html
'../../img.jpg', # img.jpg relative to foo/bar/baz.html
]
for i, filename in enumerate(to_files):
with self.subTest(from_file=from_file.src_path, to_file=filename):
file = File(filename, '/path/to/docs', '/path/to/site', use_directory_urls=False)
self.assertEqual(from_file.url, 'img.jpg')
self.assertEqual(file.url, to_file_urls[i])
self.assertEqual(from_file.url_relative_to(file.url), expected[i])
self.assertEqual(from_file.url_relative_to(file), expected[i])
from_file = File('foo/img.jpg', '/path/to/docs', '/path/to/site', use_directory_urls=False)
expected = [
'foo/img.jpg', # foo/img.jpg relative to .
'img.jpg', # foo/img.jpg relative to foo/
'../img.jpg', # foo/img.jpg relative to foo/bar/
'../../img.jpg', # foo/img.jpg relative to foo/bar/baz/
'foo/img.jpg', # foo/img.jpg relative to foo.html
'img.jpg', # foo/img.jpg relative to foo/bar.html
'../img.jpg', # foo/img.jpg relative to foo/bar/baz.html
]
for i, filename in enumerate(to_files):
with self.subTest(from_file=from_file.src_path, to_file=filename):
file = File(filename, '/path/to/docs', '/path/to/site', use_directory_urls=False)
self.assertEqual(from_file.url, 'foo/img.jpg')
self.assertEqual(file.url, to_file_urls[i])
self.assertEqual(from_file.url_relative_to(file.url), expected[i])
self.assertEqual(from_file.url_relative_to(file), expected[i])
from_file = File('index.html', '/path/to/docs', '/path/to/site', use_directory_urls=False)
expected = [
'index.html', # index.html relative to .
'../index.html', # index.html relative to foo/
'../../index.html', # index.html relative to foo/bar/
'../../../index.html', # index.html relative to foo/bar/baz/
'index.html', # index.html relative to foo.html
'../index.html', # index.html relative to foo/bar.html
'../../index.html', # index.html relative to foo/bar/baz.html
]
for i, filename in enumerate(to_files):
with self.subTest(from_file=from_file.src_path, to_file=filename):
file = File(filename, '/path/to/docs', '/path/to/site', use_directory_urls=False)
self.assertEqual(from_file.url, 'index.html')
self.assertEqual(file.url, to_file_urls[i])
self.assertEqual(from_file.url_relative_to(file.url), expected[i])
self.assertEqual(from_file.url_relative_to(file), expected[i])
from_file = File('file.html', '/path/to/docs', '/path/to/site', use_directory_urls=False)
expected = [
'file.html', # file.html relative to .
'../file.html', # file.html relative to foo/
'../../file.html', # file.html relative to foo/bar/
'../../../file.html', # file.html relative to foo/bar/baz/
'file.html', # file.html relative to foo.html
'../file.html', # file.html relative to foo/bar.html
'../../file.html', # file.html relative to foo/bar/baz.html
]
for i, filename in enumerate(to_files):
with self.subTest(from_file=from_file.src_path, to_file=filename):
file = File(filename, '/path/to/docs', '/path/to/site', use_directory_urls=False)
self.assertEqual(from_file.url, 'file.html')
self.assertEqual(file.url, to_file_urls[i])
self.assertEqual(from_file.url_relative_to(file.url), expected[i])
self.assertEqual(from_file.url_relative_to(file), expected[i])
@tempdir(
files=[
'index.md',
'readme.md',
'bar.css',
'bar.html',
'bar.jpg',
'bar.js',
'bar.md',
'.dotfile',
'templates/foo.html',
]
)
def test_get_files(self, tdir):
config = load_config(docs_dir=tdir, extra_css=['bar.css'], extra_javascript=['bar.js'])
files = get_files(config)
expected = ['index.md', 'bar.css', 'bar.html', 'bar.jpg', 'bar.js', 'bar.md', 'readme.md']
self.assertIsInstance(files, Files)
self.assertEqual(len(files), len(expected))
self.assertEqual([f.src_path for f in files], expected)
@tempdir(
files=[
'README.md',
'foo.md',
]
)
def test_get_files_include_readme_without_index(self, tdir):
config = load_config(docs_dir=tdir)
files = get_files(config)
expected = ['README.md', 'foo.md']
self.assertIsInstance(files, Files)
self.assertEqual(len(files), len(expected))
self.assertEqual([f.src_path for f in files], expected)
@tempdir(
files=[
'index.md',
'README.md',
'foo.md',
]
)
def test_get_files_exclude_readme_with_index(self, tdir):
config = load_config(docs_dir=tdir)
with self.assertLogs('mkdocs') as cm:
files = get_files(config)
self.assertRegex(
'\n'.join(cm.output),
r"^WARNING:mkdocs.structure.files:Both index.md and README.md found. Skipping README.md .+$",
)
expected = ['index.md', 'foo.md']
self.assertIsInstance(files, Files)
self.assertEqual(len(files), len(expected))
self.assertEqual([f.src_path for f in files], expected)
@tempdir()
@tempdir(files={'test.txt': 'source content'})
def test_copy_file(self, src_dir, dest_dir):
file = File('test.txt', src_dir, dest_dir, use_directory_urls=False)
dest_path = os.path.join(dest_dir, 'test.txt')
self.assertPathNotExists(dest_path)
file.copy_file()
self.assertPathIsFile(dest_path)
@tempdir(files={'test.txt': 'source content'})
def test_copy_file_same_file(self, dest_dir):
file = File('test.txt', dest_dir, dest_dir, use_directory_urls=False)
dest_path = os.path.join(dest_dir, 'test.txt')
file.copy_file()
self.assertPathIsFile(dest_path)
with open(dest_path, encoding='utf-8') as f:
self.assertEqual(f.read(), 'source content')
@tempdir(files={'test.txt': 'destination content'})
@tempdir(files={'test.txt': 'source content'})
def test_copy_file_clean_modified(self, src_dir, dest_dir):
file = File('test.txt', src_dir, dest_dir, use_directory_urls=False)
file.is_modified = mock.Mock(return_value=True)
dest_path = os.path.join(dest_dir, 'test.txt')
file.copy_file(dirty=False)
self.assertPathIsFile(dest_path)
with open(dest_path, encoding='utf-8') as f:
self.assertEqual(f.read(), 'source content')
@tempdir(files={'test.txt': 'destination content'})
@tempdir(files={'test.txt': 'source content'})
def test_copy_file_dirty_modified(self, src_dir, dest_dir):
file = File('test.txt', src_dir, dest_dir, use_directory_urls=False)
file.is_modified = mock.Mock(return_value=True)
dest_path = os.path.join(dest_dir, 'test.txt')
file.copy_file(dirty=True)
self.assertPathIsFile(dest_path)
with open(dest_path, encoding='utf-8') as f:
self.assertEqual(f.read(), 'source content')
@tempdir(files={'test.txt': 'destination content'})
@tempdir(files={'test.txt': 'source content'})
def test_copy_file_dirty_not_modified(self, src_dir, dest_dir):
file = File('test.txt', src_dir, dest_dir, use_directory_urls=False)
file.is_modified = mock.Mock(return_value=False)
dest_path = os.path.join(dest_dir, 'test.txt')
file.copy_file(dirty=True)
self.assertPathIsFile(dest_path)
with open(dest_path, encoding='utf-8') as f:
self.assertEqual(f.read(), 'destination content')
def test_files_append_remove_src_paths(self):
fs = [
File('index.md', '/path/to/docs', '/path/to/site', use_directory_urls=True),
File('foo/bar.md', '/path/to/docs', '/path/to/site', use_directory_urls=True),
File('foo/bar.html', '/path/to/docs', '/path/to/site', use_directory_urls=True),
File('foo/bar.jpg', '/path/to/docs', '/path/to/site', use_directory_urls=True),
File('foo/bar.js', '/path/to/docs', '/path/to/site', use_directory_urls=True),
File('foo/bar.css', '/path/to/docs', '/path/to/site', use_directory_urls=True),
]
files = Files(fs)
self.assertEqual(len(files), 6)
self.assertEqual(len(files.src_uris), 6)
extra_file = File('extra.md', '/path/to/docs', '/path/to/site', use_directory_urls=True)
self.assertFalse(extra_file.src_uri in files.src_uris)
files.append(extra_file)
self.assertEqual(len(files), 7)
self.assertEqual(len(files.src_uris), 7)
self.assertTrue(extra_file.src_uri in files.src_uris)
files.remove(extra_file)
self.assertEqual(len(files), 6)
self.assertEqual(len(files.src_uris), 6)
self.assertFalse(extra_file.src_uri in files.src_uris)

View File

@ -0,0 +1,477 @@
#!/usr/bin/env python
import sys
import unittest
from mkdocs.structure.files import File, Files
from mkdocs.structure.nav import Section, _get_by_type, get_navigation
from mkdocs.structure.pages import Page
from mkdocs.tests.base import dedent, load_config
class SiteNavigationTests(unittest.TestCase):
maxDiff = None
def test_simple_nav(self):
nav_cfg = [
{'Home': 'index.md'},
{'About': 'about.md'},
]
expected = dedent(
"""
Page(title='Home', url='/')
Page(title='About', url='/about/')
"""
)
cfg = load_config(nav=nav_cfg, site_url='http://example.com/')
fs = [
File(
list(item.values())[0], cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']
)
for item in nav_cfg
]
files = Files(fs)
site_navigation = get_navigation(files, cfg)
self.assertEqual(str(site_navigation).strip(), expected)
self.assertEqual(len(site_navigation.items), 2)
self.assertEqual(len(site_navigation.pages), 2)
self.assertEqual(repr(site_navigation.homepage), "Page(title='Home', url='/')")
def test_nav_no_directory_urls(self):
nav_cfg = [
{'Home': 'index.md'},
{'About': 'about.md'},
]
expected = dedent(
"""
Page(title='Home', url='/index.html')
Page(title='About', url='/about.html')
"""
)
cfg = load_config(nav=nav_cfg, use_directory_urls=False, site_url='http://example.com/')
fs = [
File(
list(item.values())[0], cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']
)
for item in nav_cfg
]
files = Files(fs)
site_navigation = get_navigation(files, cfg)
self.assertEqual(str(site_navigation).strip(), expected)
self.assertEqual(len(site_navigation.items), 2)
self.assertEqual(len(site_navigation.pages), 2)
self.assertEqual(repr(site_navigation.homepage), "Page(title='Home', url='/index.html')")
def test_nav_missing_page(self):
nav_cfg = [
{'Home': 'index.md'},
]
expected = dedent(
"""
Page(title='Home', url='/')
"""
)
cfg = load_config(nav=nav_cfg, site_url='http://example.com/')
fs = [
File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']),
File('page_not_in_nav.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']),
]
files = Files(fs)
site_navigation = get_navigation(files, cfg)
self.assertEqual(str(site_navigation).strip(), expected)
self.assertEqual(len(site_navigation.items), 1)
self.assertEqual(len(site_navigation.pages), 1)
for file in files:
self.assertIsInstance(file.page, Page)
def test_nav_no_title(self):
nav_cfg = [
'index.md',
{'About': 'about.md'},
]
expected = dedent(
"""
Page(title=[blank], url='/')
Page(title='About', url='/about/')
"""
)
cfg = load_config(nav=nav_cfg, site_url='http://example.com/')
fs = [
File(nav_cfg[0], cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']),
File(nav_cfg[1]['About'], cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']),
]
files = Files(fs)
site_navigation = get_navigation(files, cfg)
self.assertEqual(str(site_navigation).strip(), expected)
self.assertEqual(len(site_navigation.items), 2)
self.assertEqual(len(site_navigation.pages), 2)
def test_nav_external_links(self):
nav_cfg = [
{'Home': 'index.md'},
{'Local': '/local.html'},
{'External': 'http://example.com/external.html'},
]
expected = dedent(
"""
Page(title='Home', url='/')
Link(title='Local', url='/local.html')
Link(title='External', url='http://example.com/external.html')
"""
)
cfg = load_config(nav=nav_cfg, site_url='http://example.com/')
fs = [File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])]
files = Files(fs)
with self.assertLogs('mkdocs', level='DEBUG') as cm:
site_navigation = get_navigation(files, cfg)
self.assertEqual(
cm.output,
[
"DEBUG:mkdocs.structure.nav:An absolute path to '/local.html' is included in the "
"'nav' configuration, which presumably points to an external resource.",
"DEBUG:mkdocs.structure.nav:An external link to 'http://example.com/external.html' "
"is included in the 'nav' configuration.",
],
)
self.assertEqual(str(site_navigation).strip(), expected)
self.assertEqual(len(site_navigation.items), 3)
self.assertEqual(len(site_navigation.pages), 1)
def test_nav_bad_links(self):
nav_cfg = [
{'Home': 'index.md'},
{'Missing': 'missing.html'},
{'Bad External': 'example.com'},
]
expected = dedent(
"""
Page(title='Home', url='/')
Link(title='Missing', url='missing.html')
Link(title='Bad External', url='example.com')
"""
)
cfg = load_config(nav=nav_cfg, site_url='http://example.com/')
fs = [File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])]
files = Files(fs)
with self.assertLogs('mkdocs') as cm:
site_navigation = get_navigation(files, cfg)
self.assertEqual(
cm.output,
[
"WARNING:mkdocs.structure.nav:A relative path to 'missing.html' is included "
"in the 'nav' configuration, which is not found in the documentation files",
"WARNING:mkdocs.structure.nav:A relative path to 'example.com' is included "
"in the 'nav' configuration, which is not found in the documentation files",
],
)
self.assertEqual(str(site_navigation).strip(), expected)
self.assertEqual(len(site_navigation.items), 3)
self.assertEqual(len(site_navigation.pages), 1)
def test_indented_nav(self):
nav_cfg = [
{'Home': 'index.md'},
{
'API Guide': [
{'Running': 'api-guide/running.md'},
{'Testing': 'api-guide/testing.md'},
{'Debugging': 'api-guide/debugging.md'},
{
'Advanced': [
{'Part 1': 'api-guide/advanced/part-1.md'},
]
},
]
},
{
'About': [
{'Release notes': 'about/release-notes.md'},
{'License': '/license.html'},
]
},
{'External': 'https://example.com/'},
]
expected = dedent(
"""
Page(title='Home', url='/')
Section(title='API Guide')
Page(title='Running', url='/api-guide/running/')
Page(title='Testing', url='/api-guide/testing/')
Page(title='Debugging', url='/api-guide/debugging/')
Section(title='Advanced')
Page(title='Part 1', url='/api-guide/advanced/part-1/')
Section(title='About')
Page(title='Release notes', url='/about/release-notes/')
Link(title='License', url='/license.html')
Link(title='External', url='https://example.com/')
"""
)
cfg = load_config(nav=nav_cfg, site_url='http://example.com/')
fs = [
'index.md',
'api-guide/running.md',
'api-guide/testing.md',
'api-guide/debugging.md',
'api-guide/advanced/part-1.md',
'about/release-notes.md',
]
files = Files(
[File(s, cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) for s in fs]
)
site_navigation = get_navigation(files, cfg)
self.assertEqual(str(site_navigation).strip(), expected)
self.assertEqual(len(site_navigation.items), 4)
self.assertEqual(len(site_navigation.pages), 6)
self.assertEqual(repr(site_navigation.homepage), "Page(title='Home', url='/')")
self.assertIsNone(site_navigation.items[0].parent)
self.assertEqual(site_navigation.items[0].ancestors, [])
self.assertIsNone(site_navigation.items[1].parent)
self.assertEqual(site_navigation.items[1].ancestors, [])
self.assertEqual(len(site_navigation.items[1].children), 4)
self.assertEqual(
repr(site_navigation.items[1].children[0].parent), "Section(title='API Guide')"
)
self.assertEqual(site_navigation.items[1].children[0].ancestors, [site_navigation.items[1]])
self.assertEqual(
repr(site_navigation.items[1].children[1].parent), "Section(title='API Guide')"
)
self.assertEqual(site_navigation.items[1].children[1].ancestors, [site_navigation.items[1]])
self.assertEqual(
repr(site_navigation.items[1].children[2].parent), "Section(title='API Guide')"
)
self.assertEqual(site_navigation.items[1].children[2].ancestors, [site_navigation.items[1]])
self.assertEqual(
repr(site_navigation.items[1].children[3].parent), "Section(title='API Guide')"
)
self.assertEqual(site_navigation.items[1].children[3].ancestors, [site_navigation.items[1]])
self.assertEqual(len(site_navigation.items[1].children[3].children), 1)
self.assertEqual(
repr(site_navigation.items[1].children[3].children[0].parent),
"Section(title='Advanced')",
)
self.assertEqual(
site_navigation.items[1].children[3].children[0].ancestors,
[site_navigation.items[1].children[3], site_navigation.items[1]],
)
self.assertIsNone(site_navigation.items[2].parent)
self.assertEqual(len(site_navigation.items[2].children), 2)
self.assertEqual(
repr(site_navigation.items[2].children[0].parent), "Section(title='About')"
)
self.assertEqual(site_navigation.items[2].children[0].ancestors, [site_navigation.items[2]])
self.assertEqual(
repr(site_navigation.items[2].children[1].parent), "Section(title='About')"
)
self.assertEqual(site_navigation.items[2].children[1].ancestors, [site_navigation.items[2]])
self.assertIsNone(site_navigation.items[3].parent)
self.assertEqual(site_navigation.items[3].ancestors, [])
self.assertIsNone(site_navigation.items[3].children)
def test_nested_ungrouped_nav(self):
nav_cfg = [
{'Home': 'index.md'},
{'Contact': 'about/contact.md'},
{'License Title': 'about/sub/license.md'},
]
expected = dedent(
"""
Page(title='Home', url='/')
Page(title='Contact', url='/about/contact/')
Page(title='License Title', url='/about/sub/license/')
"""
)
cfg = load_config(nav=nav_cfg, site_url='http://example.com/')
fs = [
File(
list(item.values())[0], cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']
)
for item in nav_cfg
]
files = Files(fs)
site_navigation = get_navigation(files, cfg)
self.assertEqual(str(site_navigation).strip(), expected)
self.assertEqual(len(site_navigation.items), 3)
self.assertEqual(len(site_navigation.pages), 3)
def test_nested_ungrouped_nav_no_titles(self):
nav_cfg = [
'index.md',
'about/contact.md',
'about/sub/license.md',
]
expected = dedent(
"""
Page(title=[blank], url='/')
Page(title=[blank], url='/about/contact/')
Page(title=[blank], url='/about/sub/license/')
"""
)
cfg = load_config(nav=nav_cfg, site_url='http://example.com/')
fs = [
File(item, cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])
for item in nav_cfg
]
files = Files(fs)
site_navigation = get_navigation(files, cfg)
self.assertEqual(str(site_navigation).strip(), expected)
self.assertEqual(len(site_navigation.items), 3)
self.assertEqual(len(site_navigation.pages), 3)
self.assertEqual(repr(site_navigation.homepage), "Page(title=[blank], url='/')")
@unittest.skipUnless(sys.platform.startswith("win"), "requires Windows")
def test_nested_ungrouped_no_titles_windows(self):
nav_cfg = [
'index.md',
'about\\contact.md',
'about\\sub\\license.md',
]
expected = dedent(
"""
Page(title=[blank], url='/')
Page(title=[blank], url='/about/contact/')
Page(title=[blank], url='/about/sub/license/')
"""
)
cfg = load_config(nav=nav_cfg, site_url='http://example.com/')
fs = [
File(item, cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])
for item in nav_cfg
]
files = Files(fs)
site_navigation = get_navigation(files, cfg)
self.assertEqual(str(site_navigation).strip(), expected)
self.assertEqual(len(site_navigation.items), 3)
self.assertEqual(len(site_navigation.pages), 3)
def test_nav_from_files(self):
expected = dedent(
"""
Page(title=[blank], url='/')
Page(title=[blank], url='/about/')
"""
)
cfg = load_config(site_url='http://example.com/')
fs = [
File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']),
File('about.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']),
]
files = Files(fs)
site_navigation = get_navigation(files, cfg)
self.assertEqual(str(site_navigation).strip(), expected)
self.assertEqual(len(site_navigation.items), 2)
self.assertEqual(len(site_navigation.pages), 2)
self.assertEqual(repr(site_navigation.homepage), "Page(title=[blank], url='/')")
def test_nav_from_nested_files(self):
expected = dedent(
"""
Page(title=[blank], url='/')
Section(title='About')
Page(title=[blank], url='/about/license/')
Page(title=[blank], url='/about/release-notes/')
Section(title='Api guide')
Page(title=[blank], url='/api-guide/debugging/')
Page(title=[blank], url='/api-guide/running/')
Page(title=[blank], url='/api-guide/testing/')
Section(title='Advanced')
Page(title=[blank], url='/api-guide/advanced/part-1/')
"""
)
cfg = load_config(site_url='http://example.com/')
fs = [
'index.md',
'about/license.md',
'about/release-notes.md',
'api-guide/debugging.md',
'api-guide/running.md',
'api-guide/testing.md',
'api-guide/advanced/part-1.md',
]
files = Files(
[File(s, cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) for s in fs]
)
site_navigation = get_navigation(files, cfg)
self.assertEqual(str(site_navigation).strip(), expected)
self.assertEqual(len(site_navigation.items), 3)
self.assertEqual(len(site_navigation.pages), 7)
self.assertEqual(repr(site_navigation.homepage), "Page(title=[blank], url='/')")
def test_active(self):
nav_cfg = [
{'Home': 'index.md'},
{
'API Guide': [
{'Running': 'api-guide/running.md'},
{'Testing': 'api-guide/testing.md'},
{'Debugging': 'api-guide/debugging.md'},
{
'Advanced': [
{'Part 1': 'api-guide/advanced/part-1.md'},
]
},
]
},
{
'About': [
{'Release notes': 'about/release-notes.md'},
{'License': 'about/license.md'},
]
},
]
cfg = load_config(nav=nav_cfg, site_url='http://example.com/')
fs = [
'index.md',
'api-guide/running.md',
'api-guide/testing.md',
'api-guide/debugging.md',
'api-guide/advanced/part-1.md',
'about/release-notes.md',
'about/license.md',
]
files = Files(
[File(s, cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) for s in fs]
)
site_navigation = get_navigation(files, cfg)
# Confirm nothing is active
self.assertTrue(all(page.active is False for page in site_navigation.pages))
self.assertTrue(all(item.active is False for item in site_navigation.items))
# Activate
site_navigation.items[1].children[3].children[0].active = True
# Confirm ancestors are activated
self.assertTrue(site_navigation.items[1].children[3].children[0].active)
self.assertTrue(site_navigation.items[1].children[3].active)
self.assertTrue(site_navigation.items[1].active)
# Confirm non-ancestors are not activated
self.assertFalse(site_navigation.items[0].active)
self.assertFalse(site_navigation.items[1].children[0].active)
self.assertFalse(site_navigation.items[1].children[1].active)
self.assertFalse(site_navigation.items[1].children[2].active)
self.assertFalse(site_navigation.items[2].active)
self.assertFalse(site_navigation.items[2].children[0].active)
self.assertFalse(site_navigation.items[2].children[1].active)
# Deactivate
site_navigation.items[1].children[3].children[0].active = False
# Confirm ancestors are deactivated
self.assertFalse(site_navigation.items[1].children[3].children[0].active)
self.assertFalse(site_navigation.items[1].children[3].active)
self.assertFalse(site_navigation.items[1].active)
def test_get_by_type_nested_sections(self):
nav_cfg = [
{
'Section 1': [
{
'Section 2': [
{'Page': 'page.md'},
]
},
]
},
]
cfg = load_config(nav=nav_cfg, site_url='http://example.com/')
fs = [File('page.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])]
files = Files(fs)
site_navigation = get_navigation(files, cfg)
self.assertEqual(len(_get_by_type(site_navigation, Section)), 2)

View File

@ -0,0 +1,815 @@
import functools
import os
import sys
import unittest
from unittest import mock
from mkdocs.structure.files import File, Files
from mkdocs.structure.pages import Page
from mkdocs.tests.base import dedent, load_config, tempdir
load_config = functools.lru_cache(maxsize=None)(load_config)
class PageTests(unittest.TestCase):
DOCS_DIR = os.path.join(
os.path.abspath(os.path.dirname(__file__)), '../integration/subpages/docs'
)
def test_homepage(self):
cfg = load_config(docs_dir=self.DOCS_DIR)
fl = File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])
self.assertIsNone(fl.page)
pg = Page('Foo', fl, cfg)
self.assertEqual(fl.page, pg)
self.assertEqual(pg.url, '')
self.assertEqual(pg.abs_url, None)
self.assertEqual(pg.canonical_url, None)
self.assertEqual(pg.edit_url, None)
self.assertEqual(pg.file, fl)
self.assertEqual(pg.content, None)
self.assertTrue(pg.is_homepage)
self.assertTrue(pg.is_index)
self.assertTrue(pg.is_page)
self.assertFalse(pg.is_section)
self.assertTrue(pg.is_top_level)
self.assertEqual(pg.markdown, None)
self.assertEqual(pg.meta, {})
self.assertEqual(pg.next_page, None)
self.assertEqual(pg.parent, None)
self.assertEqual(pg.previous_page, None)
self.assertEqual(pg.title, 'Foo')
self.assertEqual(pg.toc, [])
def test_nested_index_page(self):
cfg = load_config(docs_dir=self.DOCS_DIR)
fl = File('sub1/index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])
pg = Page('Foo', fl, cfg)
pg.parent = 'foo'
self.assertEqual(pg.url, 'sub1/')
self.assertEqual(pg.abs_url, None)
self.assertEqual(pg.canonical_url, None)
self.assertEqual(pg.edit_url, None)
self.assertEqual(pg.file, fl)
self.assertEqual(pg.content, None)
self.assertFalse(pg.is_homepage)
self.assertTrue(pg.is_index)
self.assertTrue(pg.is_page)
self.assertFalse(pg.is_section)
self.assertFalse(pg.is_top_level)
self.assertEqual(pg.markdown, None)
self.assertEqual(pg.meta, {})
self.assertEqual(pg.next_page, None)
self.assertEqual(pg.parent, 'foo')
self.assertEqual(pg.previous_page, None)
self.assertEqual(pg.title, 'Foo')
self.assertEqual(pg.toc, [])
def test_nested_index_page_no_parent(self):
cfg = load_config(docs_dir=self.DOCS_DIR)
fl = File('sub1/index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])
pg = Page('Foo', fl, cfg)
pg.parent = None # non-homepage at nav root level; see #1919.
self.assertEqual(pg.url, 'sub1/')
self.assertEqual(pg.abs_url, None)
self.assertEqual(pg.canonical_url, None)
self.assertEqual(pg.edit_url, None)
self.assertEqual(pg.file, fl)
self.assertEqual(pg.content, None)
self.assertFalse(pg.is_homepage)
self.assertTrue(pg.is_index)
self.assertTrue(pg.is_page)
self.assertFalse(pg.is_section)
self.assertTrue(pg.is_top_level)
self.assertEqual(pg.markdown, None)
self.assertEqual(pg.meta, {})
self.assertEqual(pg.next_page, None)
self.assertEqual(pg.parent, None)
self.assertEqual(pg.previous_page, None)
self.assertEqual(pg.title, 'Foo')
self.assertEqual(pg.toc, [])
def test_nested_index_page_no_parent_no_directory_urls(self):
cfg = load_config(docs_dir=self.DOCS_DIR, use_directory_urls=False)
fl = File('sub1/index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])
pg = Page('Foo', fl, cfg)
pg.parent = None # non-homepage at nav root level; see #1919.
self.assertEqual(pg.url, 'sub1/index.html')
self.assertEqual(pg.abs_url, None)
self.assertEqual(pg.canonical_url, None)
self.assertEqual(pg.edit_url, None)
self.assertEqual(pg.file, fl)
self.assertEqual(pg.content, None)
self.assertFalse(pg.is_homepage)
self.assertTrue(pg.is_index)
self.assertTrue(pg.is_page)
self.assertFalse(pg.is_section)
self.assertTrue(pg.is_top_level)
self.assertEqual(pg.markdown, None)
self.assertEqual(pg.meta, {})
self.assertEqual(pg.next_page, None)
self.assertEqual(pg.parent, None)
self.assertEqual(pg.previous_page, None)
self.assertEqual(pg.title, 'Foo')
self.assertEqual(pg.toc, [])
def test_nested_nonindex_page(self):
cfg = load_config(docs_dir=self.DOCS_DIR)
fl = File('sub1/non-index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])
pg = Page('Foo', fl, cfg)
pg.parent = 'foo'
self.assertEqual(pg.url, 'sub1/non-index/')
self.assertEqual(pg.abs_url, None)
self.assertEqual(pg.canonical_url, None)
self.assertEqual(pg.edit_url, None)
self.assertEqual(pg.file, fl)
self.assertEqual(pg.content, None)
self.assertFalse(pg.is_homepage)
self.assertFalse(pg.is_index)
self.assertTrue(pg.is_page)
self.assertFalse(pg.is_section)
self.assertFalse(pg.is_top_level)
self.assertEqual(pg.markdown, None)
self.assertEqual(pg.meta, {})
self.assertEqual(pg.next_page, None)
self.assertEqual(pg.parent, 'foo')
self.assertEqual(pg.previous_page, None)
self.assertEqual(pg.title, 'Foo')
self.assertEqual(pg.toc, [])
def test_page_defaults(self):
cfg = load_config()
fl = File('testing.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])
pg = Page('Foo', fl, cfg)
self.assertRegex(pg.update_date, r'\d{4}-\d{2}-\d{2}')
self.assertEqual(pg.url, 'testing/')
self.assertEqual(pg.abs_url, None)
self.assertEqual(pg.canonical_url, None)
self.assertEqual(pg.edit_url, None)
self.assertEqual(pg.file, fl)
self.assertEqual(pg.content, None)
self.assertFalse(pg.is_homepage)
self.assertFalse(pg.is_index)
self.assertTrue(pg.is_page)
self.assertFalse(pg.is_section)
self.assertTrue(pg.is_top_level)
self.assertEqual(pg.markdown, None)
self.assertEqual(pg.meta, {})
self.assertEqual(pg.next_page, None)
self.assertEqual(pg.parent, None)
self.assertEqual(pg.previous_page, None)
self.assertEqual(pg.title, 'Foo')
self.assertEqual(pg.toc, [])
def test_page_no_directory_url(self):
cfg = load_config(use_directory_urls=False)
fl = File('testing.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])
pg = Page('Foo', fl, cfg)
self.assertEqual(pg.url, 'testing.html')
self.assertEqual(pg.abs_url, None)
self.assertEqual(pg.canonical_url, None)
self.assertEqual(pg.edit_url, None)
self.assertEqual(pg.file, fl)
self.assertEqual(pg.content, None)
self.assertFalse(pg.is_homepage)
self.assertFalse(pg.is_index)
self.assertTrue(pg.is_page)
self.assertFalse(pg.is_section)
self.assertTrue(pg.is_top_level)
self.assertEqual(pg.markdown, None)
self.assertEqual(pg.meta, {})
self.assertEqual(pg.next_page, None)
self.assertEqual(pg.parent, None)
self.assertEqual(pg.previous_page, None)
self.assertEqual(pg.title, 'Foo')
self.assertEqual(pg.toc, [])
def test_page_canonical_url(self):
cfg = load_config(site_url='http://example.com')
fl = File('testing.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])
pg = Page('Foo', fl, cfg)
self.assertEqual(pg.url, 'testing/')
self.assertEqual(pg.abs_url, '/testing/')
self.assertEqual(pg.canonical_url, 'http://example.com/testing/')
self.assertEqual(pg.edit_url, None)
self.assertEqual(pg.file, fl)
self.assertEqual(pg.content, None)
self.assertFalse(pg.is_homepage)
self.assertFalse(pg.is_index)
self.assertTrue(pg.is_page)
self.assertFalse(pg.is_section)
self.assertTrue(pg.is_top_level)
self.assertEqual(pg.markdown, None)
self.assertEqual(pg.meta, {})
self.assertEqual(pg.next_page, None)
self.assertEqual(pg.parent, None)
self.assertEqual(pg.previous_page, None)
self.assertEqual(pg.title, 'Foo')
self.assertEqual(pg.toc, [])
def test_page_canonical_url_nested(self):
cfg = load_config(site_url='http://example.com/foo/')
fl = File('testing.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])
pg = Page('Foo', fl, cfg)
self.assertEqual(pg.url, 'testing/')
self.assertEqual(pg.abs_url, '/foo/testing/')
self.assertEqual(pg.canonical_url, 'http://example.com/foo/testing/')
self.assertEqual(pg.edit_url, None)
self.assertEqual(pg.file, fl)
self.assertEqual(pg.content, None)
self.assertFalse(pg.is_homepage)
self.assertFalse(pg.is_index)
self.assertTrue(pg.is_page)
self.assertFalse(pg.is_section)
self.assertTrue(pg.is_top_level)
self.assertEqual(pg.markdown, None)
self.assertEqual(pg.meta, {})
self.assertEqual(pg.next_page, None)
self.assertEqual(pg.parent, None)
self.assertEqual(pg.previous_page, None)
self.assertEqual(pg.title, 'Foo')
self.assertEqual(pg.toc, [])
def test_page_canonical_url_nested_no_slash(self):
cfg = load_config(site_url='http://example.com/foo')
fl = File('testing.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])
pg = Page('Foo', fl, cfg)
self.assertEqual(pg.url, 'testing/')
self.assertEqual(pg.abs_url, '/foo/testing/')
self.assertEqual(pg.canonical_url, 'http://example.com/foo/testing/')
self.assertEqual(pg.edit_url, None)
self.assertEqual(pg.file, fl)
self.assertEqual(pg.content, None)
self.assertFalse(pg.is_homepage)
self.assertFalse(pg.is_index)
self.assertTrue(pg.is_page)
self.assertFalse(pg.is_section)
self.assertTrue(pg.is_top_level)
self.assertEqual(pg.markdown, None)
self.assertEqual(pg.meta, {})
self.assertEqual(pg.next_page, None)
self.assertEqual(pg.parent, None)
self.assertEqual(pg.previous_page, None)
self.assertEqual(pg.title, 'Foo')
self.assertEqual(pg.toc, [])
def test_predefined_page_title(self):
cfg = load_config()
fl = File('testing.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])
pg = Page('Page Title', fl, cfg)
pg.read_source(cfg)
self.assertEqual(pg.url, 'testing/')
self.assertEqual(pg.abs_url, None)
self.assertEqual(pg.canonical_url, None)
self.assertEqual(pg.edit_url, None)
self.assertEqual(pg.file, fl)
self.assertEqual(pg.content, None)
self.assertFalse(pg.is_homepage)
self.assertFalse(pg.is_index)
self.assertTrue(pg.is_page)
self.assertFalse(pg.is_section)
self.assertTrue(pg.is_top_level)
self.assertTrue(pg.markdown.startswith('# Welcome to MkDocs\n'))
self.assertEqual(pg.meta, {})
self.assertEqual(pg.next_page, None)
self.assertEqual(pg.parent, None)
self.assertEqual(pg.previous_page, None)
self.assertEqual(pg.title, 'Page Title')
self.assertEqual(pg.toc, [])
def test_page_title_from_markdown(self):
cfg = load_config()
fl = File('testing.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])
pg = Page(None, fl, cfg)
pg.read_source(cfg)
self.assertEqual(pg.url, 'testing/')
self.assertEqual(pg.abs_url, None)
self.assertEqual(pg.canonical_url, None)
self.assertEqual(pg.edit_url, None)
self.assertEqual(pg.file, fl)
self.assertEqual(pg.content, None)
self.assertFalse(pg.is_homepage)
self.assertFalse(pg.is_index)
self.assertTrue(pg.is_page)
self.assertFalse(pg.is_section)
self.assertTrue(pg.is_top_level)
self.assertTrue(pg.markdown.startswith('# Welcome to MkDocs\n'))
self.assertEqual(pg.meta, {})
self.assertEqual(pg.next_page, None)
self.assertEqual(pg.parent, None)
self.assertEqual(pg.previous_page, None)
self.assertEqual(pg.title, 'Welcome to MkDocs')
self.assertEqual(pg.toc, [])
def test_page_title_from_meta(self):
cfg = load_config(docs_dir=self.DOCS_DIR)
fl = File('metadata.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])
pg = Page(None, fl, cfg)
pg.read_source(cfg)
self.assertEqual(pg.url, 'metadata/')
self.assertEqual(pg.abs_url, None)
self.assertEqual(pg.canonical_url, None)
self.assertEqual(pg.edit_url, None)
self.assertEqual(pg.file, fl)
self.assertEqual(pg.content, None)
self.assertFalse(pg.is_homepage)
self.assertFalse(pg.is_index)
self.assertTrue(pg.is_page)
self.assertFalse(pg.is_section)
self.assertTrue(pg.is_top_level)
self.assertTrue(pg.markdown.startswith('# Welcome to MkDocs\n'))
self.assertEqual(pg.meta, {'title': 'A Page Title'})
self.assertEqual(pg.next_page, None)
self.assertEqual(pg.parent, None)
self.assertEqual(pg.previous_page, None)
self.assertEqual(pg.title, 'A Page Title')
self.assertEqual(pg.toc, [])
def test_page_title_from_filename(self):
cfg = load_config(docs_dir=self.DOCS_DIR)
fl = File('page-title.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])
pg = Page(None, fl, cfg)
pg.read_source(cfg)
self.assertEqual(pg.url, 'page-title/')
self.assertEqual(pg.abs_url, None)
self.assertEqual(pg.canonical_url, None)
self.assertEqual(pg.edit_url, None)
self.assertEqual(pg.file, fl)
self.assertEqual(pg.content, None)
self.assertFalse(pg.is_homepage)
self.assertFalse(pg.is_index)
self.assertTrue(pg.is_page)
self.assertFalse(pg.is_section)
self.assertTrue(pg.is_top_level)
self.assertTrue(pg.markdown.startswith('Page content.\n'))
self.assertEqual(pg.meta, {})
self.assertEqual(pg.next_page, None)
self.assertEqual(pg.parent, None)
self.assertEqual(pg.previous_page, None)
self.assertEqual(pg.title, 'Page title')
self.assertEqual(pg.toc, [])
def test_page_title_from_capitalized_filename(self):
cfg = load_config(docs_dir=self.DOCS_DIR)
fl = File('pageTitle.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])
pg = Page(None, fl, cfg)
pg.read_source(cfg)
self.assertEqual(pg.url, 'pageTitle/')
self.assertEqual(pg.abs_url, None)
self.assertEqual(pg.canonical_url, None)
self.assertEqual(pg.edit_url, None)
self.assertEqual(pg.file, fl)
self.assertEqual(pg.content, None)
self.assertFalse(pg.is_homepage)
self.assertFalse(pg.is_index)
self.assertTrue(pg.is_page)
self.assertFalse(pg.is_section)
self.assertTrue(pg.is_top_level)
self.assertTrue(pg.markdown.startswith('Page content.\n'))
self.assertEqual(pg.meta, {})
self.assertEqual(pg.next_page, None)
self.assertEqual(pg.parent, None)
self.assertEqual(pg.previous_page, None)
self.assertEqual(pg.title, 'pageTitle')
self.assertEqual(pg.toc, [])
def test_page_title_from_homepage_filename(self):
cfg = load_config(docs_dir=self.DOCS_DIR)
fl = File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])
pg = Page(None, fl, cfg)
pg.read_source(cfg)
self.assertEqual(pg.url, '')
self.assertEqual(pg.abs_url, None)
self.assertEqual(pg.canonical_url, None)
self.assertEqual(pg.edit_url, None)
self.assertEqual(pg.file, fl)
self.assertEqual(pg.content, None)
self.assertTrue(pg.is_homepage)
self.assertTrue(pg.is_index)
self.assertTrue(pg.is_page)
self.assertFalse(pg.is_section)
self.assertTrue(pg.is_top_level)
self.assertTrue(pg.markdown.startswith('## Test'))
self.assertEqual(pg.meta, {})
self.assertEqual(pg.next_page, None)
self.assertEqual(pg.parent, None)
self.assertEqual(pg.previous_page, None)
self.assertEqual(pg.title, 'Home')
self.assertEqual(pg.toc, [])
def test_page_eq(self):
cfg = load_config()
fl = File('testing.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])
pg = Page('Foo', fl, cfg)
self.assertTrue(pg == Page('Foo', fl, cfg))
def test_page_ne(self):
cfg = load_config()
f1 = File('testing.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])
f2 = File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])
pg = Page('Foo', f1, cfg)
# Different Title
self.assertTrue(pg != Page('Bar', f1, cfg))
# Different File
self.assertTrue(pg != Page('Foo', f2, cfg))
@tempdir()
def test_BOM(self, docs_dir):
md_src = '# An UTF-8 encoded file with a BOM'
cfg = load_config(docs_dir=docs_dir)
fl = File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])
pg = Page(None, fl, cfg)
# Create an UTF-8 Encoded file with BOM (as Microsoft editors do). See #1186
with open(fl.abs_src_path, 'w', encoding='utf-8-sig') as f:
f.write(md_src)
# Now read the file.
pg.read_source(cfg)
# Ensure the BOM (`\ufeff`) is removed
self.assertNotIn('\ufeff', pg.markdown)
self.assertEqual(pg.markdown, md_src)
self.assertEqual(pg.meta, {})
def test_page_edit_url(
self, paths={'testing.md': 'testing/', 'sub1/non-index.md': 'sub1/non-index/'}
):
for case in [
dict(
config={'repo_url': 'http://github.com/mkdocs/mkdocs'},
edit_url='http://github.com/mkdocs/mkdocs/edit/master/docs/testing.md',
edit_url2='http://github.com/mkdocs/mkdocs/edit/master/docs/sub1/non-index.md',
),
dict(
config={'repo_url': 'https://github.com/mkdocs/mkdocs/'},
edit_url='https://github.com/mkdocs/mkdocs/edit/master/docs/testing.md',
edit_url2='https://github.com/mkdocs/mkdocs/edit/master/docs/sub1/non-index.md',
),
dict(
config={'repo_url': 'http://example.com'},
edit_url=None,
edit_url2=None,
),
dict(
config={'repo_url': 'http://example.com', 'edit_uri': 'edit/master'},
edit_url='http://example.com/edit/master/testing.md',
edit_url2='http://example.com/edit/master/sub1/non-index.md',
),
dict(
config={'repo_url': 'http://example.com', 'edit_uri': '/edit/master'},
edit_url='http://example.com/edit/master/testing.md',
edit_url2='http://example.com/edit/master/sub1/non-index.md',
),
dict(
config={'repo_url': 'http://example.com/foo/', 'edit_uri': '/edit/master/'},
edit_url='http://example.com/edit/master/testing.md',
edit_url2='http://example.com/edit/master/sub1/non-index.md',
),
dict(
config={'repo_url': 'http://example.com/foo', 'edit_uri': '/edit/master/'},
edit_url='http://example.com/edit/master/testing.md',
edit_url2='http://example.com/edit/master/sub1/non-index.md',
),
dict(
config={'repo_url': 'http://example.com/foo/', 'edit_uri': '/edit/master'},
edit_url='http://example.com/edit/master/testing.md',
edit_url2='http://example.com/edit/master/sub1/non-index.md',
),
dict(
config={'repo_url': 'http://example.com/foo/', 'edit_uri': 'edit/master/'},
edit_url='http://example.com/foo/edit/master/testing.md',
edit_url2='http://example.com/foo/edit/master/sub1/non-index.md',
),
dict(
config={'repo_url': 'http://example.com/foo', 'edit_uri': 'edit/master/'},
edit_url='http://example.com/foo/edit/master/testing.md',
edit_url2='http://example.com/foo/edit/master/sub1/non-index.md',
),
dict(
config={'repo_url': 'http://example.com', 'edit_uri': '?query=edit/master'},
edit_url='http://example.com?query=edit/master/testing.md',
edit_url2='http://example.com?query=edit/master/sub1/non-index.md',
),
dict(
config={'repo_url': 'http://example.com/', 'edit_uri': '?query=edit/master/'},
edit_url='http://example.com/?query=edit/master/testing.md',
edit_url2='http://example.com/?query=edit/master/sub1/non-index.md',
),
dict(
config={'repo_url': 'http://example.com', 'edit_uri': '#edit/master'},
edit_url='http://example.com#edit/master/testing.md',
edit_url2='http://example.com#edit/master/sub1/non-index.md',
),
dict(
config={'repo_url': 'http://example.com/', 'edit_uri': '#edit/master/'},
edit_url='http://example.com/#edit/master/testing.md',
edit_url2='http://example.com/#edit/master/sub1/non-index.md',
),
dict(
config={'edit_uri': 'http://example.com/edit/master'},
edit_url='http://example.com/edit/master/testing.md',
edit_url2='http://example.com/edit/master/sub1/non-index.md',
),
dict(
config={'edit_uri_template': 'https://github.com/project/repo/wiki/{path_noext}'},
edit_url='https://github.com/project/repo/wiki/testing',
edit_url2='https://github.com/project/repo/wiki/sub1/non-index',
),
dict(
config={
'repo_url': 'https://github.com/project/repo/wiki',
'edit_uri_template': '{path_noext}/_edit',
},
edit_url='https://github.com/project/repo/wiki/testing/_edit',
edit_url2='https://github.com/project/repo/wiki/sub1/non-index/_edit',
),
dict(
config={
'repo_url': 'https://gitlab.com/project/repo',
'edit_uri_template': '-/sse/master/docs%2F{path!q}',
},
edit_url='https://gitlab.com/project/repo/-/sse/master/docs%2Ftesting.md',
edit_url2='https://gitlab.com/project/repo/-/sse/master/docs%2Fsub1%2Fnon-index.md',
),
dict(
config={
'repo_url': 'https://bitbucket.org/project/repo/',
'edit_uri_template': 'src/master/docs/{path}?mode=edit',
},
edit_url='https://bitbucket.org/project/repo/src/master/docs/testing.md?mode=edit',
edit_url2='https://bitbucket.org/project/repo/src/master/docs/sub1/non-index.md?mode=edit',
),
dict(
config={
'repo_url': 'http://example.com',
'edit_uri': '',
'edit_uri_template': '',
}, # Set to blank value
edit_url=None,
edit_url2=None,
),
dict(config={}, edit_url=None, edit_url2=None), # Nothing defined
]:
for i, path in enumerate(paths, 1):
edit_url_key = f'edit_url{i}' if i > 1 else 'edit_url'
with self.subTest(case['config'], path=path):
cfg = load_config(**case['config'])
fl = File(path, cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])
pg = Page('Foo', fl, cfg)
self.assertEqual(pg.url, paths[path])
self.assertEqual(pg.edit_url, case[edit_url_key])
@unittest.skipUnless(sys.platform.startswith("win"), "requires Windows")
def test_page_edit_url_windows(self):
self.test_page_edit_url(
paths={'testing.md': 'testing/', 'sub1\\non-index.md': 'sub1/non-index/'}
)
def test_page_edit_url_warning(self):
for case in [
dict(
config={'edit_uri': 'edit/master'},
edit_url='edit/master/testing.md',
warning="WARNING:mkdocs.structure.pages:edit_uri: "
"'edit/master/testing.md' is not a valid URL, it should include the http:// (scheme)",
),
]:
with self.subTest(case['config']):
with self.assertLogs('mkdocs') as cm:
cfg = load_config(**case['config'])
fl = File(
'testing.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']
)
pg = Page('Foo', fl, cfg)
self.assertEqual(pg.url, 'testing/')
self.assertEqual(pg.edit_url, case['edit_url'])
self.assertEqual(cm.output, [case['warning']])
def test_page_render(self):
cfg = load_config()
fl = File('testing.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])
pg = Page('Foo', fl, cfg)
pg.read_source(cfg)
self.assertEqual(pg.content, None)
self.assertEqual(pg.toc, [])
pg.render(cfg, [fl])
self.assertTrue(
pg.content.startswith('<h1 id="welcome-to-mkdocs">Welcome to MkDocs</h1>\n')
)
self.assertEqual(
str(pg.toc).strip(),
dedent(
"""
Welcome to MkDocs - #welcome-to-mkdocs
Commands - #commands
Project layout - #project-layout
"""
),
)
def test_missing_page(self):
cfg = load_config()
fl = File('missing.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])
pg = Page('Foo', fl, cfg)
with self.assertLogs('mkdocs') as cm:
with self.assertRaises(OSError):
pg.read_source(cfg)
self.assertEqual(
'\n'.join(cm.output), 'ERROR:mkdocs.structure.pages:File not found: missing.md'
)
class SourceDateEpochTests(unittest.TestCase):
def setUp(self):
self.default = os.environ.get('SOURCE_DATE_EPOCH', None)
os.environ['SOURCE_DATE_EPOCH'] = '0'
def test_source_date_epoch(self):
cfg = load_config()
fl = File('testing.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])
pg = Page('Foo', fl, cfg)
self.assertEqual(pg.update_date, '1970-01-01')
def tearDown(self):
if self.default is not None:
os.environ['SOURCE_DATE_EPOCH'] = self.default
else:
del os.environ['SOURCE_DATE_EPOCH']
class RelativePathExtensionTests(unittest.TestCase):
DOCS_DIR = os.path.join(
os.path.abspath(os.path.dirname(__file__)), '../integration/subpages/docs'
)
def get_rendered_result(self, files):
cfg = load_config(docs_dir=self.DOCS_DIR)
fs = [File(f, cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) for f in files]
pg = Page('Foo', fs[0], cfg)
pg.read_source(cfg)
pg.render(cfg, Files(fs))
return pg.content
@mock.patch('mkdocs.structure.pages.open', mock.mock_open(read_data='[link](non-index.md)'))
def test_relative_html_link(self):
self.assertEqual(
self.get_rendered_result(['index.md', 'non-index.md']),
'<p><a href="non-index/">link</a></p>', # No trailing /
)
@mock.patch('mkdocs.structure.pages.open', mock.mock_open(read_data='[link](index.md)'))
def test_relative_html_link_index(self):
self.assertEqual(
self.get_rendered_result(['non-index.md', 'index.md']),
'<p><a href="../">link</a></p>',
)
@mock.patch('mkdocs.structure.pages.open', mock.mock_open(read_data='[link](sub2/index.md)'))
def test_relative_html_link_sub_index(self):
self.assertEqual(
self.get_rendered_result(['index.md', 'sub2/index.md']),
'<p><a href="sub2/">link</a></p>', # No trailing /
)
@mock.patch(
'mkdocs.structure.pages.open', mock.mock_open(read_data='[link](sub2/non-index.md)')
)
def test_relative_html_link_sub_page(self):
self.assertEqual(
self.get_rendered_result(['index.md', 'sub2/non-index.md']),
'<p><a href="sub2/non-index/">link</a></p>', # No trailing /
)
@mock.patch('mkdocs.structure.pages.open', mock.mock_open(read_data='[link](file%20name.md)'))
def test_relative_html_link_with_encoded_space(self):
self.assertEqual(
self.get_rendered_result(['index.md', 'file name.md']),
'<p><a href="file%20name/">link</a></p>',
)
@mock.patch('mkdocs.structure.pages.open', mock.mock_open(read_data='[link](file name.md)'))
def test_relative_html_link_with_unencoded_space(self):
self.assertEqual(
self.get_rendered_result(['index.md', 'file name.md']),
'<p><a href="file%20name/">link</a></p>',
)
@mock.patch('mkdocs.structure.pages.open', mock.mock_open(read_data='[link](../index.md)'))
def test_relative_html_link_parent_index(self):
self.assertEqual(
self.get_rendered_result(['sub2/non-index.md', 'index.md']),
'<p><a href="../../">link</a></p>',
)
@mock.patch(
'mkdocs.structure.pages.open', mock.mock_open(read_data='[link](non-index.md#hash)')
)
def test_relative_html_link_hash(self):
self.assertEqual(
self.get_rendered_result(['index.md', 'non-index.md']),
'<p><a href="non-index/#hash">link</a></p>',
)
@mock.patch(
'mkdocs.structure.pages.open', mock.mock_open(read_data='[link](sub2/index.md#hash)')
)
def test_relative_html_link_sub_index_hash(self):
self.assertEqual(
self.get_rendered_result(['index.md', 'sub2/index.md']),
'<p><a href="sub2/#hash">link</a></p>',
)
@mock.patch(
'mkdocs.structure.pages.open', mock.mock_open(read_data='[link](sub2/non-index.md#hash)')
)
def test_relative_html_link_sub_page_hash(self):
self.assertEqual(
self.get_rendered_result(['index.md', 'sub2/non-index.md']),
'<p><a href="sub2/non-index/#hash">link</a></p>',
)
@mock.patch('mkdocs.structure.pages.open', mock.mock_open(read_data='[link](#hash)'))
def test_relative_html_link_hash_only(self):
self.assertEqual(
self.get_rendered_result(['index.md']),
'<p><a href="#hash">link</a></p>',
)
@mock.patch('mkdocs.structure.pages.open', mock.mock_open(read_data='![image](image.png)'))
def test_relative_image_link_from_homepage(self):
self.assertEqual(
self.get_rendered_result(['index.md', 'image.png']),
'<p><img alt="image" src="image.png" /></p>', # no opening ./
)
@mock.patch('mkdocs.structure.pages.open', mock.mock_open(read_data='![image](../image.png)'))
def test_relative_image_link_from_subpage(self):
self.assertEqual(
self.get_rendered_result(['sub2/non-index.md', 'image.png']),
'<p><img alt="image" src="../../image.png" /></p>',
)
@mock.patch('mkdocs.structure.pages.open', mock.mock_open(read_data='![image](image.png)'))
def test_relative_image_link_from_sibling(self):
self.assertEqual(
self.get_rendered_result(['non-index.md', 'image.png']),
'<p><img alt="image" src="../image.png" /></p>',
)
@mock.patch('mkdocs.structure.pages.open', mock.mock_open(read_data='*__not__ a link*.'))
def test_no_links(self):
self.assertEqual(
self.get_rendered_result(['index.md']),
'<p><em><strong>not</strong> a link</em>.</p>',
)
@mock.patch('mkdocs.structure.pages.open', mock.mock_open(read_data='[link](non-existent.md)'))
def test_bad_relative_html_link(self):
with self.assertLogs('mkdocs') as cm:
self.assertEqual(
self.get_rendered_result(['index.md']),
'<p><a href="non-existent.md">link</a></p>',
)
self.assertEqual(
'\n'.join(cm.output),
"WARNING:mkdocs.structure.pages:Documentation file 'index.md' contains a link "
"to 'non-existent.md' which is not found in the documentation files.",
)
@mock.patch(
'mkdocs.structure.pages.open',
mock.mock_open(read_data='[external](http://example.com/index.md)'),
)
def test_external_link(self):
self.assertEqual(
self.get_rendered_result(['index.md']),
'<p><a href="http://example.com/index.md">external</a></p>',
)
@mock.patch(
'mkdocs.structure.pages.open', mock.mock_open(read_data='[absolute link](/path/to/file.md)')
)
def test_absolute_link(self):
self.assertEqual(
self.get_rendered_result(['index.md']),
'<p><a href="/path/to/file.md">absolute link</a></p>',
)
@mock.patch(
'mkdocs.structure.pages.open',
mock.mock_open(read_data='[absolute local path](\\image.png)'),
)
def test_absolute_win_local_path(self):
self.assertEqual(
self.get_rendered_result(['index.md']),
'<p><a href="\\image.png">absolute local path</a></p>',
)
@mock.patch('mkdocs.structure.pages.open', mock.mock_open(read_data='<mail@example.com>'))
def test_email_link(self):
self.assertEqual(
self.get_rendered_result(['index.md']),
# Markdown's default behavior is to obscure email addresses by entity-encoding them.
# The following is equivalent to: '<p><a href="mailto:mail@example.com">mail@example.com</a></p>'
'<p><a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#109;&#97;&#105;&#108;&#64;&#101;'
'&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;">&#109;&#97;&#105;&#108;&#64;'
'&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;</a></p>',
)

View File

@ -0,0 +1,198 @@
#!/usr/bin/env python
import unittest
from mkdocs.structure.toc import get_toc
from mkdocs.tests.base import dedent, get_markdown_toc
class TableOfContentsTests(unittest.TestCase):
def test_indented_toc(self):
md = dedent(
"""
# Heading 1
## Heading 2
### Heading 3
"""
)
expected = dedent(
"""
Heading 1 - #heading-1
Heading 2 - #heading-2
Heading 3 - #heading-3
"""
)
toc = get_toc(get_markdown_toc(md))
self.assertEqual(str(toc).strip(), expected)
self.assertEqual(len(toc), 1)
def test_indented_toc_html(self):
md = dedent(
"""
# Heading 1
## <code>Heading</code> 2
## Heading 3
"""
)
expected = dedent(
"""
Heading 1 - #heading-1
Heading 2 - #heading-2
Heading 3 - #heading-3
"""
)
toc = get_toc(get_markdown_toc(md))
self.assertEqual(str(toc).strip(), expected)
self.assertEqual(len(toc), 1)
def test_flat_toc(self):
md = dedent(
"""
# Heading 1
# Heading 2
# Heading 3
"""
)
expected = dedent(
"""
Heading 1 - #heading-1
Heading 2 - #heading-2
Heading 3 - #heading-3
"""
)
toc = get_toc(get_markdown_toc(md))
self.assertEqual(str(toc).strip(), expected)
self.assertEqual(len(toc), 3)
def test_flat_h2_toc(self):
md = dedent(
"""
## Heading 1
## Heading 2
## Heading 3
"""
)
expected = dedent(
"""
Heading 1 - #heading-1
Heading 2 - #heading-2
Heading 3 - #heading-3
"""
)
toc = get_toc(get_markdown_toc(md))
self.assertEqual(str(toc).strip(), expected)
self.assertEqual(len(toc), 3)
def test_mixed_toc(self):
md = dedent(
"""
# Heading 1
## Heading 2
# Heading 3
### Heading 4
### Heading 5
"""
)
expected = dedent(
"""
Heading 1 - #heading-1
Heading 2 - #heading-2
Heading 3 - #heading-3
Heading 4 - #heading-4
Heading 5 - #heading-5
"""
)
toc = get_toc(get_markdown_toc(md))
self.assertEqual(str(toc).strip(), expected)
self.assertEqual(len(toc), 2)
def test_mixed_html(self):
md = dedent(
"""
# Heading 1
## Heading 2
# Heading 3
### Heading 4
### <a>Heading 5</a>
"""
)
expected = dedent(
"""
Heading 1 - #heading-1
Heading 2 - #heading-2
Heading 3 - #heading-3
Heading 4 - #heading-4
Heading 5 - #heading-5
"""
)
toc = get_toc(get_markdown_toc(md))
self.assertEqual(str(toc).strip(), expected)
self.assertEqual(len(toc), 2)
def test_nested_anchor(self):
md = dedent(
"""
# Heading 1
## Heading 2
# Heading 3
### Heading 4
### <a href="/">Heading 5</a>
"""
)
expected = dedent(
"""
Heading 1 - #heading-1
Heading 2 - #heading-2
Heading 3 - #heading-3
Heading 4 - #heading-4
Heading 5 - #heading-5
"""
)
toc = get_toc(get_markdown_toc(md))
self.assertEqual(str(toc).strip(), expected)
self.assertEqual(len(toc), 2)
def test_entityref(self):
md = dedent(
"""
# Heading & 1
## Heading > 2
### Heading < 3
"""
)
expected = dedent(
"""
Heading &amp; 1 - #heading-1
Heading &gt; 2 - #heading-2
Heading &lt; 3 - #heading-3
"""
)
toc = get_toc(get_markdown_toc(md))
self.assertEqual(str(toc).strip(), expected)
self.assertEqual(len(toc), 1)
def test_charref(self):
md = '# &#64;Header'
expected = '&#64;Header - #header'
toc = get_toc(get_markdown_toc(md))
self.assertEqual(str(toc).strip(), expected)
self.assertEqual(len(toc), 1)
def test_level(self):
md = dedent(
"""
# Heading 1
## Heading 1.1
### Heading 1.1.1
### Heading 1.1.2
## Heading 1.2
"""
)
toc = get_toc(get_markdown_toc(md))
def get_level_sequence(items):
for item in items:
yield item.level
yield from get_level_sequence(item.children)
self.assertEqual(tuple(get_level_sequence(toc)), (1, 2, 3, 3, 2))