Markdown Conversion: Remove hard dependency on deprecated library and replace with IMarkdownToHtmlConverter abstraction (closes #21238 and #19500) (#21242)
* Add IMarkdownToHtmlConverter abstraction with Markdig and HeyRed implementations - Add IMarkdownToHtmlConverter interface in Umbraco.Core.Strings - Add MarkdigMarkdownToHtmlConverter using the Markdig library (new default) - Add HeyRedMarkdownToHtmlConverter using HeyRed.MarkdownSharp (deprecated, for backwards compatibility) - Update HealthChecks.MarkdownToHtmlConverter to use the new abstraction - Update MarkdownEditorValueConverter to use the new abstraction - Add unit tests for both markdown converter implementations - Add unit tests for HealthChecks.MarkdownToHtmlConverter syntax highlighting 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Apply suggestion from code review. --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
co-authored by
Claude Opus 4.5
Copilot
parent
51cec0e41f
commit
0da1fa116e
@@ -50,6 +50,7 @@
|
||||
<PackageVersion Include="JsonPatch.Net" Version="3.3.0" />
|
||||
<PackageVersion Include="K4os.Compression.LZ4" Version="1.3.8" />
|
||||
<PackageVersion Include="MailKit" Version="4.14.1" />
|
||||
<PackageVersion Include="Markdig" Version="0.44.0" />
|
||||
<PackageVersion Include="Markdown" Version="2.2.1" />
|
||||
<PackageVersion Include="MessagePack" Version="3.1.4" />
|
||||
<PackageVersion Include="MiniProfiler.AspNetCore.Mvc" Version="4.5.4" />
|
||||
|
||||
@@ -196,6 +196,14 @@ Copyright: 2013-2024 .NET Foundation and Contributors
|
||||
|
||||
---
|
||||
|
||||
Markdig: A fast, powerful, CommonMark compliant, extensible Markdown processor for .NET
|
||||
|
||||
URL: https://github.com/xoofx/markdig
|
||||
License: BSD-2-Clause license
|
||||
Copyright: 2018+, Alexandre Mutel. All rights reserved.
|
||||
|
||||
---
|
||||
|
||||
Markdown: A library for parsing and compiling Markdown
|
||||
|
||||
URL: https://github.com/hey-red/Markdown
|
||||
|
||||
@@ -29,7 +29,7 @@ public class TypeFinder : ITypeFinder
|
||||
"DataAnnotationsExtensions,", "DataAnnotationsExtensions.", "Dynamic,", "Examine,", "Examine.",
|
||||
"HtmlAgilityPack,", "HtmlAgilityPack.", "HtmlDiff,", "ICSharpCode.", "Iesi.Collections,", // used by NHibernate
|
||||
"JetBrains.Annotations,", "LightInject.", // DI
|
||||
"LightInject,", "Lucene.", "Markdown,", "Microsoft.", "MiniProfiler,", "Moq,", "MySql.", "NHibernate,",
|
||||
"LightInject,", "Lucene.", "Markdig,", "Markdown,", "Microsoft.", "MiniProfiler,", "Moq,", "MySql.", "NHibernate,",
|
||||
"NHibernate.", "Newtonsoft.", "NPoco,", "NuGet.", "RouteDebugger,", "Semver.", "Serilog.", "Serilog,",
|
||||
"ServiceStack.", "SqlCE4Umbraco,", "Superpower,", // used by Serilog
|
||||
"System.", "TidyNet,", "TidyNet.", "WebDriver,", "itextsharp,", "mscorlib,", "NUnit,", "NUnit.", "NUnit3.",
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace Umbraco.Cms.Core.Strings;
|
||||
|
||||
/// <summary>
|
||||
/// Defines a service that converts Markdown-formatted text to HTML.
|
||||
/// </summary>
|
||||
public interface IMarkdownToHtmlConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts the specified Markdown-formatted text to an HTML-encoded string.
|
||||
/// </summary>
|
||||
/// <param name="markdown">The input string containing Markdown syntax to be converted.</param>
|
||||
/// <returns>A string containing the HTML representation of the input Markdown.</returns>
|
||||
public string ToHtml(string markdown);
|
||||
}
|
||||
@@ -174,7 +174,7 @@ public static partial class UmbracoBuilderExtensions
|
||||
.Remove<SimpleRichTextValueConverter>();
|
||||
|
||||
// register *all* checks, except those marked [HideFromTypeFinder] of course
|
||||
builder.Services.AddSingleton<IMarkdownToHtmlConverter, MarkdownToHtmlConverter>();
|
||||
builder.Services.AddSingleton<Core.HealthChecks.NotificationMethods.IMarkdownToHtmlConverter, MarkdownToHtmlConverter>();
|
||||
|
||||
builder.Services.AddSingleton<IContentLastChanceFinder, ContentFinderByConfigured404>();
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ using Umbraco.Cms.Infrastructure.PublishedCache;
|
||||
using Umbraco.Cms.Infrastructure.Scoping;
|
||||
using Umbraco.Cms.Infrastructure.Services;
|
||||
using Umbraco.Cms.Infrastructure.Services.Implement;
|
||||
using Umbraco.Cms.Infrastructure.Strings;
|
||||
using Umbraco.Cms.Infrastructure.Telemetry.Providers;
|
||||
using Umbraco.Cms.Infrastructure.Templates.PartialViews;
|
||||
using Umbraco.Extensions;
|
||||
@@ -87,6 +88,11 @@ public static partial class UmbracoBuilderExtensions
|
||||
builder.Services.AddUnique<IMediaSearchService, MediaSearchService>();
|
||||
builder.Services.AddUnique<IDistributedJobService, DistributedJobService>();
|
||||
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
// TODO (V18): Replace this with MarkdigMarkdownToHtmlConverter as the default implementation.
|
||||
builder.Services.AddUnique<IMarkdownToHtmlConverter, HeyRedMarkdownToHtmlConverter>();
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using HeyRed.MarkdownSharp;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.HealthChecks;
|
||||
using Umbraco.Cms.Core.HealthChecks.NotificationMethods;
|
||||
|
||||
@@ -6,10 +7,19 @@ namespace Umbraco.Cms.Infrastructure.HealthChecks;
|
||||
|
||||
public class MarkdownToHtmlConverter : IMarkdownToHtmlConverter
|
||||
{
|
||||
private readonly Core.Strings.IMarkdownToHtmlConverter _markdownToHtmlConverter;
|
||||
|
||||
public MarkdownToHtmlConverter(Core.Strings.IMarkdownToHtmlConverter markdownToHtmlConverter) => _markdownToHtmlConverter = markdownToHtmlConverter;
|
||||
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 19.")]
|
||||
public MarkdownToHtmlConverter()
|
||||
: this(StaticServiceProvider.Instance.GetRequiredService<Core.Strings.IMarkdownToHtmlConverter>())
|
||||
{
|
||||
}
|
||||
|
||||
public string ToHtml(HealthCheckResults results, HealthCheckNotificationVerbosity verbosity)
|
||||
{
|
||||
var mark = new Markdown();
|
||||
var html = mark.Transform(results.ResultsAsMarkDown(verbosity));
|
||||
var html = _markdownToHtmlConverter.ToHtml(results.ResultsAsMarkDown(verbosity));
|
||||
html = ApplyHtmlHighlighting(html);
|
||||
return html;
|
||||
}
|
||||
|
||||
+23
-6
@@ -1,7 +1,8 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using HeyRed.MarkdownSharp;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
using Umbraco.Cms.Core.PropertyEditors.DeliveryApi;
|
||||
using Umbraco.Cms.Core.Strings;
|
||||
@@ -15,11 +16,22 @@ public class MarkdownEditorValueConverter : PropertyValueConverterBase, IDeliver
|
||||
{
|
||||
private readonly HtmlLocalLinkParser _localLinkParser;
|
||||
private readonly HtmlUrlParser _urlParser;
|
||||
private readonly IMarkdownToHtmlConverter _markdownToHtmlConverter;
|
||||
|
||||
public MarkdownEditorValueConverter(HtmlLocalLinkParser localLinkParser, HtmlUrlParser urlParser)
|
||||
public MarkdownEditorValueConverter(HtmlLocalLinkParser localLinkParser, HtmlUrlParser urlParser, IMarkdownToHtmlConverter markdownToHtmlConverter)
|
||||
{
|
||||
_localLinkParser = localLinkParser;
|
||||
_urlParser = urlParser;
|
||||
_markdownToHtmlConverter = markdownToHtmlConverter;
|
||||
}
|
||||
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 19.")]
|
||||
public MarkdownEditorValueConverter(HtmlLocalLinkParser localLinkParser, HtmlUrlParser urlParser)
|
||||
: this(
|
||||
localLinkParser,
|
||||
urlParser,
|
||||
StaticServiceProvider.Instance.GetRequiredService<IMarkdownToHtmlConverter>())
|
||||
{
|
||||
}
|
||||
|
||||
public override bool IsConverter(IPublishedPropertyType propertyType)
|
||||
@@ -49,10 +61,15 @@ public class MarkdownEditorValueConverter : PropertyValueConverterBase, IDeliver
|
||||
|
||||
public override object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object? inter, bool preview)
|
||||
{
|
||||
// convert markup to HTML for frontend rendering.
|
||||
// source should come from ConvertSource and be a string (or null) already
|
||||
var mark = new Markdown();
|
||||
return new HtmlEncodedString(inter == null ? string.Empty : mark.Transform((string)inter));
|
||||
// Convert markup to HTML for frontend rendering.
|
||||
// Source should come from ConvertSource and be a string (or null) already.
|
||||
if (inter is null)
|
||||
{
|
||||
return new HtmlEncodedString(string.Empty);
|
||||
}
|
||||
|
||||
var htmlString = _markdownToHtmlConverter.ToHtml((string)inter);
|
||||
return new HtmlEncodedString(htmlString);
|
||||
}
|
||||
|
||||
public PropertyCacheLevel GetDeliveryApiPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Element;
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
using HeyRed.MarkdownSharp;
|
||||
using Umbraco.Cms.Core.Strings;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Strings;
|
||||
|
||||
// TODO (V19): Remove this class along with the HeyRed.MarkdownSharp library entirely (remove reference from Directory.props and .csproj, and remove from NOTICES.txt).
|
||||
|
||||
/// <summary>
|
||||
/// Implements a service that converts Markdown-formatted text to HTML using the HeyRed.MarkdownSharp library.
|
||||
/// </summary>
|
||||
[Obsolete("Uses the deprecated HeyRed.MarkdownSharp library which will continue to be provided for the lifetime of Umbraco 17 as the default implementation of IMarkdownToHtmlConverter. The default will be changed to MarkdigMarkdownToHtmlConverter for Umbraco 18. Scheduled for removal along with the HeyRed.MarkdownSharp library in Umbraco 19.")]
|
||||
public class HeyRedMarkdownToHtmlConverter : IMarkdownToHtmlConverter
|
||||
{
|
||||
private static readonly Markdown _markdownConverter = new();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string ToHtml(string markdown) => _markdownConverter.Transform(markdown);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using Markdig;
|
||||
using Umbraco.Cms.Core.Strings;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Strings;
|
||||
|
||||
/// <summary>
|
||||
/// Implements a service that converts Markdown-formatted text to HTML using the Markdig library.
|
||||
/// </summary>
|
||||
public class MarkdigMarkdownToHtmlConverter : IMarkdownToHtmlConverter
|
||||
{
|
||||
private static readonly MarkdownPipeline _markdownPipeline = new MarkdownPipelineBuilder().Build();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string ToHtml(string markdown) => Markdown.ToHtml(markdown, _markdownPipeline);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<PackageId>Umbraco.Cms.Infrastructure</PackageId>
|
||||
<Title>Umbraco CMS - Infrastructure</Title>
|
||||
@@ -39,6 +39,7 @@
|
||||
<PackageReference Include="Examine.Core" />
|
||||
<PackageReference Include="HtmlAgilityPack" />
|
||||
<PackageReference Include="MailKit" />
|
||||
<PackageReference Include="Markdig" />
|
||||
<PackageReference Include="Markdown" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" />
|
||||
|
||||
+4
-1
@@ -10,6 +10,7 @@ using Umbraco.Cms.Core.PropertyEditors;
|
||||
using Umbraco.Cms.Core.PropertyEditors.ValueConverters;
|
||||
using Umbraco.Cms.Core.Routing;
|
||||
using Umbraco.Cms.Core.Templates;
|
||||
using Umbraco.Cms.Infrastructure.Strings;
|
||||
|
||||
namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.DeliveryApi;
|
||||
|
||||
@@ -25,7 +26,9 @@ public class MarkdownEditorValueConverterTests : PropertyValueConverterTests
|
||||
{
|
||||
var linkParser = new HtmlLocalLinkParser(Mock.Of<IPublishedUrlProvider>());
|
||||
var urlParser = new HtmlUrlParser(Mock.Of<IOptionsMonitor<ContentSettings>>(), Mock.Of<ILogger<HtmlUrlParser>>(), Mock.Of<IProfilingLogger>(), Mock.Of<IIOHelper>());
|
||||
var valueConverter = new MarkdownEditorValueConverter(linkParser, urlParser);
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
var valueConverter = new MarkdownEditorValueConverter(linkParser, urlParser, new HeyRedMarkdownToHtmlConverter());
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
|
||||
Assert.AreEqual(typeof(string), valueConverter.GetDeliveryApiPropertyValueType(Mock.Of<IPublishedPropertyType>()));
|
||||
var result = valueConverter.ConvertIntermediateToDeliveryApiObject(Mock.Of<IPublishedElement>(), Mock.Of<IPublishedPropertyType>(), PropertyCacheLevel.Element, inter, false, false);
|
||||
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Core.HealthChecks;
|
||||
using Umbraco.Cms.Infrastructure.HealthChecks;
|
||||
using Umbraco.Cms.Infrastructure.Strings;
|
||||
|
||||
namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.HealthChecks;
|
||||
|
||||
[TestFixture]
|
||||
public class MarkdownToHtmlConverterTests
|
||||
{
|
||||
[Test]
|
||||
public async Task ToHtml_WithSuccessResult_AppliesGreenHighlighting()
|
||||
{
|
||||
var checks = new List<HealthCheck> { new SuccessHealthCheck() };
|
||||
var results = await HealthCheckResults.Create(checks);
|
||||
var converter = CreateConverter();
|
||||
|
||||
var html = converter.ToHtml(results, HealthCheckNotificationVerbosity.Summary);
|
||||
|
||||
Assert.That(html, Does.Contain("<span style=\"color: #5cb85c\">Success</span>"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task ToHtml_WithWarningResult_AppliesOrangeHighlighting()
|
||||
{
|
||||
var checks = new List<HealthCheck> { new WarningHealthCheck() };
|
||||
var results = await HealthCheckResults.Create(checks);
|
||||
var converter = CreateConverter();
|
||||
|
||||
var html = converter.ToHtml(results, HealthCheckNotificationVerbosity.Summary);
|
||||
|
||||
Assert.That(html, Does.Contain("<span style=\"color: #f0ad4e\">Warning</span>"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task ToHtml_WithErrorResult_AppliesRedHighlighting()
|
||||
{
|
||||
var checks = new List<HealthCheck> { new ErrorHealthCheck() };
|
||||
var results = await HealthCheckResults.Create(checks);
|
||||
var converter = CreateConverter();
|
||||
|
||||
var html = converter.ToHtml(results, HealthCheckNotificationVerbosity.Summary);
|
||||
|
||||
Assert.That(html, Does.Contain("<span style=\"color: #d9534f\">Error</span>"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task ToHtml_WithMixedResults_AppliesCorrectHighlightingForEachStatus()
|
||||
{
|
||||
var checks = new List<HealthCheck>
|
||||
{
|
||||
new SuccessHealthCheck(),
|
||||
new WarningHealthCheck(),
|
||||
new ErrorHealthCheck(),
|
||||
};
|
||||
var results = await HealthCheckResults.Create(checks);
|
||||
var converter = CreateConverter();
|
||||
|
||||
var html = converter.ToHtml(results, HealthCheckNotificationVerbosity.Summary);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(html, Does.Contain("<span style=\"color: #5cb85c\">Success</span>"), "Success should have green highlighting");
|
||||
Assert.That(html, Does.Contain("<span style=\"color: #f0ad4e\">Warning</span>"), "Warning should have orange highlighting");
|
||||
Assert.That(html, Does.Contain("<span style=\"color: #d9534f\">Error</span>"), "Error should have red highlighting");
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task ToHtml_WithDetailedVerbosity_IncludesMessageInOutput()
|
||||
{
|
||||
var checks = new List<HealthCheck> { new SuccessHealthCheck() };
|
||||
var results = await HealthCheckResults.Create(checks);
|
||||
var converter = CreateConverter();
|
||||
|
||||
var html = converter.ToHtml(results, HealthCheckNotificationVerbosity.Detailed);
|
||||
|
||||
Assert.That(html, Does.Contain("Check passed"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task ToHtml_WithSummaryVerbosity_DoesNotIncludeSuccessMessageInOutput()
|
||||
{
|
||||
var checks = new List<HealthCheck> { new SuccessHealthCheck() };
|
||||
var results = await HealthCheckResults.Create(checks);
|
||||
var converter = CreateConverter();
|
||||
|
||||
var html = converter.ToHtml(results, HealthCheckNotificationVerbosity.Summary);
|
||||
|
||||
Assert.That(html, Does.Not.Contain("Check passed"));
|
||||
}
|
||||
|
||||
[HealthCheck("CFD6FC34-59C9-4402-B55F-C8BC96B628A1", "Stub check")]
|
||||
public abstract class StubHealthCheck : HealthCheck
|
||||
{
|
||||
private readonly string _message;
|
||||
private readonly StatusResultType _resultType;
|
||||
|
||||
protected StubHealthCheck(StatusResultType resultType, string message)
|
||||
{
|
||||
_resultType = resultType;
|
||||
_message = message;
|
||||
}
|
||||
|
||||
public override HealthCheckStatus ExecuteAction(HealthCheckAction action) =>
|
||||
throw new NotImplementedException();
|
||||
|
||||
public override Task<IEnumerable<HealthCheckStatus>> GetStatusAsync() =>
|
||||
Task.FromResult<IEnumerable<HealthCheckStatus>>([new(_message) { ResultType = _resultType }]);
|
||||
}
|
||||
|
||||
[HealthCheck("CFD6FC34-59C9-4402-B55F-C8BC96B628A1", "Success Check")]
|
||||
public class SuccessHealthCheck : StubHealthCheck
|
||||
{
|
||||
public SuccessHealthCheck()
|
||||
: base(StatusResultType.Success, "Check passed")
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[HealthCheck("CFD6FC34-59C9-4402-B55F-C8BC96B628A2", "Warning Check")]
|
||||
public class WarningHealthCheck : StubHealthCheck
|
||||
{
|
||||
public WarningHealthCheck()
|
||||
: base(StatusResultType.Warning, "Check has warnings")
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[HealthCheck("CFD6FC34-59C9-4402-B55F-C8BC96B628A3", "Error Check")]
|
||||
public class ErrorHealthCheck : StubHealthCheck
|
||||
{
|
||||
public ErrorHealthCheck()
|
||||
: base(StatusResultType.Error, "Check failed")
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
private static MarkdownToHtmlConverter CreateConverter() =>
|
||||
new(new HeyRedMarkdownToHtmlConverter());
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Infrastructure.Strings;
|
||||
|
||||
namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Strings;
|
||||
|
||||
[TestFixture]
|
||||
public partial class MarkdownToHtmlConverterTests
|
||||
{
|
||||
private const string MarkdownSample = """
|
||||
# Heading
|
||||
|
||||
This is **bold** and *italic* text.
|
||||
|
||||
- Item one
|
||||
- Item two
|
||||
|
||||
```
|
||||
var x = 1;
|
||||
```
|
||||
""";
|
||||
|
||||
[Test]
|
||||
public void MarkdigMarkdownToHtmlConverter_ToHtml_ConvertsMarkdownToHtml()
|
||||
{
|
||||
var converter = new MarkdigMarkdownToHtmlConverter();
|
||||
|
||||
var result = converter.ToHtml(MarkdownSample);
|
||||
|
||||
const string ExpectedHtml = """
|
||||
<h1>Heading</h1>
|
||||
<p>This is <strong>bold</strong> and <em>italic</em> text.</p>
|
||||
<ul>
|
||||
<li>Item one</li>
|
||||
<li>Item two</li>
|
||||
</ul>
|
||||
<pre><code>var x = 1;</code></pre>
|
||||
""";
|
||||
|
||||
Assert.That(NormalizeHtml(result), Is.EqualTo(NormalizeHtml(ExpectedHtml)));
|
||||
}
|
||||
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
[Test]
|
||||
public void HeyRedMarkdownToHtmlConverter_ToHtml_ConvertsMarkdownToHtml()
|
||||
{
|
||||
var converter = new HeyRedMarkdownToHtmlConverter();
|
||||
|
||||
var result = converter.ToHtml(MarkdownSample);
|
||||
|
||||
const string ExpectedHtml = """
|
||||
<h1>Heading</h1>
|
||||
<p>This is <strong>bold</strong> and <em>italic</em> text.</p>
|
||||
<ul>
|
||||
<li>Item one</li>
|
||||
<li>Item two</li>
|
||||
</ul>
|
||||
<p><code>var x = 1;</code></p>
|
||||
""";
|
||||
|
||||
Assert.That(NormalizeHtml(result), Is.EqualTo(NormalizeHtml(ExpectedHtml)));
|
||||
}
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
|
||||
private static string NormalizeHtml(string html) =>
|
||||
WhitespaceBeforeTagRegex().Replace(
|
||||
WhitespaceAfterTagRegex().Replace(html, ">"),
|
||||
"<").Trim();
|
||||
|
||||
[GeneratedRegex(@">\s+")]
|
||||
private static partial Regex WhitespaceAfterTagRegex();
|
||||
|
||||
[GeneratedRegex(@"\s+<")]
|
||||
private static partial Regex WhitespaceBeforeTagRegex();
|
||||
}
|
||||
Reference in New Issue
Block a user