""" Unit tests for Phase 4: HTML Rendering Mode components. Tests cover: - SpaceRenderer base class - RenderConfig and ThemeConfig - MarkdownToHTMLRenderer - SpaceRenderingService - Cache integration """ import pytest from unittest.mock import Mock, MagicMock, patch from pathlib import Path from markitect.spaces.rendering import ( SpaceRenderer, RenderConfig, RenderResult, RenderFormat, ThemeConfig, CompositeRenderer, MarkdownToHTMLRenderer, HTMLRendererFactory, THEME_PROPERTIES, combine_theme_properties, SpaceRenderingService, SpaceRenderingServiceBuilder, ) from markitect.spaces.transclusion import ( RenderCache, ReferenceGraph, ) from markitect.spaces.events import EventBus, SpaceEventType class TestRenderFormat: """Tests for RenderFormat enum.""" def test_html_format(self): """Test HTML format value.""" assert RenderFormat.HTML.value == "html" def test_format_members(self): """Test all format members exist.""" assert RenderFormat.HTML assert RenderFormat.PDF assert RenderFormat.DOCX assert RenderFormat.LATEX class TestThemeConfig: """Tests for ThemeConfig dataclass.""" def test_default_values(self): """Test default configuration.""" config = ThemeConfig() assert config.name == "default" assert config.layers == ["basic"] assert config.custom_css is None assert config.custom_properties == {} def test_custom_config(self): """Test custom configuration.""" config = ThemeConfig( name="custom", layers=["github", "dark"], custom_css="body { color: red; }", custom_properties={"max_width": "1000px"}, ) assert config.name == "custom" assert config.layers == ["github", "dark"] assert "color: red" in config.custom_css assert config.custom_properties["max_width"] == "1000px" class TestRenderConfig: """Tests for RenderConfig dataclass.""" def test_default_values(self): """Test default configuration.""" config = RenderConfig() assert config.format == RenderFormat.HTML assert config.include_toc is False assert config.highlight_code is True assert config.sanitize_html is True assert config.link_target_blank is True def test_custom_config(self): """Test custom configuration.""" theme = ThemeConfig(name="github") config = RenderConfig( format=RenderFormat.HTML, theme=theme, include_toc=True, image_max_width="800px", ) assert config.theme.name == "github" assert config.include_toc is True assert config.image_max_width == "800px" class TestRenderResult: """Tests for RenderResult dataclass.""" def test_creation(self): """Test result creation.""" result = RenderResult( content="
Test
", format=RenderFormat.HTML, content_hash="abc", source_hash="def", document_id="doc-1", space_id="space-1", dependencies={"doc-2", "doc-3"}, ) assert len(result.dependencies) == 2 assert "doc-2" in result.dependencies def test_compute_hash(self): """Test hash computation.""" hash1 = RenderResult.compute_hash("test content") hash2 = RenderResult.compute_hash("test content") hash3 = RenderResult.compute_hash("different content") assert hash1 == hash2 assert hash1 != hash3 assert len(hash1) == 16 # SHA256 truncated to 16 chars class TestCombineThemeProperties: """Tests for theme property combination.""" def test_single_layer(self): """Test single theme layer.""" props = combine_theme_properties(["default"]) assert "font_family" in props assert "body_color" in props def test_multiple_layers(self): """Test multiple theme layers.""" props = combine_theme_properties(["default", "github"]) # GitHub layer should override default assert props["max_width"] == "980px" def test_unknown_layer(self): """Test with unknown layer.""" props = combine_theme_properties(["nonexistent"]) assert props == {} def test_empty_layers(self): """Test with no layers.""" props = combine_theme_properties([]) assert props == {} class TestMarkdownToHTMLRenderer: """Tests for MarkdownToHTMLRenderer.""" def test_supported_formats(self): """Test supported formats.""" renderer = MarkdownToHTMLRenderer() assert RenderFormat.HTML in renderer.supported_formats assert len(renderer.supported_formats) == 1 def test_simple_render(self): """Test simple markdown rendering.""" renderer = MarkdownToHTMLRenderer() result = renderer.render( content="# Hello World\n\nThis is a paragraph.", document_id="doc-1", space_id="space-1", ) assert result.format == RenderFormat.HTML assert "" in result.content or "" in result.content
def test_render_with_links(self):
"""Test link rendering with target blank."""
renderer = MarkdownToHTMLRenderer()
result = renderer.render(
content="Visit [Google](https://google.com)",
document_id="doc-1",
space_id="space-1",
)
assert "https://google.com" in result.content
assert 'target="_blank"' in result.content
def test_render_with_toc(self):
"""Test table of contents generation."""
config = RenderConfig(include_toc=True)
renderer = MarkdownToHTMLRenderer(config)
markdown = """
# Main Title
## Section 1
Content here.
## Section 2
More content.
"""
result = renderer.render(
content=markdown,
document_id="doc-1",
space_id="space-1",
)
assert 'class="toc"' in result.content
assert "Contents" in result.content
def test_render_with_dependencies(self):
"""Test dependencies are tracked."""
renderer = MarkdownToHTMLRenderer()
result = renderer.render(
content="# Test",
document_id="doc-1",
space_id="space-1",
dependencies={"dep-1", "dep-2"},
)
assert len(result.dependencies) == 2
assert "dep-1" in result.dependencies
def test_content_hash_computation(self):
"""Test that hashes are computed."""
renderer = MarkdownToHTMLRenderer()
result = renderer.render(
content="# Test",
document_id="doc-1",
space_id="space-1",
)
assert result.content_hash
assert result.source_hash
assert len(result.content_hash) == 16
def test_different_themes(self):
"""Test different theme configurations."""
github_config = RenderConfig(theme=ThemeConfig(name="github", layers=["github"]))
dark_config = RenderConfig(theme=ThemeConfig(name="dark", layers=["dark"]))
github_renderer = MarkdownToHTMLRenderer(github_config)
dark_renderer = MarkdownToHTMLRenderer(dark_config)
github_result = github_renderer.render("# Test", "doc", "space")
dark_result = dark_renderer.render("# Test", "doc", "space")
# Results should be different due to different themes
assert github_result.content != dark_result.content
assert "#24292e" in github_result.content # GitHub body color
assert "#c9d1d9" in dark_result.content # Dark mode text color
class TestHTMLRendererFactory:
"""Tests for HTMLRendererFactory."""
def test_create_default(self):
"""Test default renderer creation."""
renderer = HTMLRendererFactory.create_default()
assert isinstance(renderer, MarkdownToHTMLRenderer)
def test_create_github_style(self):
"""Test GitHub-style renderer."""
renderer = HTMLRendererFactory.create_github_style()
assert renderer.config.theme.name == "github"
def test_create_academic_style(self):
"""Test academic-style renderer."""
renderer = HTMLRendererFactory.create_academic_style()
assert renderer.config.theme.name == "academic"
assert renderer.config.include_toc is True
def test_create_minimal_style(self):
"""Test minimal-style renderer."""
renderer = HTMLRendererFactory.create_minimal_style()
assert renderer.config.theme.name == "minimal"
def test_create_dark_mode(self):
"""Test dark mode renderer."""
renderer = HTMLRendererFactory.create_dark_mode()
assert renderer.config.theme.name == "dark"
class TestCompositeRenderer:
"""Tests for CompositeRenderer."""
def test_register_renderer(self):
"""Test registering a renderer."""
composite = CompositeRenderer()
html_renderer = MarkdownToHTMLRenderer()
composite.register(html_renderer)
assert composite.get_renderer(RenderFormat.HTML) is html_renderer
def test_supported_formats(self):
"""Test listing supported formats."""
composite = CompositeRenderer()
html_renderer = MarkdownToHTMLRenderer()
composite.register(html_renderer)
formats = composite.supported_formats()
assert RenderFormat.HTML in formats
def test_render_via_composite(self):
"""Test rendering through composite."""
composite = CompositeRenderer()
composite.register(MarkdownToHTMLRenderer())
result = composite.render(
content="# Test",
document_id="doc-1",
space_id="space-1",
format=RenderFormat.HTML,
)
assert result.format == RenderFormat.HTML
assert "bold" in result.content or "bold" in result.content
def test_code_blocks(self):
"""Test code block parsing."""
renderer = MarkdownToHTMLRenderer()
markdown = "```python\nprint('hello')\n```"
result = renderer.render(markdown, "doc", "space")
assert "" in result.content or "Item" in result.content
def test_blockquotes(self):
"""Test blockquote parsing."""
renderer = MarkdownToHTMLRenderer()
markdown = "> This is a quote"
result = renderer.render(markdown, "doc", "space")
assert "