2017-04-21 15:01:08 +08:00
// Copyright 2017 The Gitea Authors. All rights reserved.
2022-11-27 13:20:29 -05:00
// SPDX-License-Identifier: MIT
2017-04-21 15:01:08 +08:00
2016-04-22 17:28:08 -05:00
package markdown_test
import (
2024-11-24 16:18:57 +08:00
"context"
2024-03-01 15:11:51 +08:00
"html/template"
2017-02-14 08:13:59 +07:00
"strings"
2017-09-17 01:17:57 +08:00
"testing"
2017-02-14 08:13:59 +07:00
2021-10-11 20:12:06 +08:00
"code.gitea.io/gitea/modules/log"
2021-04-20 06:25:08 +08:00
"code.gitea.io/gitea/modules/markup"
2023-10-24 04:54:59 +02:00
"code.gitea.io/gitea/modules/markup/markdown"
2016-11-10 17:24:48 +01:00
"code.gitea.io/gitea/modules/setting"
2024-11-14 13:02:11 +08:00
"code.gitea.io/gitea/modules/test"
2017-09-17 01:17:57 +08:00
2017-02-08 01:29:07 -05:00
"github.com/stretchr/testify/assert"
2016-04-22 17:28:08 -05:00
)
2022-01-20 18:46:10 +01:00
const (
2024-05-30 15:04:01 +08:00
AppURL = "http://localhost:3000/"
testRepoOwnerName = "user13"
testRepoName = "repo11"
2022-01-20 18:46:10 +01:00
)
2016-04-22 17:28:08 -05:00
2024-02-29 04:39:24 +01:00
// these values should match the const above
2019-04-12 01:53:34 -04:00
var localMetas = map [ string ] string {
2024-05-30 15:04:01 +08:00
"user" : testRepoOwnerName ,
"repo" : testRepoName ,
2019-04-12 01:53:34 -04:00
}
2017-02-24 21:59:56 +07:00
func TestRender_StandardLinks ( t * testing . T ) {
2024-11-24 16:18:57 +08:00
test := func ( input , expected string ) {
buffer , err := markdown . RenderString ( markup . NewTestRenderContext ( ) , input )
2021-04-20 06:25:08 +08:00
assert . NoError ( t , err )
2024-03-01 15:11:51 +08:00
assert . Equal ( t , strings . TrimSpace ( expected ) , strings . TrimSpace ( string ( buffer ) ) )
2017-02-24 21:59:56 +07:00
}
googleRendered := ` <p><a href="https://google.com/" rel="nofollow">https://google.com/</a></p> `
2024-11-24 16:18:57 +08:00
test ( "<https://google.com/>" , googleRendered )
test ( "[Link](Link)" , ` <p><a href="/Link" rel="nofollow">Link</a></p> ` )
2017-02-24 21:59:56 +07:00
}
func TestRender_Images ( t * testing . T ) {
setting . AppURL = AppURL
2026-03-08 23:57:37 +08:00
const baseLink = "http://localhost:3000/user13/repo11"
2025-06-21 14:53:22 +08:00
render := func ( input , expected string ) {
2026-03-08 23:57:37 +08:00
buffer , err := markdown . RenderString ( markup . NewTestRenderContext ( baseLink ) , input )
2021-04-20 06:25:08 +08:00
assert . NoError ( t , err )
2024-03-01 15:11:51 +08:00
assert . Equal ( t , strings . TrimSpace ( expected ) , strings . TrimSpace ( string ( buffer ) ) )
2017-02-24 21:59:56 +07:00
}
url := "../../.images/src/02/train.jpg"
title := "Train"
2018-10-30 17:26:28 -05:00
href := "https://gitea.io"
2026-03-08 23:57:37 +08:00
result := baseLink + "/.images/src/02/train.jpg" // resolved link should not go out of the base link
2021-10-11 20:12:06 +08:00
// hint: With Markdown v2.5.2, there is a new syntax: [link](URL){:target="_blank"} , but we do not support it now
2017-02-24 21:59:56 +07:00
2025-06-21 14:53:22 +08:00
render (
2017-02-24 21:59:56 +07:00
"" ,
2021-10-11 20:12:06 +08:00
` <p><a href=" ` + result + ` " target="_blank" rel="nofollow noopener"><img src=" ` + result + ` " alt=" ` + title + ` "/></a></p> ` )
2017-02-24 21:59:56 +07:00
2025-06-21 14:53:22 +08:00
render (
2017-02-24 21:59:56 +07:00
"[[" + title + "|" + url + "]]" ,
2018-02-27 08:09:18 +01:00
` <p><a href=" ` + result + ` " rel="nofollow"><img src=" ` + result + ` " title=" ` + title + ` " alt=" ` + title + ` "/></a></p> ` )
2025-06-21 14:53:22 +08:00
render (
2018-10-30 17:26:28 -05:00
"[](" + href + ")" ,
` <p><a href=" ` + href + ` " rel="nofollow"><img src=" ` + result + ` " alt=" ` + title + ` "/></a></p> ` )
2021-04-10 17:26:28 +01:00
2025-06-21 14:53:22 +08:00
render (
2021-04-10 17:26:28 +01:00
"" ,
2021-10-11 20:12:06 +08:00
` <p><a href=" ` + result + ` " target="_blank" rel="nofollow noopener"><img src=" ` + result + ` " alt=" ` + title + ` "/></a></p> ` )
2021-04-10 17:26:28 +01:00
2025-06-21 14:53:22 +08:00
render (
2021-04-10 17:26:28 +01:00
"[[" + title + "|" + url + "]]" ,
` <p><a href=" ` + result + ` " rel="nofollow"><img src=" ` + result + ` " title=" ` + title + ` " alt=" ` + title + ` "/></a></p> ` )
2025-06-21 14:53:22 +08:00
render (
2021-04-10 17:26:28 +01:00
"[](" + href + ")" ,
` <p><a href=" ` + href + ` " rel="nofollow"><img src=" ` + result + ` " alt=" ` + title + ` "/></a></p> ` )
2025-06-21 14:53:22 +08:00
defer test . MockVariableValue ( & markup . RenderBehaviorForTesting . DisableAdditionalAttributes , false ) ( )
render (
"<a><img src='a.jpg'></a>" , // by the way, empty "a" tag will be removed
` <p dir="auto"><img src="http://localhost:3000/user13/repo11/a.jpg" loading="lazy"/></p> ` )
2017-02-14 08:13:59 +07:00
}
2024-11-27 00:46:02 +08:00
func TestTotal_RenderString ( t * testing . T ) {
2026-03-08 23:57:37 +08:00
const FullURL = AppURL + testRepoOwnerName + "/" + testRepoName + "/"
2026-01-23 15:24:58 -05:00
setting . AppURL = AppURL
2024-11-27 00:46:02 +08:00
defer test . MockVariableValue ( & markup . RenderBehaviorForTesting . DisableAdditionalAttributes , true ) ( )
// Test cases without ambiguous links (It is not right to copy a whole file here, instead it should clearly test what is being tested)
sameCases := [ ] string {
// dear imgui wiki markdown extract: special wiki syntax
` Wiki! Enjoy :)
- [[Links, Language bindings, Engine bindings|Links]]
- [[Tips]]
See commit 65f1bf27bc
Ideas and codes
- Bezier widget (by @r-lyeh) ` + AppURL + ` ocornut/imgui/issues/786
- Bezier widget (by @r-lyeh) ` + FullURL + ` issues/786
- Node graph editors https://github.com/ocornut/imgui/issues/306
- [[Memory Editor|memory_editor_example]]
- [[Plot var helper|plot_var_example]] ` ,
// wine-staging wiki home extract: tables, special wiki syntax, images
` ## What is Wine Staging?
**Wine Staging** on website [wine-staging.com](http://wine-staging.com).
## Quick Links
Here are some links to the most important topics. You can find the full list of pages at the sidebar.
| [[images/icon-install.png]] | [[Installation]] |
|--------------------------------|----------------------------------------------------------|
| [[images/icon-usage.png]] | [[Usage]] |
` ,
// libgdx wiki page: inline images with special syntax
` [Excelsior JET](http://www.excelsiorjet.com/) allows you to create native executables for Windows, Linux and Mac OS X.
1. [Package your libGDX application](https://github.com/libgdx/libgdx/wiki/Gradle-on-the-Commandline#packaging-for-the-desktop)
[[images/1.png]]
2. Perform a test run by hitting the Run! button.
[[images/2.png]]
## More tests { #custom-id}
(from https://www.markdownguide.org/extended-syntax/)
### Checkboxes
- [ ] unchecked
- [x] checked
- [ ] still unchecked
### Definition list
First Term
: This is the definition of the first term.
Second Term
: This is one definition of the second term.
: This is another definition of the second term.
### Footnotes
Here is a simple footnote,[^1] and here is a longer one.[^bignote]
[^1]: This is the first footnote.
[^bignote]: Here is one with multiple paragraphs and code.
Indent paragraphs to include them in the footnote.
` + "`{ my code }`" + `
Add as many paragraphs as you like.
` ,
`
- [ ] <!-- rebase-check --> If you want to rebase/retry this PR, click this checkbox.
---
This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!-- test-comment --> ` ,
}
baseURL := ""
testAnswers := [ ] string {
2017-02-24 21:59:56 +07:00
` <p>Wiki! Enjoy :)</p>
<ul>
2024-11-24 16:18:57 +08:00
<li><a href=" ` + baseURL + ` /Links" rel="nofollow">Links, Language bindings, Engine bindings</a></li>
<li><a href=" ` + baseURL + ` /Tips" rel="nofollow">Tips</a></li>
2017-02-24 21:59:56 +07:00
</ul>
2024-05-30 15:04:01 +08:00
<p>See commit <a href="/ ` + testRepoOwnerName + ` / ` + testRepoName + ` /commit/65f1bf27bc" rel="nofollow"><code>65f1bf27bc</code></a></p>
2017-02-24 21:59:56 +07:00
<p>Ideas and codes</p>
<ul>
2024-03-13 11:34:58 +01:00
<li>Bezier widget (by <a href="/r-lyeh" rel="nofollow">@r-lyeh</a>) <a href="http://localhost:3000/ocornut/imgui/issues/786" class="ref-issue" rel="nofollow">ocornut/imgui#786</a></li>
2024-05-30 15:04:01 +08:00
<li>Bezier widget (by <a href="/r-lyeh" rel="nofollow">@r-lyeh</a>) <a href=" ` + FullURL + ` issues/786" class="ref-issue" rel="nofollow">#786</a></li>
2018-02-27 08:09:18 +01:00
<li>Node graph editors <a href="https://github.com/ocornut/imgui/issues/306" rel="nofollow">https://github.com/ocornut/imgui/issues/306</a></li>
2024-11-24 16:18:57 +08:00
<li><a href=" ` + baseURL + ` /memory_editor_example" rel="nofollow">Memory Editor</a></li>
<li><a href=" ` + baseURL + ` /plot_var_example" rel="nofollow">Plot var helper</a></li>
2017-02-24 21:59:56 +07:00
</ul>
` ,
2019-12-23 16:38:50 -06:00
` <h2 id="user-content-what-is-wine-staging">What is Wine Staging?</h2>
2017-02-14 08:13:59 +07:00
<p><strong>Wine Staging</strong> on website <a href="http://wine-staging.com" rel="nofollow">wine-staging.com</a>.</p>
2019-12-23 16:38:50 -06:00
<h2 id="user-content-quick-links">Quick Links</h2>
2017-02-14 08:13:59 +07:00
<p>Here are some links to the most important topics. You can find the full list of pages at the sidebar.</p>
<table>
<thead>
<tr>
2024-11-24 16:18:57 +08:00
<th><a href=" ` + baseURL + ` /images/icon-install.png" rel="nofollow"><img src=" ` + baseURL + ` /images/icon-install.png" title="icon-install.png" alt="images/icon-install.png"/></a></th>
<th><a href=" ` + baseURL + ` /Installation" rel="nofollow">Installation</a></th>
2017-02-14 08:13:59 +07:00
</tr>
</thead>
<tbody>
<tr>
2024-11-24 16:18:57 +08:00
<td><a href=" ` + baseURL + ` /images/icon-usage.png" rel="nofollow"><img src=" ` + baseURL + ` /images/icon-usage.png" title="icon-usage.png" alt="images/icon-usage.png"/></a></td>
<td><a href=" ` + baseURL + ` /Usage" rel="nofollow">Usage</a></td>
2017-02-14 08:13:59 +07:00
</tr>
</tbody>
</table>
` ,
2017-02-24 21:59:56 +07:00
` <p><a href="http://www.excelsiorjet.com/" rel="nofollow">Excelsior JET</a> allows you to create native executables for Windows, Linux and Mac OS X.</p>
2017-02-14 08:13:59 +07:00
<ol>
2024-11-27 00:46:02 +08:00
<li><a href="https://github.com/libgdx/libgdx/wiki/Gradle-on-the-Commandline#packaging-for-the-desktop" rel="nofollow">Package your libGDX application</a>
2024-11-24 16:18:57 +08:00
<a href=" ` + baseURL + ` /images/1.png" rel="nofollow"><img src=" ` + baseURL + ` /images/1.png" title="1.png" alt="images/1.png"/></a></li>
2024-11-27 00:46:02 +08:00
<li>Perform a test run by hitting the Run! button.
2024-11-24 16:18:57 +08:00
<a href=" ` + baseURL + ` /images/2.png" rel="nofollow"><img src=" ` + baseURL + ` /images/2.png" title="2.png" alt="images/2.png"/></a></li>
2017-02-14 08:13:59 +07:00
</ol>
2019-12-23 16:38:50 -06:00
<h2 id="user-content-custom-id">More tests</h2>
2019-03-21 14:53:06 +01:00
<p>(from <a href="https://www.markdownguide.org/extended-syntax/" rel="nofollow">https://www.markdownguide.org/extended-syntax/</a>)</p>
2020-05-03 21:17:24 +01:00
<h3 id="user-content-checkboxes">Checkboxes</h3>
2020-05-11 00:14:49 +01:00
<ul>
2021-05-23 16:14:03 +02:00
<li class="task-list-item"><input type="checkbox" disabled="" data-source-position="434"/>unchecked</li>
<li class="task-list-item"><input type="checkbox" disabled="" data-source-position="450" checked=""/>checked</li>
<li class="task-list-item"><input type="checkbox" disabled="" data-source-position="464"/>still unchecked</li>
2020-05-03 21:17:24 +01:00
</ul>
2019-12-23 16:38:50 -06:00
<h3 id="user-content-definition-list">Definition list</h3>
2019-03-21 14:53:06 +01:00
<dl>
<dt>First Term</dt>
<dd>This is the definition of the first term.</dd>
<dt>Second Term</dt>
<dd>This is one definition of the second term.</dd>
<dd>This is another definition of the second term.</dd>
</dl>
2019-12-23 16:38:50 -06:00
<h3 id="user-content-footnotes">Footnotes</h3>
2025-06-10 01:18:11 +08:00
<p>Here is a simple footnote,<sup id="fnref:user-content-1"><a href="#fn:user-content-1" rel="nofollow">1 </a></sup> and here is a longer one.<sup id="fnref:user-content-bignote"><a href="#fn:user-content-bignote" rel="nofollow">2 </a></sup></p>
2019-03-21 14:53:06 +01:00
<div>
<hr/>
<ol>
2019-12-31 01:53:28 +00:00
<li id="fn:user-content-1">
<p>This is the first footnote. <a href="#fnref:user-content-1" rel="nofollow">↩︎</a></p>
</li>
<li id="fn:user-content-bignote">
<p>Here is one with multiple paragraphs and code.</p>
2019-03-21 14:53:06 +01:00
<p>Indent paragraphs to include them in the footnote.</p>
<p><code> { my code }</code></p>
2019-12-31 01:53:28 +00:00
<p>Add as many paragraphs as you like. <a href="#fnref:user-content-bignote" rel="nofollow">↩︎</a></p>
</li>
2019-03-21 14:53:06 +01:00
</ol>
</div>
2024-11-27 00:46:02 +08:00
` ,
` <ul>
2022-03-24 16:47:37 +01:00
<li class="task-list-item"><input type="checkbox" disabled="" data-source-position="3"/> If you want to rebase/retry this PR, click this checkbox.</li>
</ul>
<hr/>
<p>This PR has been generated by <a href="https://github.com/renovatebot/renovate" rel="nofollow">Renovate Bot</a>.</p>
2017-02-14 08:13:59 +07:00
` ,
2017-02-24 21:59:56 +07:00
}
2024-11-24 16:18:57 +08:00
markup . Init ( & markup . RenderHelperFuncs {
IsUsernameMentionable : func ( ctx context . Context , username string ) bool {
return username == "r-lyeh"
} ,
} )
2025-06-18 03:48:09 +02:00
for i := range sameCases {
2026-01-23 15:24:58 -05:00
line , err := markdown . RenderString ( markup . NewTestRenderContext ( localMetas ) . WithEnableHeadingIDGeneration ( true ) , sameCases [ i ] )
2021-04-20 06:25:08 +08:00
assert . NoError ( t , err )
2024-11-27 00:46:02 +08:00
assert . Equal ( t , testAnswers [ i ] , string ( line ) )
2017-09-17 01:17:57 +08:00
}
}
2019-11-12 23:27:11 -03:00
func TestRender_RenderParagraphs ( t * testing . T ) {
test := func ( t * testing . T , str string , cnt int ) {
2024-11-22 13:48:09 +08:00
res , err := markdown . RenderRawString ( markup . NewTestRenderContext ( ) , str )
2021-04-20 06:25:08 +08:00
assert . NoError ( t , err )
assert . Equal ( t , cnt , strings . Count ( res , "<p" ) , "Rendered result for unix should have %d paragraph(s) but has %d:\n%s\n" , cnt , strings . Count ( res , "<p" ) , res )
mac := strings . ReplaceAll ( str , "\n" , "\r" )
2024-11-22 13:48:09 +08:00
res , err = markdown . RenderRawString ( markup . NewTestRenderContext ( ) , mac )
2021-04-20 06:25:08 +08:00
assert . NoError ( t , err )
assert . Equal ( t , cnt , strings . Count ( res , "<p" ) , "Rendered result for mac should have %d paragraph(s) but has %d:\n%s\n" , cnt , strings . Count ( res , "<p" ) , res )
dos := strings . ReplaceAll ( str , "\n" , "\r\n" )
2024-11-22 13:48:09 +08:00
res , err = markdown . RenderRawString ( markup . NewTestRenderContext ( ) , dos )
2021-04-20 06:25:08 +08:00
assert . NoError ( t , err )
assert . Equal ( t , cnt , strings . Count ( res , "<p" ) , "Rendered result for windows should have %d paragraph(s) but has %d:\n%s\n" , cnt , strings . Count ( res , "<p" ) , res )
2019-11-12 23:27:11 -03:00
}
test ( t , "\nOne\nTwo\nThree" , 1 )
test ( t , "\n\nOne\nTwo\nThree" , 1 )
test ( t , "\n\nOne\nTwo\nThree\n\n\n" , 1 )
test ( t , "A\n\nB\nC\n" , 2 )
test ( t , "A\n\n\nB\nC\n" , 2 )
}
2021-03-14 16:36:51 +00:00
2021-03-15 23:20:05 +00:00
func TestMarkdownRenderRaw ( t * testing . T ) {
testcases := [ ] [ ] byte {
{ // clusterfuzz_testcase_minimized_fuzz_markdown_render_raw_6267570554535936
0x2a , 0x20 , 0x2d , 0x0a , 0x09 , 0x20 , 0x60 , 0x5b , 0x0a , 0x09 , 0x20 , 0x60 ,
0x5b ,
} ,
{ // clusterfuzz_testcase_minimized_fuzz_markdown_render_raw_6278827345051648
0x2d , 0x20 , 0x2d , 0x0d , 0x09 , 0x60 , 0x0d , 0x09 , 0x60 ,
} ,
{ // clusterfuzz_testcase_minimized_fuzz_markdown_render_raw_6016973788020736[] = {
0x7b , 0x63 , 0x6c , 0x61 , 0x73 , 0x73 , 0x3d , 0x35 , 0x7d , 0x0a , 0x3d ,
} ,
}
for _ , testcase := range testcases {
2021-10-11 20:12:06 +08:00
log . Info ( "Test markdown render error with fuzzy data: %x, the following errors can be recovered" , testcase )
2024-11-22 13:48:09 +08:00
_ , err := markdown . RenderRawString ( markup . NewTestRenderContext ( ) , string ( testcase ) )
2021-04-20 06:25:08 +08:00
assert . NoError ( t , err )
2021-03-15 23:20:05 +00:00
}
}
2021-03-14 16:36:51 +00:00
func TestRenderSiblingImages_Issue12925 ( t * testing . T ) {
testcase := ` 

`
2025-04-04 23:45:23 +08:00
expected := ` <p><a href="/image1" target="_blank" rel="nofollow noopener"><img src="/image1" alt="image1"/></a>
<a href="/image2" target="_blank" rel="nofollow noopener"><img src="/image2" alt="image2"/></a></p>
2021-03-14 16:36:51 +00:00
`
2025-04-04 23:45:23 +08:00
res , err := markdown . RenderString ( markup . NewTestRenderContext ( ) , testcase )
2021-04-20 06:25:08 +08:00
assert . NoError ( t , err )
2025-04-04 23:45:23 +08:00
assert . Equal ( t , expected , string ( res ) )
2021-12-16 00:49:12 +01:00
}
2021-03-15 23:20:05 +00:00
2021-12-16 00:49:12 +01:00
func TestRenderEmojiInLinks_Issue12331 ( t * testing . T ) {
testcase := ` [Link with emoji :moon: in text](https://gitea.io) `
expected := ` <p><a href="https://gitea.io" rel="nofollow">Link with emoji <span class="emoji" aria-label="waxing gibbous moon">🌔</span> in text</a></p>
`
2024-11-22 13:48:09 +08:00
res , err := markdown . RenderString ( markup . NewTestRenderContext ( ) , testcase )
2021-12-16 00:49:12 +01:00
assert . NoError ( t , err )
2024-03-01 15:11:51 +08:00
assert . Equal ( t , template . HTML ( expected ) , res )
2021-03-14 16:36:51 +00:00
}
2022-10-05 19:55:36 +01:00
2022-10-21 15:00:53 +03:00
func TestColorPreview ( t * testing . T ) {
const nl = "\n"
positiveTests := [ ] struct {
testcase string
expected string
} {
2024-03-31 19:17:34 +08:00
{ // do not render color names
"The CSS class `red` is there" ,
"<p>The CSS class <code>red</code> is there</p>\n" ,
} ,
2022-10-21 15:00:53 +03:00
{ // hex
"`#FF0000`" ,
` <p><code>#FF0000<span class="color-preview" style="background-color: #FF0000"></span></code></p> ` + nl ,
} ,
{ // rgb
"`rgb(16, 32, 64)`" ,
` <p><code>rgb(16, 32, 64)<span class="color-preview" style="background-color: rgb(16, 32, 64)"></span></code></p> ` + nl ,
} ,
{ // short hex
2024-03-31 19:17:34 +08:00
"This is the color white `#0a0`" ,
` <p>This is the color white <code>#0a0<span class="color-preview" style="background-color: #0a0"></span></code></p> ` + nl ,
2022-10-21 15:00:53 +03:00
} ,
{ // hsl
"HSL stands for hue, saturation, and lightness. An example: `hsl(0, 100%, 50%)`." ,
` <p>HSL stands for hue, saturation, and lightness. An example: <code>hsl(0, 100%, 50%)<span class="color-preview" style="background-color: hsl(0, 100%, 50%)"></span></code>.</p> ` + nl ,
} ,
{ // uppercase hsl
"HSL stands for hue, saturation, and lightness. An example: `HSL(0, 100%, 50%)`." ,
` <p>HSL stands for hue, saturation, and lightness. An example: <code>HSL(0, 100%, 50%)<span class="color-preview" style="background-color: HSL(0, 100%, 50%)"></span></code>.</p> ` + nl ,
} ,
}
for _ , test := range positiveTests {
2024-11-22 13:48:09 +08:00
res , err := markdown . RenderString ( markup . NewTestRenderContext ( ) , test . testcase )
2022-10-21 15:00:53 +03:00
assert . NoError ( t , err , "Unexpected error in testcase: %q" , test . testcase )
2024-03-01 15:11:51 +08:00
assert . Equal ( t , template . HTML ( test . expected ) , res , "Unexpected result in testcase %q" , test . testcase )
2022-10-21 15:00:53 +03:00
}
negativeTests := [ ] string {
// not a color code
"`FF0000`" ,
// inside a code block
"```javascript" + nl + ` const red = "#FF0000"; ` + nl + "```" ,
// no backticks
"rgb(166, 32, 64)" ,
// typo
"`hsI(0, 100%, 50%)`" ,
// looks like a color but not really
"`hsl(40, 60, 80)`" ,
}
for _ , test := range negativeTests {
2024-11-22 13:48:09 +08:00
res , err := markdown . RenderString ( markup . NewTestRenderContext ( ) , test )
2022-10-21 15:00:53 +03:00
assert . NoError ( t , err , "Unexpected error in testcase: %q" , test )
assert . NotContains ( t , res , ` <span class="color-preview" style="background-color: ` , "Unexpected result in testcase %q" , test )
}
}
2025-04-03 13:48:24 +08:00
func TestMarkdownFrontmatter ( t * testing . T ) {
2023-06-13 02:44:47 -04:00
testcases := [ ] struct {
2025-04-03 13:48:24 +08:00
name string
input string
2023-06-13 02:44:47 -04:00
expected string
} {
2025-04-03 13:48:24 +08:00
{
"MapInFrontmatter" ,
` ---
key1: val1
key2: val2
---
test
` ,
` <details class="frontmatter-content"><summary><span>octicon-table(12/)</span> key1, key2</summary><table>
<thead>
<tr>
<th>key1</th>
<th>key2</th>
</tr>
</thead>
<tbody>
<tr>
<td>val1</td>
<td>val2</td>
</tr>
</tbody>
</table>
</details><p>test</p>
` ,
} ,
{
"ListInFrontmatter" ,
` ---
- item1
- item2
---
test
` ,
2026-04-03 20:19:04 +08:00
` <hr/>
<ul>
<li>item1</li>
<li>item2</li>
</ul>
<hr/>
2025-04-03 13:48:24 +08:00
<p>test</p>
` ,
} ,
{
"StringInFrontmatter" ,
` ---
anything
---
test
` ,
2026-04-03 20:19:04 +08:00
` <hr/>
<h2>anything</h2>
2025-04-03 13:48:24 +08:00
<p>test</p>
` ,
} ,
2023-06-13 02:44:47 -04:00
{
// data-source-position should take into account YAML frontmatter.
2025-04-03 13:48:24 +08:00
"ListAfterFrontmatter" ,
2023-06-13 02:44:47 -04:00
` ---
foo: bar
---
- [ ] task 1 ` ,
2025-04-03 13:48:24 +08:00
` <details class="frontmatter-content"><summary><span>octicon-table(12/)</span> foo</summary><table>
2023-06-13 02:44:47 -04:00
<thead>
<tr>
<th>foo</th>
</tr>
</thead>
<tbody>
<tr>
<td>bar</td>
</tr>
</tbody>
</table>
</details><ul>
<li class="task-list-item"><input type="checkbox" disabled="" data-source-position="19"/>task 1</li>
</ul>
2026-04-03 20:19:04 +08:00
` ,
} ,
// we have our own frontmatter parser, don't need to use github.com/yuin/goldmark-meta
{
"InvalidFrontmatter" ,
` ---
foo
` ,
` <hr/>
<p>foo</p>
2023-06-13 02:44:47 -04:00
` ,
} ,
}
2026-04-03 20:19:04 +08:00
for _ , tt := range testcases {
t . Run ( tt . name , func ( t * testing . T ) {
res , err := markdown . RenderString ( markup . NewTestRenderContext ( ) , tt . input )
assert . NoError ( t , err , "Unexpected error in testcase: %q" , tt . name )
assert . Equal ( t , tt . expected , string ( res ) , "Unexpected result in testcase %q" , tt . name )
} )
2023-06-13 02:44:47 -04:00
}
}
2024-01-15 09:49:24 +01:00
func TestRenderLinks ( t * testing . T ) {
2026-02-16 11:27:49 +01:00
defer test . MockVariableValue ( & setting . AppURL , AppURL ) ( )
defer test . MockVariableValue ( & markup . RenderBehaviorForTesting . DisableAdditionalAttributes , true ) ( )
2024-03-01 15:11:51 +08:00
input := ` space @mention-user$ { SPACE}$ { SPACE}
2024-01-15 09:49:24 +01:00
/just/a/path.bin
https://example.com/file.bin
[local link](file.bin)
[remote link](https://example.com)
[[local link|file.bin]]
[[remote link|https://example.com]]

2024-01-27 04:36:01 +01:00


2024-01-15 09:49:24 +01:00

[[local image|image.jpg]]
[[remote link|https://example.com/image.jpg]]
https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit
:+1:
mail@domain.com
@mention-user test
#123
2024-03-01 15:11:51 +08:00
space$ { SPACE}$ { SPACE}
2024-01-15 09:49:24 +01:00
`
2024-03-01 15:11:51 +08:00
input = strings . ReplaceAll ( input , "${SPACE}" , " " ) // replace ${SPACE} with " ", to avoid some editor's auto-trimming
2024-11-27 00:46:02 +08:00
expected := ` <p>space @mention-user<br/>
/just/a/path.bin
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a>
<a href="/file.bin" rel="nofollow">local link</a>
<a href="https://example.com" rel="nofollow">remote link</a>
<a href="/file.bin" rel="nofollow">local link</a>
<a href="https://example.com" rel="nofollow">remote link</a>
<a href="/image.jpg" target="_blank" rel="nofollow noopener"><img src="/image.jpg" alt="local image"/></a>
<a href="/path/file" target="_blank" rel="nofollow noopener"><img src="/path/file" alt="local image"/></a>
<a href="/path/file" target="_blank" rel="nofollow noopener"><img src="/path/file" alt="local image"/></a>
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a>
<a href="/image.jpg" rel="nofollow"><img src="/image.jpg" title="local image" alt="local image"/></a>
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt="remote link"/></a>
2026-02-16 11:27:49 +01:00
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a>
2024-11-27 00:46:02 +08:00
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
2026-02-16 11:27:49 +01:00
<a href="https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb" rel="nofollow">https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb</a>
2024-11-27 00:46:02 +08:00
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit
<span class="emoji" aria-label="thumbs up">👍</span>
<a href="mailto:mail@domain.com" rel="nofollow">mail@domain.com</a>
@mention-user test
#123
2024-01-15 09:49:24 +01:00
space</p>
2024-11-27 00:46:02 +08:00
`
result , err := markdown . RenderString ( markup . NewTestRenderContext ( localMetas ) , input )
assert . NoError ( t , err )
assert . Equal ( t , expected , string ( result ) )
2026-02-16 11:27:49 +01:00
t . Run ( "LocalCommitAndCompare" , func ( t * testing . T ) {
input := ` http://localhost:3000/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb
http://localhost:3000/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash `
expected := ` <p><a href="http://localhost:3000/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb" rel="nofollow"><code>88fc37a3c0</code></a>
<a href="http://localhost:3000/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow"><code>88fc37a3c0...12fc37a3c0 (hash)</code></a></p>
`
result , err := markdown . RenderString ( markup . NewTestRenderContext ( localMetas ) , input )
assert . NoError ( t , err )
assert . Equal ( t , expected , string ( result ) )
} )
2024-01-15 09:49:24 +01:00
}
2025-04-04 23:45:23 +08:00
func TestMarkdownLink ( t * testing . T ) {
defer test . MockVariableValue ( & markup . RenderBehaviorForTesting . DisableAdditionalAttributes , true ) ( )
input := ` <a href=foo>link1</a>
<a href='/foo'>link2</a>
<a href="#foo">link3</a> `
result , err := markdown . RenderString ( markup . NewTestRenderContext ( "/base" , localMetas ) , input )
assert . NoError ( t , err )
assert . Equal ( t , ` <p><a href="/base/foo" rel="nofollow">link1</a>
<a href="/base/foo" rel="nofollow">link2</a>
<a href="#user-content-foo" rel="nofollow">link3</a></p>
2026-01-23 15:24:58 -05:00
` , string ( result ) )
input = "https://example.com/__init__.py"
result , err = markdown . RenderString ( markup . NewTestRenderContext ( "/base" , localMetas ) , input )
assert . NoError ( t , err )
assert . Equal ( t , ` <p><a href="https://example.com/__init__.py" rel="nofollow">https://example.com/__init__.py</a></p>
2025-04-04 23:45:23 +08:00
` , string ( result ) )
}
2026-04-04 04:03:59 +08:00
func TestMarkdownUlDir ( t * testing . T ) {
defer test . MockVariableValue ( & markup . RenderBehaviorForTesting . DisableAdditionalAttributes , false ) ( )
result , err := markdown . RenderString ( markup . NewTestRenderContext ( ) , `
* a
* b
` )
assert . NoError ( t , err )
assert . Equal ( t , ` <ul dir="auto">
<li>a
<ul>
<li>b</li>
</ul>
</li>
</ul>
` , string ( result ) )
}