mirror of
https://github.com/Tyrrrz/DiscordChatExporter.git
synced 2026-08-13 14:03:01 -06:00
Fix markdown heading parsing at end of input
This commit is contained in:
parent
05f8df51e9
commit
0f2991cef2
137
DiscordChatExporter.Cli.Tests/Specs/HtmlMarkdownHeadingSpecs.cs
Normal file
137
DiscordChatExporter.Cli.Tests/Specs/HtmlMarkdownHeadingSpecs.cs
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using DiscordChatExporter.Core.Discord;
|
||||
using DiscordChatExporter.Core.Discord.Data;
|
||||
using DiscordChatExporter.Core.Exporting;
|
||||
using DiscordChatExporter.Core.Exporting.Filtering;
|
||||
using DiscordChatExporter.Core.Exporting.Partitioning;
|
||||
using FluentAssertions;
|
||||
using Xunit;
|
||||
|
||||
namespace DiscordChatExporter.Cli.Tests.Specs;
|
||||
|
||||
public class HtmlMarkdownHeadingSpecs
|
||||
{
|
||||
private static async ValueTask<string> FormatMarkdownAsync(string markdown) =>
|
||||
await HtmlMarkdownVisitor.FormatAsync(null!, markdown);
|
||||
|
||||
private static ExportContext CreateExportContext(bool shouldFormatMarkdown)
|
||||
{
|
||||
var guild = Guild.DirectMessages;
|
||||
var channel = new Channel(
|
||||
new Snowflake(1),
|
||||
ChannelKind.DirectTextChat,
|
||||
guild.Id,
|
||||
null,
|
||||
"test",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
null
|
||||
);
|
||||
|
||||
var request = new ExportRequest(
|
||||
guild,
|
||||
channel,
|
||||
Path.Combine(Path.GetTempPath(), "DiscordChatExporter.Tests", "test.html"),
|
||||
null,
|
||||
ExportFormat.HtmlDark,
|
||||
null,
|
||||
null,
|
||||
PartitionLimit.Null,
|
||||
MessageFilter.Null,
|
||||
false,
|
||||
shouldFormatMarkdown,
|
||||
false,
|
||||
true,
|
||||
"en-US",
|
||||
true
|
||||
);
|
||||
|
||||
return new ExportContext(new DiscordClient(""), request);
|
||||
}
|
||||
|
||||
private static Message CreateMessage(string content) =>
|
||||
new(
|
||||
new Snowflake(2),
|
||||
MessageKind.Default,
|
||||
MessageFlags.None,
|
||||
new User(new Snowflake(3), false, 1234, "test-user", "Test User", ""),
|
||||
DateTimeOffset.Parse("2024-01-01T00:00:00+00:00"),
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
content,
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
[Theory]
|
||||
[InlineData("# Heading", "<h1>Heading</h1>")]
|
||||
[InlineData("# Heading\nbody", "<h1>Heading</h1>body")]
|
||||
[InlineData("# Heading\r\nbody", "<h1>Heading</h1>body")]
|
||||
[InlineData("## Heading", "<h2>Heading</h2>")]
|
||||
[InlineData("### Heading", "<h3>Heading</h3>")]
|
||||
public async Task I_can_render_markdown_headings_as_html(string markdown, string expectedHtml)
|
||||
{
|
||||
// Act
|
||||
var html = await FormatMarkdownAsync(markdown);
|
||||
|
||||
// Assert
|
||||
html.Should().Be(expectedHtml);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("#hashtag")]
|
||||
[InlineData("#")]
|
||||
[InlineData("#\nHeading")]
|
||||
[InlineData("#\r\nHeading")]
|
||||
public async Task I_do_not_render_non_heading_hash_text_as_html_heading(string markdown)
|
||||
{
|
||||
// Act
|
||||
var html = await FormatMarkdownAsync(markdown);
|
||||
|
||||
// Assert
|
||||
html.Should().Be(markdown);
|
||||
html.Should().NotContain("<h");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task I_can_escape_html_sensitive_text_inside_markdown_headings()
|
||||
{
|
||||
// Act
|
||||
var html = await FormatMarkdownAsync("# <Heading> & \"quote\"");
|
||||
|
||||
// Assert
|
||||
html.Should().Be("<h1><Heading> & "quote"</h1>");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task I_keep_markdown_headings_literal_when_formatting_is_disabled()
|
||||
{
|
||||
// Arrange
|
||||
var context = CreateExportContext(false);
|
||||
var message = CreateMessage("# <Heading> & \"quote\"");
|
||||
|
||||
// Act
|
||||
var html = await new MessageGroupTemplate
|
||||
{
|
||||
Context = context,
|
||||
Messages = [message],
|
||||
}.RenderAsync();
|
||||
|
||||
// Assert
|
||||
html.Should().Contain("# <Heading> &");
|
||||
html.Should().NotContain("# <Heading>");
|
||||
html.Should().NotContain("<h1>");
|
||||
}
|
||||
}
|
||||
|
|
@ -131,8 +131,8 @@ internal static partial class MarkdownParser
|
|||
|
||||
private static readonly IMatcher<MarkdownContext, MarkdownNode> HeadingNodeMatcher =
|
||||
new RegexMatcher<MarkdownContext, MarkdownNode>(
|
||||
// Consume the linebreak so that it's not attached to following nodes.
|
||||
new Regex(@"^(\#{1,3})\s(.+)\n", DefaultRegexOptions),
|
||||
// Consume the linebreak, if present, so that it's not attached to following nodes.
|
||||
new Regex(@"^(\#{1,3})[^\S\r\n]+([^\r\n]+)(?:\r?\n|$)", DefaultRegexOptions),
|
||||
(c, s, m) => new HeadingNode(m.Groups[1].Length, Parse(c, s.Relocate(m.Groups[2])))
|
||||
);
|
||||
|
||||
|
|
|
|||
3
DiscordChatExporter.Core/Properties/AssemblyInfo.cs
Normal file
3
DiscordChatExporter.Core/Properties/AssemblyInfo.cs
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("DiscordChatExporter.Cli.Tests")]
|
||||
Loading…
Reference in a new issue