90 lines
5.7 KiB
Go
90 lines
5.7 KiB
Go
package test
|
|
|
|
import (
|
|
domainArticle "RenewCMS/internal/domain/article"
|
|
"RenewCMS/internal/infrastructure/markdown"
|
|
"errors"
|
|
"testing"
|
|
)
|
|
|
|
func TestMarkdownConverterFidelity(t *testing.T) {
|
|
converter := markdown.NewConverter()
|
|
examples := []struct {
|
|
name, html, markdown string
|
|
}{
|
|
{"headings", "<h1>T</h1><h3>S</h3>", "# T\n\n### S"},
|
|
{"bold and italic", "<p><strong>b</strong> <em>i</em></p>", "**b** *i*"},
|
|
{"link", `<p><a href="https://example.com/x">link</a></p>`, "[link](https://example.com/x)"},
|
|
{"bullet list", "<ul><li><p>a</p></li><li><p>b</p></li></ul>", "- a\n- b"},
|
|
{"ordered list", "<ol><li><p>a</p></li><li><p>b</p></li></ol>", "1. a\n2. b"},
|
|
{"data-indent dropped", `<ul><li data-indent="2"><p>a</p></li></ul>`, "- a"},
|
|
{"nested list", "<ul><li><p>a</p><ul><li><p>n</p></li></ul></li></ul>", "- a\n - n"},
|
|
{"blockquote", "<blockquote><p>q</p></blockquote>", "> q"},
|
|
{"code block", `<pre><code class="language-go">x := 1</code></pre>`, "```go\nx := 1\n```"},
|
|
{"rule", "<hr>", "---"},
|
|
{"image", `<img src="/static/uploadedImages/a.png" alt="A">`, ""},
|
|
{"script and style removed", "<p>ok</p><script>alert(1)</script><style>p{}</style>", "ok"},
|
|
{"unsupported span stripped", `<p>x<span style="color:red">y</span></p>`, "xy"},
|
|
{"inline code", "<p>use <code>go test</code></p>", "use `go test`"},
|
|
{"empty body", "", ""},
|
|
{"line break", "<p>a<br>b</p>", "a\\\nb"},
|
|
{"literal angle bracket is escaped", "<p><script></p>", "\\<script>"},
|
|
{"javascript link dropped", `<p><a href="javascript:alert(1)">x</a></p>`, "x"},
|
|
{"bold keeps outer spaces", "<p>a<strong> b </strong>c</p>", "a **b** c"},
|
|
{"loose list item", "<ul><li><p>a</p><p>b</p></li></ul>", "- a\n\n b"},
|
|
{"inline code with edge backtick", "<p><code>`x</code></p>", "`` `x ``"},
|
|
{"code block keeps br and drops script", "<pre><code>a<br>b<script>z</script></code></pre>", "```\na\nb\n```"},
|
|
{"adjacent same marks merge", "<p><strong>a</strong><strong>b</strong></p>", "**ab**"},
|
|
{"adjacent different marks stay apart", "<p><strong>a</strong><em>b</em></p>", "**a***b*"},
|
|
{"two paragraphs", "<p>a</p><p>b</p>", "a\n\nb"},
|
|
{"centered paragraph", `<p style="text-align: center">hi <strong>b</strong></p>`, "<div style=\"text-align: center\">\n\nhi **b**\n\n</div>"},
|
|
{"right aligned heading", `<h2 style="text-align:right">T</h2>`, "<div style=\"text-align: right\">\n\n## T\n\n</div>"},
|
|
{"justified among other styles", `<p style="color:red; TEXT-ALIGN: Justify">a</p>`, "<div style=\"text-align: justify\">\n\na\n\n</div>"},
|
|
{"tiptap style with trailing semicolon", `<p style="text-align: center;">a</p>`, "<div style=\"text-align: center\">\n\na\n\n</div>"},
|
|
{"tiptap explicit left", `<p style="text-align: left;">a</p>`, "a"},
|
|
{"left alignment is the default", `<p style="text-align: left">a</p>`, "a"},
|
|
{"unknown alignment ignored", `<p style="text-align: start">a</p>`, "a"},
|
|
{"style without value ignored", `<p style="text-align">a</p>`, "a"},
|
|
{"empty aligned paragraph", `<p style="text-align: center"></p>`, ""},
|
|
{"aligned paragraph in list item", `<ul><li><p style="text-align: center">a</p></li></ul>`, "- <div style=\"text-align: center\">\n\n a\n\n </div>"},
|
|
{"aligned paragraph in blockquote", `<blockquote><p style="text-align: center">q</p></blockquote>`, "> <div style=\"text-align: center\">\n>\n> q\n>\n> </div>"},
|
|
{"alignment skips the image inside a paragraph", `<p style="text-align: center">x<img src="/i.png" alt="I">y</p>`, "<div style=\"text-align: center\">\n\nx\n\n</div>\n\n\n\n<div style=\"text-align: center\">\n\ny\n\n</div>"},
|
|
{"text characters are escaped", "<p>a * b _c_ `d` [e]</p>", "a \\* b \\_c\\_ \\`d\\` \\[e\\]"},
|
|
{"leading hash is escaped", "<p># not a heading</p>", "\\# not a heading"},
|
|
{"hash without space is kept", "<p>#tag</p>", "#tag"},
|
|
{"leading dash is escaped", "<p>- not a list</p>", "\\- not a list"},
|
|
{"leading plus is escaped", "<p>+ not a list</p>", "\\+ not a list"},
|
|
{"leading quote marker is escaped", "<p>> not a quote</p>", "\\> not a quote"},
|
|
{"leading number is escaped", "<p>1. not a list</p>", "1\\. not a list"},
|
|
{"leading number with parenthesis is escaped", "<p>2) not a list</p>", "2\\) not a list"},
|
|
{"leading rule is escaped", "<p>---</p>", "\\---"},
|
|
{"leading equals is escaped", "<p>==</p>", "\\=="},
|
|
{"inner dash is kept", "<p>a - b</p>", "a - b"},
|
|
{"marker after a line break is escaped", "<p>a<br>- b</p>", "a\\\n\\- b"},
|
|
{"combined document", `<h1>Title</h1><p style="text-align: center">Intro <strong>bold</strong> and <em>it</em></p><ul><li><p>one</p></li><li><p>two</p></li></ul><img src="/static/uploadedImages/a.png" alt="A"><pre><code class="language-go">x := 1</code></pre><blockquote><p>quote</p></blockquote><hr>`, "# Title\n\n<div style=\"text-align: center\">\n\nIntro **bold** and *it*\n\n</div>\n\n- one\n- two\n\n\n\n```go\nx := 1\n```\n\n> quote\n\n---"},
|
|
}
|
|
for _, example := range examples {
|
|
t.Run(example.name, func(t *testing.T) {
|
|
got, err := converter.Render(example.html, domainArticle.BodyFormatMarkdown)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if got != example.markdown {
|
|
t.Errorf("expected %q, got %q", example.markdown, got)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMarkdownConverterHtmlAndUnknownFormat(t *testing.T) {
|
|
converter := markdown.NewConverter()
|
|
html := "<p>a</p><script>x</script>"
|
|
got, err := converter.Render(html, domainArticle.BodyFormatHTML)
|
|
if err != nil || got != html {
|
|
t.Errorf("html must pass through unchanged, got %q, %v", got, err)
|
|
}
|
|
if _, err := converter.Render(html, domainArticle.BodyFormat("pdf")); !errors.Is(err, domainArticle.ErrUnsupportedBodyFormat) {
|
|
t.Errorf("expected ErrUnsupportedBodyFormat, got %v", err)
|
|
}
|
|
}
|