Files
MeshCore/test/test_hosted_doc_links.py
T

103 lines
5.9 KiB
Python

#!/usr/bin/env python3
"""Verify source banners against MkDocs' actual published URL mapping."""
import importlib.util
from pathlib import Path
import tempfile
import unittest
ROOT = Path(__file__).resolve().parents[1]
SPEC = importlib.util.spec_from_file_location('hosted_doc_links', ROOT / 'scripts/update_hosted_doc_links.py')
LINKS = importlib.util.module_from_spec(SPEC)
SPEC.loader.exec_module(LINKS)
class HostedDocLinksTest(unittest.TestCase):
def test_every_served_document_has_its_current_exact_link(self):
manifest = LINKS.transform(ROOT)
self.assertTrue(manifest['pages'])
pending = [row['source'] for row in manifest['pages'] if not row['already_current']]
self.assertEqual(pending, [],
'Missing or stale hosted links: ' + ', '.join(pending)
+ '; run scripts/update_hosted_doc_links.py --apply')
routes = {row['source']: row['url'] for row in manifest['pages']}
self.assertEqual(routes['index.md'], LINKS.HOSTED_BASE)
self.assertEqual(routes['research/index.md'], LINKS.HOSTED_BASE + 'research/')
self.assertEqual(routes['WiFi.md'], LINKS.HOSTED_BASE + 'WiFi/')
self.assertEqual(routes['releases/1.17.1.9.md'], LINKS.HOSTED_BASE + 'releases/1.17.1.9/')
def test_mkdocs_routes_cover_spaces_case_nested_index_and_non_navigation_pages(self):
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
(root / 'docs/Nested Space').mkdir(parents=True)
(root / 'mkdocs.yml').write_text('site_name: Route fixture\ntheme: {name: mkdocs}\n'
'not_in_nav: |\n /Hidden.md\n'
'exclude_docs: |\n /Excluded.md\n')
for name in ('index.md', 'WiFi.md', 'Hidden.md', 'Nested Space/index.md',
'Nested Space/Mixed Case.md', 'Excluded.md'):
(root / 'docs' / name).write_bytes(('# ' + name + '\n\nBody stays the same.\n').encode())
excluded_before = (root / 'docs/Excluded.md').read_bytes()
manifest = LINKS.transform(root, apply=True)
routes = {row['source']: row['url'] for row in manifest['pages']}
self.assertEqual(routes, {
'index.md': LINKS.HOSTED_BASE,
'WiFi.md': LINKS.HOSTED_BASE + 'WiFi/',
'Hidden.md': LINKS.HOSTED_BASE + 'Hidden/',
'Nested Space/index.md': LINKS.HOSTED_BASE + 'Nested%20Space/',
'Nested Space/Mixed Case.md': LINKS.HOSTED_BASE + 'Nested%20Space/Mixed%20Case/',
})
before = {p: p.read_bytes() for p in (root / 'docs').rglob('*.md')}
self.assertTrue(all(row['already_current'] for row in LINKS.transform(root, apply=True)['pages']))
self.assertEqual(before, {p: p.read_bytes() for p in before})
self.assertEqual((root / 'docs/Excluded.md').read_bytes(), excluded_before)
def test_original_body_frontmatter_bom_and_line_endings_are_preserved(self):
for text in ('# Heading\n\nBody with <b>HTML</b>.\n',
'---\ntitle: Example\n---\n\n# Heading\n',
'\ufeff---\r\ntitle: Example\r\n...\r\n\r\n# Heading\r\n'):
with self.subTest(text=text):
updated = LINKS.with_banner(text, LINKS.HOSTED_BASE + 'Example/')
self.assertEqual(LINKS.document_body(updated), text)
self.assertEqual(LINKS.with_banner(updated, LINKS.HOSTED_BASE + 'Example/'), updated)
if 'title: Example' in text:
self.assertLess(updated.index('title: Example'), updated.index(LINKS.BEGIN))
def test_moved_page_gets_a_new_link_without_changing_its_body(self):
body = '# Example\n\nOriginal text.\n'
old = LINKS.with_banner(body, LINKS.HOSTED_BASE + 'old/')
updated = LINKS.with_banner(old, LINKS.HOSTED_BASE + 'new/')
self.assertNotIn('href="' + LINKS.HOSTED_BASE + 'old/', updated)
self.assertIn('href="' + LINKS.HOSTED_BASE + 'new/', updated)
self.assertEqual(LINKS.document_body(updated), body)
def test_duplicate_misplaced_or_incomplete_banner_is_rejected(self):
banner = LINKS.with_banner('# Heading\n', LINKS.HOSTED_BASE)
for text in (banner + banner, '# Heading\n' + banner, banner.replace(LINKS.END + '\n\n', LINKS.END)):
with self.subTest(text=text):
with self.assertRaises(ValueError):
LINKS.with_banner(text, LINKS.HOSTED_BASE)
def test_shared_script_is_registered_exactly_once(self):
self.assertEqual((ROOT / 'mkdocs.yml').read_text().count(LINKS.SCRIPT), 1)
self.assertTrue((ROOT / 'docs/_javascript/hosted_doc_links.js').is_file())
def test_hooks_preserve_other_tool_config_and_workflow_changes(self):
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
(root / '.github/workflows').mkdir(parents=True)
config = 'site_name: Fixture\nextra_javascript:\n - _javascript/telemetry_decoder.js?v=telemetry-fix2\n'
workflow = ('name: Tests\njobs:\n management-reports:\n steps:\n'
' - run: node scripts/test_telemetry_decoder.js\n')
(root / 'mkdocs.yml').write_text(config)
(root / '.github/workflows/run-unit-tests.yml').write_text(workflow)
LINKS.install_hooks(root)
self.assertEqual((root / 'mkdocs.yml').read_text().replace(LINKS.SCRIPT, ''), config)
self.assertEqual((root / '.github/workflows/run-unit-tests.yml').read_text()
.replace(LINKS.WORKFLOW_JOB, ''), workflow)
before = {p: p.read_bytes() for p in root.rglob('*') if p.is_file()}
LINKS.install_hooks(root)
self.assertEqual(before, {p: p.read_bytes() for p in before})
if __name__ == '__main__':
unittest.main()