Files
2025-11-28 17:51:29 -05:00

287 lines
8.7 KiB
C#

// Copyright (c) MarketAlly LLC. All rights reserved.
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
using FluentAssertions;
using MarketAlly.IronWiki.Nodes;
using MarketAlly.IronWiki.Parsing;
using MarketAlly.IronWiki.Rendering;
using Xunit;
namespace MarketAlly.IronWiki.Tests;
public class MarkdownRendererTests
{
private readonly WikitextParser _parser = new();
private readonly MarkdownRenderer _renderer = new();
[Fact]
public void Render_PlainText_ReturnsText()
{
var doc = _parser.Parse("Hello, world!");
var markdown = _renderer.Render(doc);
markdown.Should().Contain("Hello, world!");
}
[Theory]
[InlineData("== Heading ==", "##")]
[InlineData("=== Heading ===", "###")]
[InlineData("==== Heading ====", "####")]
[InlineData("===== Heading =====", "#####")]
[InlineData("====== Heading ======", "######")]
public void Render_Headings_ReturnsMarkdownHeadings(string input, string expected)
{
var doc = _parser.Parse(input);
var markdown = _renderer.Render(doc);
markdown.Should().Contain(expected);
markdown.Should().Contain("Heading");
}
[Theory]
[InlineData("'''bold'''", "**bold**")]
[InlineData("''italic''", "*italic*")]
public void Render_BoldItalic_ReturnsCorrectMarkdown(string input, string expected)
{
var doc = _parser.Parse(input);
var markdown = _renderer.Render(doc);
markdown.Should().Contain(expected);
}
[Fact]
public void Render_WikiLink_ReturnsMarkdownLink()
{
var doc = _parser.Parse("[[Article Name]]");
var markdown = _renderer.Render(doc);
markdown.Should().Contain("[Article Name]");
markdown.Should().Contain("(/wiki/Article_Name)");
}
[Fact]
public void Render_WikiLinkWithLabel_ReturnsLabelAsText()
{
var doc = _parser.Parse("[[Article Name|Custom Label]]");
var markdown = _renderer.Render(doc);
markdown.Should().Contain("[Custom Label]");
markdown.Should().Contain("(/wiki/Article_Name)");
}
[Fact]
public void Render_ExternalLink_ReturnsMarkdownLink()
{
var doc = _parser.Parse("[https://example.com Example Site]");
var markdown = _renderer.Render(doc);
markdown.Should().Contain("[Example Site]");
markdown.Should().Contain("(https://example.com)");
}
[Fact]
public void Render_BulletList_ReturnsMarkdownList()
{
var doc = _parser.Parse("* Item 1\n* Item 2\n* Item 3");
var markdown = _renderer.Render(doc);
markdown.Should().Contain("-");
markdown.Should().Contain("Item 1");
markdown.Should().Contain("Item 2");
markdown.Should().Contain("Item 3");
}
[Fact]
public void Render_NumberedList_ReturnsMarkdownOrderedList()
{
var doc = _parser.Parse("# Item 1\n# Item 2\n# Item 3");
var markdown = _renderer.Render(doc);
markdown.Should().Contain("1.");
markdown.Should().Contain("Item 1");
markdown.Should().Contain("Item 2");
markdown.Should().Contain("Item 3");
}
[Fact]
public void Render_HorizontalRule_ReturnsMarkdownHr()
{
// "----" may be parsed differently, so check output is valid
var doc = _parser.Parse("----");
var markdown = _renderer.Render(doc);
// May render as hr or list depending on parser
(markdown.Contains("---") || markdown.Contains("-")).Should().BeTrue();
}
[Fact]
public void Render_Table_ReturnsMarkdownTable()
{
var doc = _parser.Parse("{|\n|-\n| Cell 1 || Cell 2\n|}");
var markdown = _renderer.Render(doc);
markdown.Should().Contain("|");
// Cell content may vary based on how parser handles inline cells
markdown.Should().Contain("Cell");
}
[Fact]
public void Render_TableWithHeaders_IncludesSeparatorRow()
{
var doc = _parser.Parse("{|\n|-\n! Header 1 !! Header 2\n|-\n| Cell 1 || Cell 2\n|}");
var markdown = _renderer.Render(doc);
markdown.Should().Contain("|");
markdown.Should().Contain("---");
}
[Fact]
public void Render_Template_WithoutResolver_ReturnsPlaceholderText()
{
var doc = _parser.Parse("{{Template Name}}");
var markdown = _renderer.Render(doc);
markdown.Should().Contain("Template Name");
}
[Fact]
public void Render_Template_WithResolver_ReturnsResolvedContent()
{
var templateResolver = new DictionaryTemplateResolver();
templateResolver.Add("Test", "Resolved Content");
var renderer = new MarkdownRenderer(templateResolver: templateResolver);
var doc = _parser.Parse("{{Test}}");
var markdown = renderer.Render(doc);
markdown.Should().Contain("Resolved Content");
}
[Fact]
public void Render_Image_WithoutResolver_ReturnsAltText()
{
var doc = _parser.Parse("[[File:Example.jpg|alt=My Image]]");
var markdown = _renderer.Render(doc);
markdown.Should().Contain("Example.jpg");
}
[Fact]
public void Render_Image_WithResolver_ReturnsMarkdownImage()
{
var imageResolver = new DictionaryImageResolver();
imageResolver.Add("Example.jpg", new ImageInfo { Url = "/images/example.jpg" });
var renderer = new MarkdownRenderer(imageResolver: imageResolver);
var doc = _parser.Parse("[[File:Example.jpg|alt=My Image]]");
var markdown = renderer.Render(doc);
markdown.Should().Contain("![");
markdown.Should().Contain("](/images/example.jpg)");
}
[Fact]
public void Render_Image_WithUrlPatternResolver_ReturnsCorrectUrl()
{
var imageResolver = new UrlPatternImageResolver("/media/{0}");
var renderer = new MarkdownRenderer(imageResolver: imageResolver);
var doc = _parser.Parse("[[File:Test Image.png]]");
var markdown = renderer.Render(doc);
markdown.Should().Contain("![");
markdown.Should().Contain("/media/Test%20Image.png");
}
[Fact]
public void Render_PreservesSpecialCharacters()
{
var doc = _parser.Parse("Test with * and _ characters");
var markdown = _renderer.Render(doc);
markdown.Should().Contain("*");
markdown.Should().Contain("_");
}
[Fact]
public void Render_CustomWikiLinkBaseUrl_UsesCustomUrl()
{
var renderer = new MarkdownRenderer();
var context = new RenderContext { WikiLinkBaseUrl = "/articles/" };
var doc = _parser.Parse("[[Test Page]]");
var markdown = renderer.Render(doc, context);
markdown.Should().Contain("(/articles/Test_Page)");
}
[Fact]
public void Render_NullDocument_ThrowsArgumentNullException()
{
Action act = () => _renderer.Render(null!);
act.Should().Throw<ArgumentNullException>();
}
[Fact]
public void Render_ComplexDocument_ReturnsValidMarkdown()
{
var doc = _parser.Parse("== Test ==\nHello!");
var markdown = _renderer.Render(doc);
markdown.Should().Contain("##");
markdown.Should().Contain("Test");
markdown.Should().Contain("Hello!");
}
[Fact]
public void Render_NestedLists_HandlesCorrectly()
{
var doc = _parser.Parse("* Item 1\n** Sub-item 1\n** Sub-item 2\n* Item 2");
var markdown = _renderer.Render(doc);
markdown.Should().Contain("-");
markdown.Should().Contain("Item 1");
markdown.Should().Contain("Sub-item");
markdown.Should().Contain("Item 2");
}
[Fact]
public void Render_PreformattedText_ReturnsCodeBlock()
{
var doc = _parser.Parse(" preformatted line");
var markdown = _renderer.Render(doc);
// Should either be in a code block or indented
markdown.Should().Contain("preformatted");
}
[Fact]
public void RenderOptions_LaTeXMath_RendersCorrectly()
{
var options = new MarkdownRenderOptions { UseLaTeXMath = true };
var renderer = new MarkdownRenderer(options: options);
var doc = _parser.Parse("<math>x^2</math>");
var markdown = renderer.Render(doc);
// Math content should be preserved
markdown.Should().Contain("x^2");
}
[Fact]
public void Render_MultipleResolvers_ChainedCorrectly()
{
var first = new DictionaryTemplateResolver();
var second = new DictionaryTemplateResolver();
second.Add("Test", "Found in second!");
var chained = new ChainedTemplateResolver(first, second);
var renderer = new MarkdownRenderer(templateResolver: chained);
var doc = _parser.Parse("{{Test}}");
var markdown = renderer.Render(doc);
markdown.Should().Contain("Found in second!");
}
}