To find a closing `|` at the end of the last column, GridTableParser used PeekCharExtra, which ignores the end of the slice. When the line was shorter than the table, it could read the next line instead, and a `|` there made the cell run past the end of the line, so the next line's leading `|` ended up in the cell. Use PeekChar, which stays within the current line.
586 lines
19 KiB
C#
586 lines
19 KiB
C#
using System.Text.RegularExpressions;
|
||
|
||
using Markdig.Extensions.AutoLinks;
|
||
using Markdig.Extensions.Tables;
|
||
using Markdig.Syntax;
|
||
using NUnit.Framework;
|
||
|
||
namespace Markdig.Tests;
|
||
|
||
public class MiscTests
|
||
{
|
||
[Test]
|
||
public void LinkWithInvalidNonAsciiDomainNameIsIgnored()
|
||
{
|
||
// Url from https://github.com/lunet-io/markdig/issues/438
|
||
_ = Markdown.ToHtml("[minulém díle](http://V%20minulém%20díle%20jsme%20nainstalovali%20SQL%20Server,%20který%20je%20nutný%20pro%20běh%20Configuration%20Manageru.%20Dnes%20nás%20čeká%20instalace%20WSUS,%20což%20je%20produkt,%20jež%20je%20možné%20používat%20i%20jako%20samostatnou%20funkci%20ve%20Windows%20Serveru,%20který%20se%20stará%20o%20stažení%20a%20instalaci%20aktualizací%20z%20Microsoft%20Update%20na%20klientské%20počítače.%20Stejně%20jako%20v%20předchozích%20dílech,%20tak%20i%20v%20tomto%20si%20ukážeme%20obě%20varianty%20instalace%20–%20a%20to%20jak%20instalaci%20z%20PowerShellu,%20tak%20instalaci%20pomocí%20GUI.) ");
|
||
|
||
// Valid IDN
|
||
TestParser.TestSpec("[foo](http://ünicode.com)", "<p><a href=\"http://xn--nicode-2ya.com\">foo</a></p>");
|
||
TestParser.TestSpec("[foo](http://ünicode.ünicode.com)", "<p><a href=\"http://xn--nicode-2ya.xn--nicode-2ya.com\">foo</a></p>");
|
||
|
||
// Invalid IDN
|
||
TestParser.TestSpec("[foo](http://ünicode..com)", "<p><a href=\"http://%C3%BCnicode..com\">foo</a></p>");
|
||
}
|
||
|
||
[TestCase("link [foo [bar]]")] // https://spec.commonmark.org/0.29/#example-508
|
||
[TestCase("link [foo][bar]")]
|
||
[TestCase("link [][foo][bar][]")]
|
||
[TestCase("link [][foo][bar][[]]")]
|
||
[TestCase("link [foo] [bar]")]
|
||
[TestCase("link [[foo] [] [bar] [[abc]def]]")]
|
||
[TestCase("[]")]
|
||
[TestCase("[ ]")]
|
||
[TestCase("[bar][]")]
|
||
[TestCase("[bar][ foo]")]
|
||
[TestCase("[bar][foo ][]")]
|
||
[TestCase("[bar][fo[ ]o ][][]")]
|
||
[TestCase("[a]b[c[d[e]f]g]h")]
|
||
[TestCase("a[b[c[d]e]f[g]h]i foo [j]k[l[m]n]o")]
|
||
[TestCase("a[b[c[d]e]f[g]h]i[] [][foo][bar][] foo [j]k[l[m]n]o")]
|
||
[TestCase("a[b[c[d]e]f[g]h]i foo [j]k[l[m]n]o[][]")]
|
||
public void LinkTextMayContainBalancedBrackets(string linkText)
|
||
{
|
||
string markdown = $"[{linkText}](/uri)";
|
||
string expected = $@"<p><a href=""/uri"">{linkText}</a></p>";
|
||
|
||
TestParser.TestSpec(markdown, expected);
|
||
|
||
// Make the link text unbalanced
|
||
foreach (var bracketIndex in linkText
|
||
.Select((c, i) => new Tuple<char, int>(c, i))
|
||
.Where(t => t.Item1 == '[' || t.Item1 == ']')
|
||
.Select(t => t.Item2))
|
||
{
|
||
string brokenLinkText = linkText.Remove(bracketIndex, 1);
|
||
|
||
markdown = $"[{brokenLinkText}](/uri)";
|
||
expected = $@"<p><a href=""/uri"">{brokenLinkText}</a></p>";
|
||
|
||
string actual = Markdown.ToHtml(markdown);
|
||
Assert.AreNotEqual(expected, actual);
|
||
}
|
||
}
|
||
|
||
[Theory]
|
||
[TestCase('[', 9 * 1024, true, false)]
|
||
[TestCase('[', 11 * 1024, true, true)]
|
||
[TestCase('[', 100, false, false)]
|
||
[TestCase('[', 150, false, true)]
|
||
[TestCase('>', 100, true, false)]
|
||
[TestCase('>', 150, true, true)]
|
||
public void GuardsAgainstHighlyNestedNodes(char c, int count, bool parseOnly, bool shouldThrow)
|
||
{
|
||
var markdown = new string(c, count);
|
||
Action test = parseOnly ? () => Markdown.Parse(markdown) : () => Markdown.ToHtml(markdown);
|
||
|
||
if (shouldThrow)
|
||
{
|
||
Exception e = Assert.Throws<ArgumentException>(test);
|
||
Assert.True(e.Message.Contains("depth limit"));
|
||
}
|
||
else
|
||
{
|
||
test();
|
||
}
|
||
}
|
||
|
||
[Test]
|
||
public void MaximumNestingDepthCanBeRaisedForDeepListExtras()
|
||
{
|
||
var markdown = "Krankenhaus\nD. " + string.Join(" ", Enumerable.Repeat("M.", 160));
|
||
var pipeline = new MarkdownPipelineBuilder
|
||
{
|
||
MaximumNestingDepth = 512,
|
||
}.UseListExtras().Build();
|
||
|
||
Assert.DoesNotThrow(() => Markdown.ToHtml(markdown, pipeline));
|
||
}
|
||
|
||
[Test]
|
||
public void MaximumNestingDepthCanBeLowered()
|
||
{
|
||
var pipeline = new MarkdownPipelineBuilder
|
||
{
|
||
MaximumNestingDepth = 16,
|
||
}.Build();
|
||
|
||
Exception e = Assert.Throws<ArgumentException>(() => Markdown.ToHtml(new string('>', 20), pipeline));
|
||
Assert.True(e.Message.Contains("depth limit"));
|
||
}
|
||
|
||
[Test]
|
||
public void IsIssue356Corrected()
|
||
{
|
||
string input = @"https://foo.bar/path/\#m4mv5W0GYKZpGvfA.97";
|
||
string expected = @"<p><a href=""https://foo.bar/path/%5C#m4mv5W0GYKZpGvfA.97"">https://foo.bar/path/\#m4mv5W0GYKZpGvfA.97</a></p>";
|
||
|
||
TestParser.TestSpec($"<{input}>", expected);
|
||
TestParser.TestSpec(input, expected, "autolinks|advanced");
|
||
}
|
||
|
||
[Test]
|
||
public void IsIssue365Corrected()
|
||
{
|
||
// The scheme must be escaped too...
|
||
string input = "";
|
||
string expected = "<p><img src=\"%22onclick=%22alert&#40;%27click%27&#41;%22://\" alt=\"image\" /></p>";
|
||
|
||
TestParser.TestSpec(input, expected);
|
||
}
|
||
|
||
[Test]
|
||
public void TestAltTextIsCorrectlyEscaped()
|
||
{
|
||
TestParser.TestSpec(
|
||
@"",
|
||
@"<p><img src=""girl.png"" alt=""This is image alt text with quotation ' and double quotation "hello" world"" /></p>");
|
||
}
|
||
|
||
[Test]
|
||
public void TestFixHang()
|
||
{
|
||
var input = File.ReadAllText(Path.Combine(TestParser.TestsDirectory, "hang.md"));
|
||
_ = Markdown.ToHtml(input);
|
||
}
|
||
|
||
[Test]
|
||
public void TestInvalidHtmlEntity()
|
||
{
|
||
var input = "9&ddr;&*&ddr;&de��__";
|
||
TestParser.TestSpec(input, "<p>9&ddr;&*&ddr;&de��__</p>");
|
||
}
|
||
|
||
[Test]
|
||
public void TestInvalidCharacterHandling()
|
||
{
|
||
var input = File.ReadAllText(Path.Combine(TestParser.TestsDirectory, "ArgumentOutOfRangeException.md"));
|
||
_ = Markdown.ToHtml(input);
|
||
}
|
||
|
||
[Test]
|
||
public void TestInvalidCodeEscape()
|
||
{
|
||
var input = "```**Header** ";
|
||
_ = Markdown.ToHtml(input);
|
||
}
|
||
|
||
[Test]
|
||
public void TestEmphasisAndHtmlEntity()
|
||
{
|
||
var markdownText = "*Unlimited-Fun®*®";
|
||
TestParser.TestSpec(markdownText, "<p><em>Unlimited-Fun®</em>®</p>");
|
||
}
|
||
|
||
[Test]
|
||
public void TestThematicInsideCodeBlockInsideList()
|
||
{
|
||
var input = @"1. In the :
|
||
|
||
```
|
||
Id DisplayName Description
|
||
-- ----------- -----------
|
||
62375ab9-6b52-47ed-826b-58e47e0e304b Group.Unified ...
|
||
```";
|
||
TestParser.TestSpec(input, @"<ol>
|
||
<li><p>In the :</p>
|
||
<pre><code>Id DisplayName Description
|
||
-- ----------- -----------
|
||
62375ab9-6b52-47ed-826b-58e47e0e304b Group.Unified ...
|
||
</code></pre></li>
|
||
</ol>");
|
||
}
|
||
|
||
[Test]
|
||
public void VisualizeMathExpressions()
|
||
{
|
||
string math = @"Math expressions
|
||
|
||
$\frac{n!}{k!(n-k)!} = \binom{n}{k}$
|
||
|
||
$$\frac{n!}{k!(n-k)!} = \binom{n}{k}$$
|
||
|
||
$$
|
||
\frac{n!}{k!(n-k)!} = \binom{n}{k}
|
||
$$
|
||
|
||
<div class=""math"">
|
||
\begin{align}
|
||
\sqrt{37} & = \sqrt{\frac{73^2-1}{12^2}} \\
|
||
& = \sqrt{\frac{73^2}{12^2}\cdot\frac{73^2-1}{73^2}} \\
|
||
& = \sqrt{\frac{73^2}{12^2}}\sqrt{\frac{73^2-1}{73^2}} \\
|
||
& = \frac{73}{12}\sqrt{1 - \frac{1}{73^2}} \\
|
||
& \approx \frac{73}{12}\left(1 - \frac{1}{2\cdot73^2}\right)
|
||
\end{align}
|
||
</div>
|
||
";
|
||
//Console.WriteLine("Math Expressions:\n");
|
||
var pl = new MarkdownPipelineBuilder().UseMathematics().Build(); // UseEmphasisExtras(EmphasisExtraOptions.Subscript).Build()
|
||
var html = Markdown.ToHtml(math, pl);
|
||
//Console.WriteLine(html);
|
||
}
|
||
|
||
[Test]
|
||
public void InlineMathExpression()
|
||
{
|
||
string math = @"Math expressions
|
||
|
||
$\frac{n!}{k!(n-k)!} = \binom{n}{k}$
|
||
";
|
||
var pl = new MarkdownPipelineBuilder().UseMathematics().Build(); // UseEmphasisExtras(EmphasisExtraOptions.Subscript).Build()
|
||
|
||
var html = Markdown.ToHtml(math, pl);
|
||
|
||
var test1 = html.Contains("<p><span class=\"math\">\\(");
|
||
var test2 = html.Contains("\\)</span></p>");
|
||
if (!test1 || !test2)
|
||
{
|
||
Console.WriteLine(html);
|
||
}
|
||
|
||
Assert.IsTrue(test1, "Leading bracket missing");
|
||
Assert.IsTrue(test2, "Trailing bracket missing");
|
||
}
|
||
|
||
[Test]
|
||
public void BlockMathExpression()
|
||
{
|
||
string math = @"Math expressions
|
||
|
||
$$
|
||
\frac{n!}{k!(n-k)!} = \binom{n}{k}
|
||
$$
|
||
";
|
||
var pl = new MarkdownPipelineBuilder().UseMathematics().Build(); // UseEmphasisExtras(EmphasisExtraOptions.Subscript).Build()
|
||
|
||
var html = Markdown.ToHtml(math, pl);
|
||
var test1 = html.Contains("<div class=\"math\">\n\\[");
|
||
var test2 = html.Contains("\\]</div>");
|
||
if (!test1 || !test2)
|
||
{
|
||
Console.WriteLine(html);
|
||
}
|
||
|
||
Assert.IsTrue(test1, "Leading bracket missing");
|
||
Assert.IsTrue(test2, "Trailing bracket missing");
|
||
}
|
||
|
||
[Test]
|
||
public void CanDisableParsingHeadings()
|
||
{
|
||
var noHeadingsPipeline = new MarkdownPipelineBuilder().DisableHeadings().Build();
|
||
|
||
TestParser.TestSpec("Foo\n===", "<h1>Foo</h1>");
|
||
TestParser.TestSpec("Foo\n===", "<p>Foo\n===</p>", noHeadingsPipeline);
|
||
|
||
TestParser.TestSpec("# Heading 1", "<h1>Heading 1</h1>");
|
||
TestParser.TestSpec("# Heading 1", "<p># Heading 1</p>", noHeadingsPipeline);
|
||
|
||
// Does not also disable link reference definitions
|
||
TestParser.TestSpec("[Foo]\n\n[Foo]: bar", "<p><a href=\"bar\">Foo</a></p>");
|
||
TestParser.TestSpec("[Foo]\n\n[Foo]: bar", "<p><a href=\"bar\">Foo</a></p>", noHeadingsPipeline);
|
||
}
|
||
|
||
[Test]
|
||
public void CanOpenAutoLinksInNewWindow()
|
||
{
|
||
var pipeline = new MarkdownPipelineBuilder().UseAutoLinks().Build();
|
||
var newWindowPipeline = new MarkdownPipelineBuilder().UseAutoLinks(new AutoLinkOptions() { OpenInNewWindow = true }).Build();
|
||
|
||
TestParser.TestSpec("www.foo.bar", "<p><a href=\"http://www.foo.bar\">www.foo.bar</a></p>", pipeline);
|
||
TestParser.TestSpec("www.foo.bar", "<p><a href=\"http://www.foo.bar\" target=\"_blank\">www.foo.bar</a></p>", newWindowPipeline);
|
||
}
|
||
|
||
[Test]
|
||
public void CanUseHttpsPrefixForWWWAutoLinks()
|
||
{
|
||
var pipeline = new MarkdownPipelineBuilder().UseAutoLinks().Build();
|
||
var httpsPipeline = new MarkdownPipelineBuilder().UseAutoLinks(new AutoLinkOptions() { UseHttpsForWWWLinks = true }).Build();
|
||
|
||
TestParser.TestSpec("www.foo.bar", "<p><a href=\"http://www.foo.bar\">www.foo.bar</a></p>", pipeline);
|
||
TestParser.TestSpec("www.foo.bar", "<p><a href=\"https://www.foo.bar\">www.foo.bar</a></p>", httpsPipeline);
|
||
}
|
||
|
||
[Test]
|
||
public void RootInlineHasCorrectSourceSpan()
|
||
{
|
||
var pipeline = new MarkdownPipelineBuilder().UsePreciseSourceLocation().Build();
|
||
pipeline.TrackTrivia = true;
|
||
|
||
var document = Markdown.Parse("0123456789\n", pipeline);
|
||
|
||
var expectedSourceSpan = new SourceSpan(0, 10);
|
||
Assert.That(((LeafBlock)document.LastChild).Inline.Span == expectedSourceSpan);
|
||
}
|
||
|
||
[Test]
|
||
public void RootInlineInTableCellHasCorrectSourceSpan()
|
||
{
|
||
var pipeline = new MarkdownPipelineBuilder().UsePreciseSourceLocation().UseAdvancedExtensions().Build();
|
||
pipeline.TrackTrivia = true;
|
||
|
||
var document = Markdown.Parse("| a | b |\n| --- | --- |\n| <span id=\"dest\"></span><span id=\"DEST\"></span>*dest*<br/> | \\[in\\] The address of the result of the operation.<br/> |", pipeline);
|
||
|
||
var paragraph = (ParagraphBlock)((TableCell)((TableRow)((Table)document.LastChild).LastChild).First()).LastChild;
|
||
Assert.That(paragraph.Inline.Span.Start == paragraph.Inline.FirstChild.Span.Start);
|
||
Assert.That(paragraph.Inline.Span.End == paragraph.Inline.LastChild.Span.End);
|
||
}
|
||
|
||
[Test]
|
||
public void TestGridTableShortLine()
|
||
{
|
||
var input = @"
|
||
+--+
|
||
| |
|
||
+-";
|
||
|
||
var expected = @"<table>
|
||
<col style=""width:100%"" />
|
||
<tbody>
|
||
<tr>
|
||
<td></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
";
|
||
TestParser.TestSpec(input, expected, new MarkdownPipelineBuilder().UseGridTables().Build());
|
||
}
|
||
|
||
[Test]
|
||
public void TestGridTableLastCellWithoutClosingPipe()
|
||
{
|
||
// The last cell is open and shorter than the column, and the next line has
|
||
// a `|` where this one would end: https://github.com/xoofx/markdig/issues/494
|
||
var input = @"
|
||
+-----+----------+
|
||
| 6 | 1
|
||
| |
|
||
+-----+----------+";
|
||
|
||
var expected = @"<table>
|
||
<col style=""width:33.33%"" />
|
||
<col style=""width:66.67%"" />
|
||
<tbody>
|
||
<tr>
|
||
<td>6</td>
|
||
<td>1</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
";
|
||
TestParser.TestSpec(input, expected, new MarkdownPipelineBuilder().UseGridTables().Build());
|
||
}
|
||
|
||
[Test]
|
||
public void TestDefinitionListInListItemWithBlankLine()
|
||
{
|
||
var input = @"
|
||
-
|
||
|
||
term
|
||
: definition
|
||
";
|
||
|
||
var expected = @"<ul>
|
||
<li>
|
||
<dl>
|
||
<dt>term</dt>
|
||
<dd>definition</dd>
|
||
</dl>
|
||
</li>
|
||
</ul>
|
||
";
|
||
TestParser.TestSpec(input, expected, new MarkdownPipelineBuilder().UseDefinitionLists().Build());
|
||
}
|
||
|
||
[Test]
|
||
public void TestAlertWithinAlertOrNestedBlock()
|
||
{
|
||
var input = @"
|
||
>[!NOTE]
|
||
[!NOTE]
|
||
The second one is not a note.
|
||
|
||
>>[!NOTE]
|
||
Also not a note.
|
||
";
|
||
|
||
var expected = @"<div class=""markdown-alert markdown-alert-note"">
|
||
<p class=""markdown-alert-title""><svg viewBox=""0 0 16 16"" version=""1.1"" width=""16"" height=""16"" aria-hidden=""true""><path d=""M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z""></path></svg>Note</p>
|
||
<p>[!NOTE]
|
||
The second one is not a note.</p>
|
||
</div>
|
||
<blockquote>
|
||
<blockquote>
|
||
<p>[!NOTE]
|
||
Also not a note.</p>
|
||
</blockquote>
|
||
</blockquote>
|
||
";
|
||
TestParser.TestSpec(input, expected, new MarkdownPipelineBuilder().UseAlertBlocks().Build());
|
||
}
|
||
|
||
[Test]
|
||
public void TestNestedAlertInsideBlockquote()
|
||
{
|
||
// >>[!NOTE] should become a blockquote wrapping an alert when AllowNestedAlerts is enabled
|
||
var input = @">>[!NOTE]
|
||
Also a note.
|
||
";
|
||
|
||
var expected = @"<blockquote>
|
||
<div class=""markdown-alert markdown-alert-note"">
|
||
<p class=""markdown-alert-title""><svg viewBox=""0 0 16 16"" version=""1.1"" width=""16"" height=""16"" aria-hidden=""true""><path d=""M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z""></path></svg>Note</p>
|
||
<p>Also a note.</p>
|
||
</div>
|
||
</blockquote>
|
||
";
|
||
TestParser.TestSpec(input, expected, new MarkdownPipelineBuilder().UseAlertBlocks(allowNestedAlerts: true).Build());
|
||
}
|
||
|
||
[Test]
|
||
public void TestNestedAlertInsideAlert()
|
||
{
|
||
// An alert inside another alert is never recognized, even with AllowNestedAlerts
|
||
var input = @">[!NOTE]
|
||
> >[!TIP]
|
||
> > A tip inside a note
|
||
";
|
||
|
||
var expected = @"<div class=""markdown-alert markdown-alert-note"">
|
||
<p class=""markdown-alert-title""><svg viewBox=""0 0 16 16"" version=""1.1"" width=""16"" height=""16"" aria-hidden=""true""><path d=""M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z""></path></svg>Note</p>
|
||
<p></p>
|
||
<blockquote>
|
||
<p>[!TIP]
|
||
A tip inside a note</p>
|
||
</blockquote>
|
||
</div>
|
||
";
|
||
TestParser.TestSpec(input, expected, new MarkdownPipelineBuilder().UseAlertBlocks(allowNestedAlerts: true).Build());
|
||
}
|
||
|
||
[Test]
|
||
public void TestAlertInsideListItem()
|
||
{
|
||
// Alerts inside list items require AllowNestedAlerts
|
||
var input = @"- > [!NOTE]
|
||
> A note inside a list item
|
||
";
|
||
|
||
var expected = @"<ul>
|
||
<li>
|
||
<div class=""markdown-alert markdown-alert-note"">
|
||
<p class=""markdown-alert-title""><svg viewBox=""0 0 16 16"" version=""1.1"" width=""16"" height=""16"" aria-hidden=""true""><path d=""M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z""></path></svg>Note</p>
|
||
<p>A note inside a list item</p>
|
||
</div>
|
||
</li>
|
||
</ul>
|
||
";
|
||
TestParser.TestSpec(input, expected, new MarkdownPipelineBuilder().UseAlertBlocks(allowNestedAlerts: true).Build());
|
||
}
|
||
|
||
[Test]
|
||
public void TestAlertInsideNestedListItem()
|
||
{
|
||
// Alert inside a nested list item (list item indented under another list item) requires AllowNestedAlerts
|
||
var input = @"- list item 1
|
||
- list item 2
|
||
- > [!NOTE]
|
||
> A note inside a nested list item
|
||
";
|
||
|
||
var expected = @"<ul>
|
||
<li>list item 1</li>
|
||
<li>list item 2
|
||
<ul>
|
||
<li>
|
||
<div class=""markdown-alert markdown-alert-note"">
|
||
<p class=""markdown-alert-title""><svg viewBox=""0 0 16 16"" version=""1.1"" width=""16"" height=""16"" aria-hidden=""true""><path d=""M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z""></path></svg>Note</p>
|
||
<p>A note inside a nested list item</p>
|
||
</div>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
";
|
||
TestParser.TestSpec(input, expected, new MarkdownPipelineBuilder().UseAlertBlocks(allowNestedAlerts: true).Build());
|
||
}
|
||
|
||
[Test]
|
||
public void TestIssue887ListAfterNestedBlockQuote()
|
||
{
|
||
var input = @"> >* _Unordered 1_ QL2 level 1 no space sep
|
||
> > Continued 1
|
||
> > * Unordered 2 level 1
|
||
> > 1. **Ordered 1** QL2 level 1
|
||
> > Continued 1
|
||
> > > Not continued QL3
|
||
> > 2. Ordered 2 QL2 level 1
|
||
> > Continued 2a
|
||
> >
|
||
> > Continued 2b
|
||
> >
|
||
> > Continued 2c
|
||
> >
|
||
> > 3. Ordered 3 QL2 level 2
|
||
> > Continued 3a
|
||
> >
|
||
> > Continued 3b
|
||
";
|
||
|
||
var expected = @"<blockquote>
|
||
<blockquote>
|
||
<ul>
|
||
<li><em>Unordered 1</em> QL2 level 1 no space sep
|
||
Continued 1</li>
|
||
<li>Unordered 2 level 1</li>
|
||
</ul>
|
||
<ol>
|
||
<li><strong>Ordered 1</strong> QL2 level 1
|
||
Continued 1</li>
|
||
</ol>
|
||
<blockquote>
|
||
<p>Not continued QL3</p>
|
||
</blockquote>
|
||
<ol start=""2"">
|
||
<li><p>Ordered 2 QL2 level 1
|
||
Continued 2a</p>
|
||
<p>Continued 2b</p>
|
||
<p>Continued 2c</p>
|
||
<ol start=""3"">
|
||
<li><p>Ordered 3 QL2 level 2
|
||
Continued 3a</p>
|
||
<p>Continued 3b</p>
|
||
</li>
|
||
</ol>
|
||
</li>
|
||
</ol>
|
||
</blockquote>
|
||
</blockquote>";
|
||
|
||
TestParser.TestSpec(input, expected);
|
||
}
|
||
|
||
[Test]
|
||
public void TestIssue845ListItemBlankLine()
|
||
{
|
||
TestParser.TestSpec("-\n\n foo",@"
|
||
<ul>
|
||
<li></li>
|
||
</ul>
|
||
<p>foo</p>");
|
||
TestParser.TestSpec("-\n-\n\n foo",@"
|
||
<ul>
|
||
<li></li>
|
||
<li></li>
|
||
</ul>
|
||
<p>foo</p>");
|
||
TestParser.TestSpec("-\n\n-\n\n foo",@"
|
||
<ul>
|
||
<li></li>
|
||
<li></li>
|
||
</ul>
|
||
<p>foo</p>");
|
||
}
|
||
}
|