MAJOR FEATURES: - **Layered Theme Architecture**: Combine themes across UI, document, and branding scopes - **Advanced Theme Combinations**: Support complex themes like "dark,academic" or "light,github,corporate" - **Legacy Compatibility**: Existing --template usage continues to work seamlessly - **Enhanced CLI Validation**: Proper theme validation with helpful error messages TECHNICAL IMPROVEMENTS: - Replace DocumentManager with CleanDocumentManager throughout codebase - Add ThemeType custom click parameter with comprehensive validation - Implement parse_theme_string() and combine_theme_properties() functions - Add _get_template_css() and _generate_layered_css() methods - Support for UI themes (light/dark), document themes (basic/github/academic), and branding themes (corporate/startup) THEME CAPABILITIES: - **Single themes**: basic, github, dark, academic, light, corporate, startup - **Layered themes**: dark,academic combines dark UI with academic typography - **Complex combinations**: light,github,corporate for branded GitHub-style documents - **Intelligent property merging**: Later themes override earlier theme properties QUALITY ASSURANCE: - All template system tests passing (12/12) - Fixed import errors and missing dependencies - Updated test expectations for new validation messages - Comprehensive validation prevents unknown theme usage Breaking Change: --template parameter renamed to --theme with enhanced functionality 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
402 lines
14 KiB
Python
402 lines
14 KiB
Python
"""
|
|
Tests for Issue #132: Template System and CSS Injection
|
|
|
|
This module tests template selection and custom CSS injection functionality
|
|
for client-side markdown rendering.
|
|
"""
|
|
|
|
import pytest
|
|
import tempfile
|
|
import os
|
|
from pathlib import Path
|
|
from unittest.mock import patch, MagicMock
|
|
import json
|
|
|
|
# Add project root to path for imports
|
|
import sys
|
|
project_root = Path(__file__).parent.parent.parent.parent
|
|
sys.path.insert(0, str(project_root))
|
|
|
|
|
|
class TestIssue132TemplateSystem:
|
|
"""Test template selection and CSS injection functionality."""
|
|
|
|
def setup_method(self):
|
|
"""Set up test environment."""
|
|
# Create temporary directory for test outputs
|
|
self.temp_dir = tempfile.mkdtemp()
|
|
self.markdown_content = """# Template Test
|
|
|
|
This is a test document for template system validation.
|
|
|
|
## Features
|
|
- Multiple templates
|
|
- Custom CSS support
|
|
- Responsive design
|
|
"""
|
|
|
|
def teardown_method(self):
|
|
"""Clean up test environment."""
|
|
# Clean up temporary files
|
|
import shutil
|
|
shutil.rmtree(self.temp_dir, ignore_errors=True)
|
|
|
|
def test_default_template_generates_basic_html(self):
|
|
"""Test that default template generates basic HTML structure - Issue #132."""
|
|
input_file = Path(self.temp_dir) / "default.md"
|
|
input_file.write_text(self.markdown_content)
|
|
|
|
output_file = Path(self.temp_dir) / "default.html"
|
|
|
|
# Template system IS implemented - test actual functionality
|
|
from markitect.cli import cli
|
|
from click.testing import CliRunner
|
|
|
|
runner = CliRunner()
|
|
result = runner.invoke(cli, [
|
|
'md-render',
|
|
str(input_file),
|
|
'--output', str(output_file)
|
|
])
|
|
|
|
assert result.exit_code == 0
|
|
assert output_file.exists()
|
|
|
|
html_content = output_file.read_text()
|
|
|
|
# Should contain basic HTML5 structure
|
|
assert '<!DOCTYPE html>' in html_content
|
|
assert '<meta charset="utf-8">' in html_content
|
|
assert '<title>' in html_content
|
|
|
|
def test_github_template_option(self):
|
|
"""Test GitHub-style template selection - Issue #132."""
|
|
input_file = Path(self.temp_dir) / "github.md"
|
|
input_file.write_text(self.markdown_content)
|
|
|
|
output_file = Path(self.temp_dir) / "github.html"
|
|
|
|
# Template system IS implemented - test GitHub template
|
|
from markitect.cli import cli
|
|
from click.testing import CliRunner
|
|
|
|
runner = CliRunner()
|
|
result = runner.invoke(cli, [
|
|
'md-render',
|
|
str(input_file),
|
|
'--output', str(output_file),
|
|
'--theme', 'github'
|
|
])
|
|
|
|
assert result.exit_code == 0
|
|
assert output_file.exists()
|
|
|
|
html_content = output_file.read_text()
|
|
assert 'border-bottom: 1px solid #d0d7de' in html_content # GitHub heading style
|
|
|
|
def test_template_loading_from_filesystem(self):
|
|
"""Test template system uses embedded templates - Issue #132."""
|
|
# Templates are embedded in code, not loaded from filesystem
|
|
# Test that template system provides all expected templates
|
|
from markitect.plugins.builtin.markdown_commands import TEMPLATE_STYLES
|
|
|
|
# Should have all expected templates available
|
|
expected_templates = ['basic', 'github', 'academic', 'dark']
|
|
|
|
for template_name in expected_templates:
|
|
assert template_name in TEMPLATE_STYLES
|
|
template_config = TEMPLATE_STYLES[template_name]
|
|
|
|
# Each template should have required style properties
|
|
assert 'body_color' in template_config
|
|
assert 'font_family' in template_config
|
|
assert 'max_width' in template_config
|
|
|
|
# Test that templates are properly formatted with variable placeholders
|
|
from markitect.plugins.builtin.markdown_commands import generate_html_with_embedded_markdown
|
|
test_html = generate_html_with_embedded_markdown("# Test", "Test Title", "basic", "", {})
|
|
# HTML template should be properly formatted
|
|
assert '<!DOCTYPE html>' in test_html
|
|
assert 'Test Title' in test_html
|
|
assert '# Test' in test_html
|
|
|
|
def test_template_variable_substitution(self):
|
|
"""Test template variable substitution system - Issue #132."""
|
|
input_file = Path(self.temp_dir) / "variables.md"
|
|
input_file.write_text("# Variable Test\n\nTesting substitution.")
|
|
|
|
output_file = Path(self.temp_dir) / "variables.html"
|
|
|
|
# Template engine IS implemented - test actual functionality
|
|
from markitect.cli import cli
|
|
from click.testing import CliRunner
|
|
|
|
runner = CliRunner()
|
|
result = runner.invoke(cli, [
|
|
'md-render',
|
|
str(input_file),
|
|
'--output', str(output_file)
|
|
])
|
|
|
|
assert result.exit_code == 0
|
|
assert output_file.exists()
|
|
|
|
html_content = output_file.read_text()
|
|
|
|
# Variables should be substituted with actual values
|
|
assert '{{ markdown_json }}' not in html_content # Should be replaced
|
|
assert '{{ title }}' not in html_content # Should be replaced
|
|
assert '{{ css_content }}' not in html_content # Should be replaced
|
|
|
|
# Should contain actual markdown content as JSON
|
|
assert '# Variable Test' in html_content
|
|
|
|
def test_custom_css_injection(self):
|
|
"""Test custom CSS injection into templates - Issue #132."""
|
|
custom_css = """
|
|
body {
|
|
font-family: 'Comic Sans MS', cursive;
|
|
background-color: #f0f0f0;
|
|
}
|
|
.markdown-content {
|
|
max-width: 800px;
|
|
margin: 0 auto;
|
|
}
|
|
"""
|
|
|
|
# Create CSS file
|
|
css_file = Path(self.temp_dir) / "custom.css"
|
|
css_file.write_text(custom_css)
|
|
|
|
input_file = Path(self.temp_dir) / "styled.md"
|
|
input_file.write_text(self.markdown_content)
|
|
|
|
output_file = Path(self.temp_dir) / "styled.html"
|
|
|
|
# CSS injection IS implemented - test actual functionality
|
|
from markitect.cli import cli
|
|
from click.testing import CliRunner
|
|
|
|
runner = CliRunner()
|
|
result = runner.invoke(cli, [
|
|
'md-render',
|
|
str(input_file),
|
|
'--output', str(output_file),
|
|
'--css', str(css_file)
|
|
])
|
|
|
|
assert result.exit_code == 0
|
|
assert output_file.exists()
|
|
|
|
html_content = output_file.read_text()
|
|
# Custom CSS should be injected
|
|
assert 'Comic Sans MS' in html_content
|
|
assert 'background-color: #f0f0f0' in html_content
|
|
|
|
def test_css_content_embedded_in_html(self):
|
|
"""Test that CSS content is properly embedded in HTML - Issue #132."""
|
|
custom_css = "body { color: red; }"
|
|
css_file = Path(self.temp_dir) / "red.css"
|
|
css_file.write_text(custom_css)
|
|
|
|
input_file = Path(self.temp_dir) / "red_test.md"
|
|
input_file.write_text("# Red Test\n\nShould be red text.")
|
|
|
|
output_file = Path(self.temp_dir) / "red_test.html"
|
|
|
|
# CSS embedding IS implemented - test actual functionality
|
|
from markitect.cli import cli
|
|
from click.testing import CliRunner
|
|
|
|
runner = CliRunner()
|
|
result = runner.invoke(cli, [
|
|
'md-render',
|
|
str(input_file),
|
|
'--output', str(output_file),
|
|
'--css', str(css_file)
|
|
])
|
|
|
|
assert result.exit_code == 0
|
|
assert output_file.exists()
|
|
|
|
html_content = output_file.read_text()
|
|
|
|
# CSS should be embedded in <style> tags
|
|
assert '<style>' in html_content
|
|
assert 'body { color: red; }' in html_content
|
|
assert '</style>' in html_content
|
|
|
|
def test_template_with_markdown_parser_integration(self):
|
|
"""Test template integration with JavaScript markdown parser - Issue #132."""
|
|
input_file = Path(self.temp_dir) / "integration.md"
|
|
input_file.write_text("# Integration Test\n\nTesting parser integration.")
|
|
|
|
output_file = Path(self.temp_dir) / "integration.html"
|
|
|
|
# Integration IS implemented - test actual functionality
|
|
from markitect.cli import cli
|
|
from click.testing import CliRunner
|
|
|
|
runner = CliRunner()
|
|
result = runner.invoke(cli, [
|
|
'md-render',
|
|
str(input_file),
|
|
'--output', str(output_file)
|
|
])
|
|
|
|
assert result.exit_code == 0
|
|
assert output_file.exists()
|
|
|
|
html_content = output_file.read_text()
|
|
|
|
# Should contain markdown parser script
|
|
assert 'marked.min.js' in html_content
|
|
assert 'marked.parse' in html_content
|
|
assert 'Integration Test' in html_content
|
|
|
|
# Should contain rendering JavaScript
|
|
assert 'DOMContentLoaded' in html_content
|
|
assert 'getElementById' in html_content
|
|
assert 'innerHTML' in html_content
|
|
|
|
def test_multiple_templates_available(self):
|
|
"""Test that multiple template options are available - Issue #132."""
|
|
# Test template availability
|
|
theme_options = ['basic', 'github', 'academic', 'dark']
|
|
|
|
from markitect.plugins.builtin.markdown_commands import md_render_command
|
|
from click.testing import CliRunner
|
|
|
|
# Create test markdown file
|
|
input_file = Path(self.temp_dir) / "template_test.md"
|
|
input_file.write_text("# Template Test\n\nTesting multiple templates.")
|
|
|
|
runner = CliRunner()
|
|
|
|
for theme in theme_options:
|
|
output_file = Path(self.temp_dir) / f"{theme}_output.html"
|
|
|
|
result = runner.invoke(md_render_command, [
|
|
str(input_file),
|
|
'--output', str(output_file),
|
|
'--theme', theme
|
|
])
|
|
|
|
# Should be able to specify different templates
|
|
assert result.exit_code == 0
|
|
assert output_file.exists()
|
|
|
|
# Verify template-specific styling
|
|
html_content = output_file.read_text()
|
|
assert '<title>Template Test</title>' in html_content
|
|
|
|
def test_dark_theme_template_specific_styling(self):
|
|
"""Test that dark theme has appropriate dark styling - Issue #132."""
|
|
input_file = Path(self.temp_dir) / "dark_test.md"
|
|
input_file.write_text("# Dark Theme Test\n\n> Blockquote test\n\n```code block```")
|
|
|
|
output_file = Path(self.temp_dir) / "dark_test.html"
|
|
|
|
from markitect.plugins.builtin.markdown_commands import md_render_command
|
|
from click.testing import CliRunner
|
|
|
|
runner = CliRunner()
|
|
result = runner.invoke(md_render_command, [
|
|
str(input_file),
|
|
'--output', str(output_file),
|
|
'--theme', 'dark'
|
|
])
|
|
|
|
assert result.exit_code == 0
|
|
assert output_file.exists()
|
|
|
|
html_content = output_file.read_text()
|
|
|
|
# Verify dark theme specific colors
|
|
assert 'background-color: #0d1117' in html_content # Dark background
|
|
assert 'color: #e1e4e8' in html_content # Light text
|
|
assert 'color: #58a6ff' in html_content # Blue headings
|
|
assert 'background-color: #161b22' in html_content # Dark code blocks
|
|
assert 'border-left: 4px solid #58a6ff' in html_content # Blue blockquote border
|
|
|
|
def test_invalid_template_handling(self):
|
|
"""Test error handling for invalid template names - Issue #132."""
|
|
input_file = Path(self.temp_dir) / "invalid.md"
|
|
input_file.write_text("# Invalid Template Test")
|
|
|
|
# Error handling IS implemented - test invalid template
|
|
from markitect.cli import cli
|
|
from click.testing import CliRunner
|
|
|
|
runner = CliRunner()
|
|
result = runner.invoke(cli, [
|
|
'md-render',
|
|
str(input_file),
|
|
'--theme', 'nonexistent_template'
|
|
])
|
|
|
|
# Should exit with error code for invalid template choice
|
|
assert result.exit_code != 0
|
|
assert ('invalid choice' in result.output.lower() or
|
|
'not one of' in result.output.lower() or
|
|
'unknown theme' in result.output.lower())
|
|
|
|
def test_template_title_extraction_from_markdown(self):
|
|
"""Test title extraction from markdown for template variables - Issue #132."""
|
|
markdown_with_title = """# Main Title
|
|
|
|
This document should use "Main Title" as the HTML title.
|
|
"""
|
|
|
|
input_file = Path(self.temp_dir) / "title_test.md"
|
|
input_file.write_text(markdown_with_title)
|
|
|
|
output_file = Path(self.temp_dir) / "title_test.html"
|
|
|
|
# Title extraction IS implemented - test actual functionality
|
|
from markitect.cli import cli
|
|
from click.testing import CliRunner
|
|
|
|
runner = CliRunner()
|
|
result = runner.invoke(cli, [
|
|
'md-render',
|
|
str(input_file),
|
|
'--output', str(output_file)
|
|
])
|
|
|
|
assert result.exit_code == 0
|
|
assert output_file.exists()
|
|
|
|
html_content = output_file.read_text()
|
|
|
|
# HTML title should be extracted from first heading
|
|
assert '<title>Main Title</title>' in html_content
|
|
|
|
def test_responsive_template_css(self):
|
|
"""Test that default templates include responsive CSS - Issue #132."""
|
|
input_file = Path(self.temp_dir) / "responsive.md"
|
|
input_file.write_text("# Responsive Test\n\nTesting responsive design.")
|
|
|
|
output_file = Path(self.temp_dir) / "responsive.html"
|
|
|
|
# Responsive CSS IS implemented - test actual functionality
|
|
from markitect.cli import cli
|
|
from click.testing import CliRunner
|
|
|
|
runner = CliRunner()
|
|
result = runner.invoke(cli, [
|
|
'md-render',
|
|
str(input_file),
|
|
'--output', str(output_file)
|
|
])
|
|
|
|
assert result.exit_code == 0
|
|
assert output_file.exists()
|
|
|
|
html_content = output_file.read_text()
|
|
|
|
# Should include viewport meta tag
|
|
assert '<meta name="viewport"' in html_content
|
|
|
|
# Should include responsive CSS patterns
|
|
assert 'max-width' in html_content |