def test_create_media_urls_use_directory_urls(self): expected_results = { 'https://media.cdn.org/jq.js': [ 'https://media.cdn.org/jq.js', 'https://media.cdn.org/jq.js', 'https://media.cdn.org/jq.js' ], 'http://media.cdn.org/jquery.js': [ 'http://media.cdn.org/jquery.js', 'http://media.cdn.org/jquery.js', 'http://media.cdn.org/jquery.js' ], '//media.cdn.org/jquery.js': [ '//media.cdn.org/jquery.js', '//media.cdn.org/jquery.js', '//media.cdn.org/jquery.js' ], 'media.cdn.org/jquery.js': [ 'media.cdn.org/jquery.js', '../media.cdn.org/jquery.js', '../../media.cdn.org/jquery.js' ], 'local/file/jquery.js': [ 'local/file/jquery.js', '../local/file/jquery.js', '../../local/file/jquery.js' ], 'local\\windows\\file\\jquery.js': [ 'local/windows/file/jquery.js', '../local/windows/file/jquery.js', '../../local/windows/file/jquery.js' ], 'image.png': ['image.png', '../image.png', '../../image.png'], 'style.css?v=20180308c': [ 'style.css?v=20180308c', '../style.css?v=20180308c', '../../style.css?v=20180308c' ], '#some_id': ['#some_id', '#some_id', '#some_id'] } cfg = load_config(use_directory_urls=True) pages = [ Page( 'Home', File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), cfg), Page( 'About', File('about.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), cfg), Page( 'FooBar', File('foo/bar.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), cfg) ] for i, page in enumerate(pages): urls = utils.create_media_urls(expected_results.keys(), page) self.assertEqual([v[i] for v in expected_results.values()], urls)
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))
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.source, 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_populate_page_read_error(self, docs_dir, mock_open): cfg = load_config(docs_dir=docs_dir) file = File( 'missing.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'], ) page = Page('Foo', file, cfg) with self.assertLogs('mkdocs', level='ERROR') as cm: self.assertRaises( OSError, build._populate_page, page, cfg, Files([file]), ) self.assertEqual( cm.output, [ 'ERROR:mkdocs.structure.pages:File not found: missing.md', "ERROR:mkdocs.commands.build:Error reading page 'missing.md': Error message.", ], ) self.assert_mock_called_once(mock_open)
def _data_to_navigation(data, files, config): if isinstance(data, dict): return [ _data_to_navigation( (key, value), files, config) if isinstance(value, str) else Section( title=key, children=_data_to_navigation( value, files, config, ), ) for key, value in data.items() ] elif isinstance(data, list): return [ _data_to_navigation(item, files, config)[0] if isinstance(item, dict) and len(item) == 1 else _data_to_navigation( item, files, config) for item in data ] title, path = data if isinstance(data, tuple) else (None, data) file = files.get_file_from_path(path) if file: return Page(title, file, config) return Link(title, path)
def test_populate_page(self, docs_dir): cfg = load_config(docs_dir=docs_dir) file = File( 'index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'], ) page = Page('Foo', file, cfg) build._populate_page(page, cfg, Files([file])) self.assertEqual(page.content, '<p>page content</p>')
def get_rendered_result(self, files): cfg = load_config(docs_dir=self.DOCS_DIR) fs = [] for f in files: fs.append( File(f.replace('/', os.sep), cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])) pg = Page('Foo', fs[0], cfg) pg.read_source(cfg) pg.render(cfg, Files(fs)) return pg.content
def test_populate_page_dirty_not_modified(self, site_dir, docs_dir): cfg = load_config(docs_dir=docs_dir, site_dir=site_dir) file = File( 'index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'], ) page = Page('Foo', file, cfg) build._populate_page(page, cfg, Files([file]), dirty=True) # Content is empty as file read was skipped self.assertEqual(page.source, None) self.assertEqual(page.content, None)
def test_populate_page_dirty_modified(self, site_dir): cfg = load_config(site_dir=site_dir) file = File( 'testing.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'], ) page = Page('Foo', file, cfg) build._populate_page(page, cfg, Files([file]), dirty=True) self.assertTrue(page.source.startswith('# Welcome to MkDocs')) self.assertTrue( page.content.startswith( '<h1 id="welcome-to-mkdocs">Welcome to MkDocs</h1>', ), )
def get_navigation(files, config): """ Build site navigation from config and files.""" nav_config = config['nav'] or nest_paths( f.src_path for f in files.documentation_pages()) items = _data_to_navigation(nav_config, files, config) if not isinstance(items, list): items = [items] # Get only the pages from the navigation, ignoring any sections and links. pages = _get_by_type(items, Page) # Include next, previous and parent links. _add_previous_and_next_links(pages) _add_parent_links(items) missing_from_config = [ file for file in files.documentation_pages() if file.page is None ] if missing_from_config: log.info( 'The following pages exist in the docs directory, but are not ' 'included in the "nav" configuration:\n - {}'.format( '\n - '.join([file.src_path for file in missing_from_config]), ), ) # Any documentation files not found in the nav should still have an associated page, so we # create them here. The Page object will automatically be assigned to `file.page` during # its creation (and this is the only way in which these page objects are accessable). for file in missing_from_config: Page(None, file, config) links = _get_by_type(items, Link) for link in links: scheme, netloc, path, params, query, fragment = urlparse(link.url) if scheme or netloc: log.debug( "An external link to '{}' is included in " "the 'nav' configuration.".format(link.url), ) elif link.url.startswith('/'): log.debug( "An absolute path to '{}' is included in the 'nav' configuration, " "which presumably points to an external resource.".format( link.url, ), ) else: msg = ( "A relative path to '{}' is included in the 'nav' configuration, " "which is not found in the documentation files".format( link.url, )) log.warning(msg) return Navigation(items, pages)
def build_page(title, path, config, md_src=''): """ Helper which returns a Page object. """ files = Files([ File( path, config['docs_dir'], config['site_dir'], config['use_directory_urls'], ), ]) page = Page(title, list(files)[0], config) # Fake page.read_source() page.source, page.meta = meta.get_data(md_src) return page, files
def test_BOM(self): md_src = '# An UTF-8 encoded file with a BOM' with TemporaryDirectory() as docs_dir: # We don't use mkdocs.tests.base.tempdir decorator here due to uniqueness of this test. 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 Micorsoft 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.source) self.assertEqual(pg.source, md_src) self.assertEqual(pg.meta, {})
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_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.source, 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.source, 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.source, 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_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.source.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_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.source.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_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_create_search_index(self): html_content = """ <h1 id="heading-1">Heading 1</h1> <p>Content 1</p> <h2 id="heading-2">Heading 2</h1> <p>Content 2</p> <h3 id="heading-3">Heading 3</h1> <p>Content 3</p> """ cfg = load_config() pages = [ Page( 'Home', File( 'index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'], ), cfg, ), Page( 'About', File( 'about.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'], ), cfg, ), ] md = dedent(""" # Heading 1 ## Heading 2 ### Heading 3 """) toc = get_toc(get_markdown_toc(md)) full_content = ''.join("""Heading{0}Content{0}""".format(i) for i in range(1, 4)) for page in pages: # Fake page.read_source() and page.render() page.source = md page.toc = toc page.content = html_content index = search_index.SearchIndex() index.add_entry_from_context(page) self.assertEqual(len(index._entries), 4) loc = page.url self.assertEqual(index._entries[0]['title'], page.title) self.assertEqual( strip_whitespace(index._entries[0]['text'], ), full_content, ) self.assertEqual(index._entries[0]['location'], loc) self.assertEqual(index._entries[1]['title'], "Heading 1") self.assertEqual(index._entries[1]['text'], "Content 1") self.assertEqual( index._entries[1]['location'], "{}#heading-1".format(loc), ) self.assertEqual(index._entries[2]['title'], "Heading 2") self.assertEqual( strip_whitespace(index._entries[2]['text'], ), "Content2", ) self.assertEqual( index._entries[2]['location'], "{}#heading-2".format(loc), ) self.assertEqual(index._entries[3]['title'], "Heading 3") self.assertEqual( strip_whitespace(index._entries[3]['text'], ), "Content3", ) self.assertEqual( index._entries[3]['location'], "{}#heading-3".format(loc), )
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 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) self.assertRaises(OSError, pg.read_source, cfg)
def test_nested_page_edit_url_windows(self): configs = [{ 'repo_url': 'http://github.com/mkdocs/mkdocs' }, { 'repo_url': 'https://github.com/mkdocs/mkdocs/' }, { 'repo_url': 'http://example.com' }, { 'repo_url': 'http://example.com', 'edit_uri': 'edit/master' }, { 'repo_url': 'http://example.com', 'edit_uri': '/edit/master' }, { 'repo_url': 'http://example.com/foo/', 'edit_uri': '/edit/master/' }, { 'repo_url': 'http://example.com/foo', 'edit_uri': '/edit/master/' }, { 'repo_url': 'http://example.com/foo/', 'edit_uri': '/edit/master' }, { 'repo_url': 'http://example.com/foo/', 'edit_uri': 'edit/master/' }, { 'repo_url': 'http://example.com/foo', 'edit_uri': 'edit/master/' }, { 'repo_url': 'http://example.com', 'edit_uri': '?query=edit/master' }, { 'repo_url': 'http://example.com/', 'edit_uri': '?query=edit/master/' }, { 'repo_url': 'http://example.com', 'edit_uri': '#edit/master' }, { 'repo_url': 'http://example.com/', 'edit_uri': '#edit/master/' }] expected = [ 'http://github.com/mkdocs/mkdocs/edit/master/docs/sub1/non-index.md', 'https://github.com/mkdocs/mkdocs/edit/master/docs/sub1/non-index.md', None, 'http://example.com/edit/master/sub1/non-index.md', 'http://example.com/edit/master/sub1/non-index.md', 'http://example.com/edit/master/sub1/non-index.md', 'http://example.com/edit/master/sub1/non-index.md', 'http://example.com/edit/master/sub1/non-index.md', 'http://example.com/foo/edit/master/sub1/non-index.md', 'http://example.com/foo/edit/master/sub1/non-index.md', 'http://example.com?query=edit/master/sub1/non-index.md', 'http://example.com/?query=edit/master/sub1/non-index.md', 'http://example.com#edit/master/sub1/non-index.md', 'http://example.com/#edit/master/sub1/non-index.md' ] for i, c in enumerate(configs): c['docs_dir'] = self.DOCS_DIR cfg = load_config(**c) fl = File('sub1\\non-index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) pg = Page('Foo', fl, cfg) self.assertEqual(pg.url, 'sub1/non-index/') self.assertEqual(pg.edit_url, expected[i])
def test_page_edit_url(self): configs = [ { 'repo_url': 'http://github.com/mkdocs/mkdocs' }, { 'repo_url': 'https://github.com/mkdocs/mkdocs/' }, { 'repo_url': 'http://example.com' }, { 'repo_url': 'http://example.com', 'edit_uri': 'edit/master' }, { 'repo_url': 'http://example.com', 'edit_uri': '/edit/master' }, { 'repo_url': 'http://example.com/foo/', 'edit_uri': '/edit/master/' }, { 'repo_url': 'http://example.com/foo', 'edit_uri': '/edit/master/' }, { 'repo_url': 'http://example.com/foo/', 'edit_uri': '/edit/master' }, { 'repo_url': 'http://example.com/foo/', 'edit_uri': 'edit/master/' }, { 'repo_url': 'http://example.com/foo', 'edit_uri': 'edit/master/' }, { 'repo_url': 'http://example.com', 'edit_uri': '?query=edit/master' }, { 'repo_url': 'http://example.com/', 'edit_uri': '?query=edit/master/' }, { 'repo_url': 'http://example.com', 'edit_uri': '#edit/master' }, { 'repo_url': 'http://example.com/', 'edit_uri': '#edit/master/' }, { 'repo_url': 'http://example.com', 'edit_uri': '' # Set to blank value }, { # Nothing defined } ] expected = [ 'http://github.com/mkdocs/mkdocs/edit/master/docs/testing.md', 'https://github.com/mkdocs/mkdocs/edit/master/docs/testing.md', None, 'http://example.com/edit/master/testing.md', 'http://example.com/edit/master/testing.md', 'http://example.com/edit/master/testing.md', 'http://example.com/edit/master/testing.md', 'http://example.com/edit/master/testing.md', 'http://example.com/foo/edit/master/testing.md', 'http://example.com/foo/edit/master/testing.md', 'http://example.com?query=edit/master/testing.md', 'http://example.com/?query=edit/master/testing.md', 'http://example.com#edit/master/testing.md', 'http://example.com/#edit/master/testing.md', None, None ] for i, c in enumerate(configs): cfg = load_config(**c) 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, expected[i])