Compare commits
2 commits
da16d3f28e
...
23a65c8674
Author | SHA1 | Date | |
---|---|---|---|
D. Moonfire | 23a65c8674 | ||
D. Moonfire | 55a4bbe676 |
66
src/MfGames.Markdown/RewriteLinkTransformer.cs
Normal file
66
src/MfGames.Markdown/RewriteLinkTransformer.cs
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
using Markdig.Renderers.Normalize;
|
||||||
|
using Markdig.Syntax;
|
||||||
|
using Markdig.Syntax.Inlines;
|
||||||
|
|
||||||
|
namespace MfGames.Markdown;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A transformer that goes through a Markdown document and visits each link
|
||||||
|
/// so it can be rewritten in a different format.
|
||||||
|
/// </summary>
|
||||||
|
public class RewriteLinkTransformer
|
||||||
|
{
|
||||||
|
private string output;
|
||||||
|
|
||||||
|
public RewriteLinkTransformer() { }
|
||||||
|
|
||||||
|
public RewriteLinkTransformer(Action<LinkInline> onLink)
|
||||||
|
{
|
||||||
|
this.OnLink = onLink;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A callback to trigger for every link found in the document.
|
||||||
|
/// </summary>
|
||||||
|
public Action<LinkInline>? OnLink { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parses the given input as Markdown, goes through and transforms all
|
||||||
|
/// the links, and then returns the modified Markdown.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input">The input text as Markdown.</param>
|
||||||
|
/// <returns>Modified Markdown text.</returns>
|
||||||
|
public string? Transform(string? input)
|
||||||
|
{
|
||||||
|
// If we get a null or blank string, we return it.
|
||||||
|
if (string.IsNullOrWhiteSpace(input) || this.OnLink == null)
|
||||||
|
{
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the Markdown into an abstract syntax tree (AST). We need the
|
||||||
|
// trivia because we want to round-trip as much as possible and it
|
||||||
|
// contains things like extra whitespace or indention.
|
||||||
|
MarkdownDocument document = Markdig.Markdown.Parse(input, true);
|
||||||
|
|
||||||
|
// Go through all the links.
|
||||||
|
IEnumerable<LinkInline> linkList = document.Descendants<LinkInline>();
|
||||||
|
|
||||||
|
foreach (LinkInline oldLink in linkList)
|
||||||
|
{
|
||||||
|
this.OnLink.Invoke(oldLink);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the AST back into Markdown and return the results. The
|
||||||
|
// RoundtripRenderer doesn't work here, but NormalizeRenderer seems to
|
||||||
|
// allow us to modify the link above and get the results.
|
||||||
|
var writer = new StringWriter();
|
||||||
|
var renderer = new NormalizeRenderer(writer);
|
||||||
|
|
||||||
|
renderer.Write(document);
|
||||||
|
|
||||||
|
this.output = writer.ToString();
|
||||||
|
|
||||||
|
return this.output;
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\MfGames.Markdown.Gemtext\MfGames.Markdown.Gemtext.csproj" />
|
<ProjectReference Include="..\MfGames.Markdown.Gemtext\MfGames.Markdown.Gemtext.csproj" />
|
||||||
|
<ProjectReference Include="..\MfGames.Markdown\MfGames.Markdown.csproj" />
|
||||||
<ProjectReference Include="..\MfGames.Nitride.Gemtext\MfGames.Nitride.Gemtext.csproj" />
|
<ProjectReference Include="..\MfGames.Nitride.Gemtext\MfGames.Nitride.Gemtext.csproj" />
|
||||||
<ProjectReference Include="..\MfGames.Nitride.Html\MfGames.Nitride.Html.csproj" />
|
<ProjectReference Include="..\MfGames.Nitride.Html\MfGames.Nitride.Html.csproj" />
|
||||||
<ProjectReference Include="..\MfGames.Nitride.Slugs\MfGames.Nitride.Slugs.csproj" />
|
<ProjectReference Include="..\MfGames.Nitride.Slugs\MfGames.Nitride.Slugs.csproj" />
|
||||||
|
|
90
src/MfGames.Nitride.Markdown/ParseMarkdownHeadingOne.cs
Normal file
90
src/MfGames.Nitride.Markdown/ParseMarkdownHeadingOne.cs
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
using FluentValidation;
|
||||||
|
using Markdig.Renderers.Roundtrip;
|
||||||
|
using Markdig.Syntax;
|
||||||
|
using MfGames.Gallium;
|
||||||
|
using MfGames.Nitride.Contents;
|
||||||
|
using MfGames.Nitride.Generators;
|
||||||
|
|
||||||
|
namespace MfGames.Nitride.Markdown;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An operation that parses the Markdown and converts the first heading one
|
||||||
|
/// into a model to include in the component. The rest of the Markdown is put
|
||||||
|
/// back as the text content of the entity.
|
||||||
|
/// </summary>
|
||||||
|
[WithProperties]
|
||||||
|
public partial class ParseMarkdownHeadingOne : IOperation
|
||||||
|
{
|
||||||
|
private readonly IValidator<ParseMarkdownHeadingOne> validator;
|
||||||
|
|
||||||
|
public ParseMarkdownHeadingOne(IValidator<ParseMarkdownHeadingOne> validator)
|
||||||
|
{
|
||||||
|
this.validator = validator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a callback for adding a heading to a given entity.
|
||||||
|
/// </summary>
|
||||||
|
public Func<Entity, string?, Entity>? AddModelCallback { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IEnumerable<Entity> Run(
|
||||||
|
IEnumerable<Entity> input,
|
||||||
|
CancellationToken cancellationToken = default
|
||||||
|
)
|
||||||
|
{
|
||||||
|
this.validator.ValidateAndThrow(this);
|
||||||
|
|
||||||
|
return input.SelectManyEntity<IsMarkdown>(x => x.Select(this.Parse));
|
||||||
|
}
|
||||||
|
|
||||||
|
private string? GetText(MarkdownObject? block)
|
||||||
|
{
|
||||||
|
if (block == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var writer = new StringWriter();
|
||||||
|
var renderer = new RoundtripRenderer(writer);
|
||||||
|
|
||||||
|
renderer.Write(block);
|
||||||
|
|
||||||
|
return writer.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Entity Parse(Entity entity)
|
||||||
|
{
|
||||||
|
// Get the text content of the file. No text, we don't do anything (but
|
||||||
|
// there is going to be text since we filtered on IsMarkdown).
|
||||||
|
string? oldText = entity.GetTextContentString();
|
||||||
|
|
||||||
|
if (oldText == null)
|
||||||
|
{
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the result as Markdown and pull out the heading. If we can't
|
||||||
|
// find one, then we just return the entity. We need to track trivia
|
||||||
|
// because we are round-tripping back to Markdown.
|
||||||
|
MarkdownDocument document = Markdig.Markdown.Parse(oldText, true);
|
||||||
|
Block? block = document.FirstOrDefault(block => block is HeadingBlock);
|
||||||
|
|
||||||
|
if (block is not HeadingBlock { Level: 1 } heading)
|
||||||
|
{
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
string? headingText = this.GetText(heading.Inline);
|
||||||
|
|
||||||
|
// Convert the heading into the model.
|
||||||
|
// Pull out the heading so we can write the rest back.
|
||||||
|
document.Remove(heading);
|
||||||
|
|
||||||
|
string newText = this.GetText(document)!;
|
||||||
|
|
||||||
|
// Allow the extending class to add the model to the entity and then
|
||||||
|
// set the text content to the new value before returning the results.
|
||||||
|
return this.AddModelCallback!.Invoke(entity, headingText).SetTextContent(newText);
|
||||||
|
}
|
||||||
|
}
|
55
src/MfGames.Nitride.Markdown/RewriteLinkToIndexPath.cs
Normal file
55
src/MfGames.Nitride.Markdown/RewriteLinkToIndexPath.cs
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Markdig.Syntax.Inlines;
|
||||||
|
using MfGames.Gallium;
|
||||||
|
|
||||||
|
namespace MfGames.Nitride.Markdown;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An operation that rewrites link paths to be directory indexes.
|
||||||
|
/// </summary>
|
||||||
|
public class RewriteLinkToIndexPath : IOperation
|
||||||
|
{
|
||||||
|
private static readonly Regex IndexRegex = new(@"/index\.(markdown|md|html|htm)$");
|
||||||
|
|
||||||
|
private readonly RewriteMarkdownLink rewrite;
|
||||||
|
|
||||||
|
public RewriteLinkToIndexPath(RewriteMarkdownLink rewrite)
|
||||||
|
{
|
||||||
|
this.rewrite = rewrite;
|
||||||
|
this.rewrite.OnLinkCallback = this.OnLinkCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IEnumerable<Entity> Run(
|
||||||
|
IEnumerable<Entity> input,
|
||||||
|
CancellationToken cancellationToken = default
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return this.rewrite.Run(input, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnLinkCallback(LinkInline link)
|
||||||
|
{
|
||||||
|
// Ignore blank links (they could happen).
|
||||||
|
string? url = link.Url;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(url))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We only want links that start with a period or are relative to the
|
||||||
|
// current directory. Ideally, we don't want links that are remote URLs.
|
||||||
|
// This is a simplistic version.
|
||||||
|
if (url.Contains("://"))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check to see what the path ends with something we can map.
|
||||||
|
if (IndexRegex.IsMatch(url))
|
||||||
|
{
|
||||||
|
link.Url = IndexRegex.Replace(url, "/");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
49
src/MfGames.Nitride.Markdown/RewriteMarkdownLink.cs
Normal file
49
src/MfGames.Nitride.Markdown/RewriteMarkdownLink.cs
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
using Markdig.Syntax.Inlines;
|
||||||
|
using MfGames.Gallium;
|
||||||
|
using MfGames.Markdown;
|
||||||
|
using MfGames.Nitride.Contents;
|
||||||
|
using MfGames.Nitride.Generators;
|
||||||
|
|
||||||
|
namespace MfGames.Nitride.Markdown;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An operation that turns rewrites Markdown links with a variable callback.
|
||||||
|
/// </summary>
|
||||||
|
[WithProperties]
|
||||||
|
public partial class RewriteMarkdownLink : IOperation
|
||||||
|
{
|
||||||
|
private readonly RewriteLinkTransformer transformer;
|
||||||
|
|
||||||
|
public RewriteMarkdownLink()
|
||||||
|
{
|
||||||
|
this.transformer = new RewriteLinkTransformer();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Action<LinkInline>? OnLinkCallback
|
||||||
|
{
|
||||||
|
get => this.transformer.OnLink;
|
||||||
|
set => this.transformer.OnLink = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IEnumerable<Entity> Run(
|
||||||
|
IEnumerable<Entity> input,
|
||||||
|
CancellationToken cancellationToken = default
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return input.SelectManyEntity<IsMarkdown>(x => x.Select(this.Transform));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This turns all links that start with a link into a single link while
|
||||||
|
/// removing all trailing links within the line. This is to simplify the
|
||||||
|
/// rendering of the link on page.
|
||||||
|
/// </summary>
|
||||||
|
private Entity Transform(Entity entity)
|
||||||
|
{
|
||||||
|
string input = entity.GetTextContentString()!;
|
||||||
|
string output = this.transformer.Transform(input)!;
|
||||||
|
|
||||||
|
return entity.SetTextContent(output);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
using FluentValidation;
|
||||||
|
|
||||||
|
namespace MfGames.Nitride.Markdown.Validators;
|
||||||
|
|
||||||
|
public class ParseMarkdownHeadingOneValidator : AbstractValidator<ParseMarkdownHeadingOne>
|
||||||
|
{
|
||||||
|
public ParseMarkdownHeadingOneValidator()
|
||||||
|
{
|
||||||
|
this.RuleFor(x => x.AddModelCallback).NotNull();
|
||||||
|
}
|
||||||
|
}
|
79
tests/MfGames.Markdown.Tests/RewriteLinkTransformer.cs
Normal file
79
tests/MfGames.Markdown.Tests/RewriteLinkTransformer.cs
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
using MfGames.TestSetup;
|
||||||
|
using Xunit;
|
||||||
|
using Xunit.Abstractions;
|
||||||
|
|
||||||
|
namespace MfGames.Markdown.Tests;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests the functionality of RewriteLinkTransformer.
|
||||||
|
/// </summary>
|
||||||
|
public class RewriteLinkTransformerTests : TestBase<TestContext>
|
||||||
|
{
|
||||||
|
public RewriteLinkTransformerTests(ITestOutputHelper output)
|
||||||
|
: base(output) { }
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ComplexExample()
|
||||||
|
{
|
||||||
|
string input = string.Join(
|
||||||
|
"\n",
|
||||||
|
"# A [Heading](/heading/index.md)",
|
||||||
|
"",
|
||||||
|
"That includes many [not-changing](https://example.org) examples",
|
||||||
|
"and ones that [will change](/gary/index.md).",
|
||||||
|
"",
|
||||||
|
"No one knows how this [won't change](/favicon.ico).",
|
||||||
|
""
|
||||||
|
);
|
||||||
|
|
||||||
|
string expected = string.Join(
|
||||||
|
"\n",
|
||||||
|
"# A [Heading](/heading/)",
|
||||||
|
"",
|
||||||
|
"That includes many [not-changing](https://example.org) examples",
|
||||||
|
"and ones that [will change](/gary/).",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"No one knows how this [won't change](/favicon.ico).",
|
||||||
|
""
|
||||||
|
);
|
||||||
|
|
||||||
|
RewriteLinkTransformer transformer =
|
||||||
|
new((link) => link.Url = link.Url?.Replace("/index.md", "/"));
|
||||||
|
|
||||||
|
string? output = transformer.Transform(input);
|
||||||
|
|
||||||
|
Assert.Equal(expected, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void HandleLink()
|
||||||
|
{
|
||||||
|
const string Input = "[Link Title](/link/index.md)";
|
||||||
|
|
||||||
|
RewriteLinkTransformer transformer =
|
||||||
|
new((link) => link.Url = link.Url?.Replace("/index.md", "/"));
|
||||||
|
|
||||||
|
string? output = transformer.Transform(Input);
|
||||||
|
|
||||||
|
Assert.Equal("[Link Title](/link/)", output);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void HandleNulls()
|
||||||
|
{
|
||||||
|
RewriteLinkTransformer transformer = new();
|
||||||
|
|
||||||
|
Assert.Null(transformer.Transform(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void HandleText()
|
||||||
|
{
|
||||||
|
const string Input = "Content";
|
||||||
|
RewriteLinkTransformer transformer = new();
|
||||||
|
string? output = transformer.Transform(Input);
|
||||||
|
|
||||||
|
Assert.Equal("Content", output);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,154 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using MfGames.Gallium;
|
||||||
|
using MfGames.Nitride.Contents;
|
||||||
|
using MfGames.TestSetup;
|
||||||
|
using Xunit;
|
||||||
|
using Xunit.Abstractions;
|
||||||
|
|
||||||
|
namespace MfGames.Nitride.Markdown.Tests;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests the functionality of the ParseMarkdownHeadingOne operation.
|
||||||
|
/// </summary>
|
||||||
|
public class ParseMarkdownHeadingOneTests : TestBase<MarkdownTestContext>
|
||||||
|
{
|
||||||
|
public ParseMarkdownHeadingOneTests(ITestOutputHelper output)
|
||||||
|
: base(output) { }
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ParseComplexHeader()
|
||||||
|
{
|
||||||
|
using MarkdownTestContext context = this.CreateContext();
|
||||||
|
|
||||||
|
List<Entity> input =
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
new Entity()
|
||||||
|
.Set(IsMarkdown.Instance)
|
||||||
|
.SetTextContent("# Heading [One](/test)\n\nContent\nsecond\n\nline"),
|
||||||
|
};
|
||||||
|
|
||||||
|
ParseMarkdownHeadingOne op = context
|
||||||
|
.Resolve<ParseMarkdownHeadingOne>()
|
||||||
|
.WithAddModelCallback((entity, heading) => entity.Set(heading));
|
||||||
|
|
||||||
|
IEnumerable<Entity> output = op.Run(input);
|
||||||
|
Entity first = output.First();
|
||||||
|
string content = first.GetTextContentString()!.Trim();
|
||||||
|
|
||||||
|
Assert.Equal("Content\nsecond\n\nline", content);
|
||||||
|
Assert.Equal("Heading [One](/test)", first.Get<string>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ParseEmptyHeader()
|
||||||
|
{
|
||||||
|
using MarkdownTestContext context = this.CreateContext();
|
||||||
|
|
||||||
|
List<Entity> input =
|
||||||
|
new() { new Entity().Set(IsMarkdown.Instance).SetTextContent("#\nContent"), };
|
||||||
|
|
||||||
|
ParseMarkdownHeadingOne op = context
|
||||||
|
.Resolve<ParseMarkdownHeadingOne>()
|
||||||
|
.WithAddModelCallback((entity, heading) => entity.Set(heading));
|
||||||
|
|
||||||
|
IEnumerable<Entity> output = op.Run(input);
|
||||||
|
Entity first = output.First();
|
||||||
|
string content = first.GetTextContentString()!.Trim();
|
||||||
|
|
||||||
|
Assert.Equal("Content", content);
|
||||||
|
Assert.Equal("", first.Get<string>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ParseHeaderTwo()
|
||||||
|
{
|
||||||
|
using MarkdownTestContext context = this.CreateContext();
|
||||||
|
|
||||||
|
List<Entity> input =
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
new Entity().Set(IsMarkdown.Instance).SetTextContent("## Heading Two\nContent"),
|
||||||
|
};
|
||||||
|
|
||||||
|
ParseMarkdownHeadingOne op = context
|
||||||
|
.Resolve<ParseMarkdownHeadingOne>()
|
||||||
|
.WithAddModelCallback((entity, heading) => entity.Set(heading));
|
||||||
|
|
||||||
|
IEnumerable<Entity> output = op.Run(input);
|
||||||
|
Entity first = output.First();
|
||||||
|
string content = first.GetTextContentString()!.Trim();
|
||||||
|
|
||||||
|
Assert.Equal("## Heading Two\nContent", content);
|
||||||
|
Assert.False(first.Has<string>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ParseMultipleHeaderOne()
|
||||||
|
{
|
||||||
|
using MarkdownTestContext context = this.CreateContext();
|
||||||
|
|
||||||
|
List<Entity> input =
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
new Entity()
|
||||||
|
.Set(IsMarkdown.Instance)
|
||||||
|
.SetTextContent("# Heading One\n\n# Heading Two\n Content"),
|
||||||
|
};
|
||||||
|
|
||||||
|
ParseMarkdownHeadingOne op = context
|
||||||
|
.Resolve<ParseMarkdownHeadingOne>()
|
||||||
|
.WithAddModelCallback((entity, heading) => entity.Set(heading));
|
||||||
|
|
||||||
|
IEnumerable<Entity> output = op.Run(input);
|
||||||
|
Entity first = output.First();
|
||||||
|
string content = first.GetTextContentString()!.Trim();
|
||||||
|
|
||||||
|
Assert.Equal("# Heading Two\n Content", content);
|
||||||
|
Assert.Equal("Heading One", first.Get<string>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ParseNoHeader()
|
||||||
|
{
|
||||||
|
using MarkdownTestContext context = this.CreateContext();
|
||||||
|
|
||||||
|
List<Entity> input =
|
||||||
|
new() { new Entity().Set(IsMarkdown.Instance).SetTextContent("Content"), };
|
||||||
|
|
||||||
|
ParseMarkdownHeadingOne op = context
|
||||||
|
.Resolve<ParseMarkdownHeadingOne>()
|
||||||
|
.WithAddModelCallback((entity, heading) => entity.Set(heading));
|
||||||
|
|
||||||
|
IEnumerable<Entity> output = op.Run(input);
|
||||||
|
Entity first = output.First();
|
||||||
|
string content = first.GetTextContentString()!.Trim();
|
||||||
|
|
||||||
|
Assert.Equal("Content", content);
|
||||||
|
Assert.False(first.Has<string>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ParseSimpleHeader()
|
||||||
|
{
|
||||||
|
using MarkdownTestContext context = this.CreateContext();
|
||||||
|
|
||||||
|
List<Entity> input =
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
new Entity().Set(IsMarkdown.Instance).SetTextContent("# Heading One\n\nContent"),
|
||||||
|
};
|
||||||
|
|
||||||
|
ParseMarkdownHeadingOne op = context
|
||||||
|
.Resolve<ParseMarkdownHeadingOne>()
|
||||||
|
.WithAddModelCallback((entity, heading) => entity.Set(heading));
|
||||||
|
|
||||||
|
IEnumerable<Entity> output = op.Run(input);
|
||||||
|
Entity first = output.First();
|
||||||
|
string content = first.GetTextContentString()!.Trim();
|
||||||
|
|
||||||
|
Assert.Equal("Content", content);
|
||||||
|
Assert.Equal("Heading One", first.Get<string>());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using MfGames.Gallium;
|
||||||
|
using MfGames.Nitride.Contents;
|
||||||
|
using MfGames.TestSetup;
|
||||||
|
using Xunit;
|
||||||
|
using Xunit.Abstractions;
|
||||||
|
|
||||||
|
namespace MfGames.Nitride.Markdown.Tests;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests the functionality of the RewriteLinkToIndexPath operation.
|
||||||
|
/// </summary>
|
||||||
|
public class RewriteLinkToIndexPathTests : TestBase<MarkdownTestContext>
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public RewriteLinkToIndexPathTests(ITestOutputHelper output)
|
||||||
|
: base(output) { }
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void NoRewriteSimpleIndexPhp()
|
||||||
|
{
|
||||||
|
using MarkdownTestContext context = this.CreateContext();
|
||||||
|
|
||||||
|
List<Entity> input =
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
new Entity().Set(IsMarkdown.Instance).SetTextContent("[Link Title](/index.php)"),
|
||||||
|
};
|
||||||
|
|
||||||
|
var op = context.Resolve<RewriteLinkToIndexPath>();
|
||||||
|
|
||||||
|
IEnumerable<Entity> output = op.Run(input);
|
||||||
|
Entity first = output.First();
|
||||||
|
string content = first.GetTextContentString()!.Trim();
|
||||||
|
|
||||||
|
Assert.Equal("[Link Title](/index.php)", content);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RewritePeriodSlashRelativePath()
|
||||||
|
{
|
||||||
|
using MarkdownTestContext context = this.CreateContext();
|
||||||
|
|
||||||
|
List<Entity> input =
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
new Entity()
|
||||||
|
.Set(IsMarkdown.Instance)
|
||||||
|
.SetTextContent("[Link Title](./bob/index.md)"),
|
||||||
|
};
|
||||||
|
|
||||||
|
var op = context.Resolve<RewriteLinkToIndexPath>();
|
||||||
|
|
||||||
|
IEnumerable<Entity> output = op.Run(input);
|
||||||
|
Entity first = output.First();
|
||||||
|
string content = first.GetTextContentString()!.Trim();
|
||||||
|
|
||||||
|
Assert.Equal("[Link Title](./bob/)", content);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RewriteRemoteIndex()
|
||||||
|
{
|
||||||
|
using MarkdownTestContext context = this.CreateContext();
|
||||||
|
|
||||||
|
List<Entity> input =
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
new Entity()
|
||||||
|
.Set(IsMarkdown.Instance)
|
||||||
|
.SetTextContent("[Link Title](https://example.org/index.md)"),
|
||||||
|
};
|
||||||
|
|
||||||
|
var op = context.Resolve<RewriteLinkToIndexPath>();
|
||||||
|
|
||||||
|
IEnumerable<Entity> output = op.Run(input);
|
||||||
|
Entity first = output.First();
|
||||||
|
string content = first.GetTextContentString()!.Trim();
|
||||||
|
|
||||||
|
Assert.Equal("[Link Title](https://example.org/index.md)", content);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RewriteSimpleIndexMarkdown()
|
||||||
|
{
|
||||||
|
using MarkdownTestContext context = this.CreateContext();
|
||||||
|
|
||||||
|
List<Entity> input =
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
new Entity().Set(IsMarkdown.Instance).SetTextContent("[Link Title](/index.md)"),
|
||||||
|
};
|
||||||
|
|
||||||
|
var op = context.Resolve<RewriteLinkToIndexPath>();
|
||||||
|
|
||||||
|
IEnumerable<Entity> output = op.Run(input);
|
||||||
|
Entity first = output.First();
|
||||||
|
string content = first.GetTextContentString()!.Trim();
|
||||||
|
|
||||||
|
Assert.Equal("[Link Title](/)", content);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue