github.com/gitbundle/modules@v0.0.0-20231025071548-85b91c5c3b01/markup/html_test.go (about) 1 // Copyright 2023 The GitBundle Inc. All rights reserved. 2 // Copyright 2017 The Gitea Authors. All rights reserved. 3 // Use of this source code is governed by a MIT-style 4 // license that can be found in the LICENSE file. 5 6 package markup_test 7 8 import ( 9 "context" 10 "io" 11 "strings" 12 "testing" 13 14 "github.com/gitbundle/modules/emoji" 15 "github.com/gitbundle/modules/git" 16 "github.com/gitbundle/modules/log" 17 . "github.com/gitbundle/modules/markup" 18 "github.com/gitbundle/modules/markup/markdown" 19 "github.com/gitbundle/modules/setting" 20 "github.com/gitbundle/modules/util" 21 22 "github.com/stretchr/testify/assert" 23 ) 24 25 var localMetas = map[string]string{ 26 "user": "gogits", 27 "repo": "gogs", 28 "repoPath": "../../integrations/gitea-repositories-meta/user13/repo11.git/", 29 } 30 31 func TestMain(m *testing.M) { 32 setting.LoadAllowEmpty() 33 if err := git.InitSimple(context.Background()); err != nil { 34 log.Fatal("git init failed, err: %v", err) 35 } 36 } 37 38 func TestRender_Commits(t *testing.T) { 39 setting.AppURL = TestAppURL 40 test := func(input, expected string) { 41 buffer, err := RenderString(&RenderContext{ 42 Ctx: git.DefaultContext, 43 RelativePath: ".md", 44 URLPrefix: TestRepoURL, 45 Metas: localMetas, 46 }, input) 47 assert.NoError(t, err) 48 assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) 49 } 50 51 sha := "65f1bf27bc3bf70f64657658635e66094edbcb4d" 52 repo := TestRepoURL 53 commit := util.URLJoin(repo, "commit", sha) 54 tree := util.URLJoin(repo, "tree", sha, "src") 55 56 file := util.URLJoin(repo, "commit", sha, "example.txt") 57 fileWithExtra := file + ":" 58 fileWithHash := file + "#L2" 59 fileWithHasExtra := file + "#L2:" 60 commitCompare := util.URLJoin(repo, "compare", sha+"..."+sha) 61 commitCompareWithHash := commitCompare + "#L2" 62 63 test(sha, `<p><a href="`+commit+`" rel="nofollow"><code>65f1bf27bc</code></a></p>`) 64 test(sha[:7], `<p><a href="`+commit[:len(commit)-(40-7)]+`" rel="nofollow"><code>65f1bf2</code></a></p>`) 65 test(sha[:39], `<p><a href="`+commit[:len(commit)-(40-39)]+`" rel="nofollow"><code>65f1bf27bc</code></a></p>`) 66 test(commit, `<p><a href="`+commit+`" rel="nofollow"><code>65f1bf27bc</code></a></p>`) 67 test(tree, `<p><a href="`+tree+`" rel="nofollow"><code>65f1bf27bc/src</code></a></p>`) 68 69 test(file, `<p><a href="`+file+`" rel="nofollow"><code>65f1bf27bc/example.txt</code></a></p>`) 70 test(fileWithExtra, `<p><a href="`+file+`" rel="nofollow"><code>65f1bf27bc/example.txt</code></a>:</p>`) 71 test(fileWithHash, `<p><a href="`+fileWithHash+`" rel="nofollow"><code>65f1bf27bc/example.txt (L2)</code></a></p>`) 72 test(fileWithHasExtra, `<p><a href="`+fileWithHash+`" rel="nofollow"><code>65f1bf27bc/example.txt (L2)</code></a>:</p>`) 73 test(commitCompare, `<p><a href="`+commitCompare+`" rel="nofollow"><code>65f1bf27bc...65f1bf27bc</code></a></p>`) 74 test(commitCompareWithHash, `<p><a href="`+commitCompareWithHash+`" rel="nofollow"><code>65f1bf27bc...65f1bf27bc (L2)</code></a></p>`) 75 76 test("commit "+sha, `<p>commit <a href="`+commit+`" rel="nofollow"><code>65f1bf27bc</code></a></p>`) 77 test("/home/gitea/"+sha, "<p>/home/gitea/"+sha+"</p>") 78 test("deadbeef", `<p>deadbeef</p>`) 79 test("d27ace93", `<p>d27ace93</p>`) 80 test(sha[:14]+".x", `<p>`+sha[:14]+`.x</p>`) 81 82 expected14 := `<a href="` + commit[:len(commit)-(40-14)] + `" rel="nofollow"><code>` + sha[:10] + `</code></a>` 83 test(sha[:14]+".", `<p>`+expected14+`.</p>`) 84 test(sha[:14]+",", `<p>`+expected14+`,</p>`) 85 test("["+sha[:14]+"]", `<p>[`+expected14+`]</p>`) 86 } 87 88 func TestRender_CrossReferences(t *testing.T) { 89 setting.AppURL = TestAppURL 90 91 test := func(input, expected string) { 92 buffer, err := RenderString(&RenderContext{ 93 RelativePath: "a.md", 94 URLPrefix: setting.AppSubURL, 95 Metas: localMetas, 96 }, input) 97 assert.NoError(t, err) 98 assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) 99 } 100 101 test( 102 "gogits/gogs#12345", 103 `<p><a href="`+util.URLJoin(TestAppURL, "gogits", "gogs", "issues", "12345")+`" class="ref-issue" rel="nofollow">gogits/gogs#12345</a></p>`) 104 test( 105 "go-gitea/gitea#12345", 106 `<p><a href="`+util.URLJoin(TestAppURL, "go-gitea", "gitea", "issues", "12345")+`" class="ref-issue" rel="nofollow">go-gitea/gitea#12345</a></p>`) 107 test( 108 "/home/gitea/go-gitea/gitea#12345", 109 `<p>/home/gitea/go-gitea/gitea#12345</p>`) 110 test( 111 util.URLJoin(TestAppURL, "gogitea", "gitea", "issues", "12345"), 112 `<p><a href="`+util.URLJoin(TestAppURL, "gogitea", "gitea", "issues", "12345")+`" class="ref-issue" rel="nofollow">gogitea/gitea#12345</a></p>`) 113 test( 114 util.URLJoin(TestAppURL, "go-gitea", "gitea", "issues", "12345"), 115 `<p><a href="`+util.URLJoin(TestAppURL, "go-gitea", "gitea", "issues", "12345")+`" class="ref-issue" rel="nofollow">go-gitea/gitea#12345</a></p>`) 116 test( 117 util.URLJoin(TestAppURL, "gogitea", "some-repo-name", "issues", "12345"), 118 `<p><a href="`+util.URLJoin(TestAppURL, "gogitea", "some-repo-name", "issues", "12345")+`" class="ref-issue" rel="nofollow">gogitea/some-repo-name#12345</a></p>`) 119 } 120 121 func TestMisc_IsSameDomain(t *testing.T) { 122 setting.AppURL = TestAppURL 123 124 sha := "b6dd6210eaebc915fd5be5579c58cce4da2e2579" 125 commit := util.URLJoin(TestRepoURL, "commit", sha) 126 127 assert.True(t, IsSameDomain(commit)) 128 assert.False(t, IsSameDomain("http://google.com/ncr")) 129 assert.False(t, IsSameDomain("favicon.ico")) 130 } 131 132 func TestRender_links(t *testing.T) { 133 setting.AppURL = TestAppURL 134 135 test := func(input, expected string) { 136 buffer, err := RenderString(&RenderContext{ 137 RelativePath: "a.md", 138 URLPrefix: TestRepoURL, 139 }, input) 140 assert.NoError(t, err) 141 assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) 142 } 143 // Text that should be turned into URL 144 145 defaultCustom := setting.Markdown.CustomURLSchemes 146 setting.Markdown.CustomURLSchemes = []string{"ftp", "magnet"} 147 InitializeSanitizer() 148 CustomLinkURLSchemes(setting.Markdown.CustomURLSchemes) 149 150 test( 151 "https://www.example.com", 152 `<p><a href="https://www.example.com" rel="nofollow">https://www.example.com</a></p>`) 153 test( 154 "http://www.example.com", 155 `<p><a href="http://www.example.com" rel="nofollow">http://www.example.com</a></p>`) 156 test( 157 "https://example.com", 158 `<p><a href="https://example.com" rel="nofollow">https://example.com</a></p>`) 159 test( 160 "http://example.com", 161 `<p><a href="http://example.com" rel="nofollow">http://example.com</a></p>`) 162 test( 163 "http://foo.com/blah_blah", 164 `<p><a href="http://foo.com/blah_blah" rel="nofollow">http://foo.com/blah_blah</a></p>`) 165 test( 166 "http://foo.com/blah_blah/", 167 `<p><a href="http://foo.com/blah_blah/" rel="nofollow">http://foo.com/blah_blah/</a></p>`) 168 test( 169 "http://www.example.com/wpstyle/?p=364", 170 `<p><a href="http://www.example.com/wpstyle/?p=364" rel="nofollow">http://www.example.com/wpstyle/?p=364</a></p>`) 171 test( 172 "https://www.example.com/foo/?bar=baz&inga=42&quux", 173 `<p><a href="https://www.example.com/foo/?bar=baz&inga=42&quux" rel="nofollow">https://www.example.com/foo/?bar=baz&inga=42&quux</a></p>`) 174 test( 175 "http://142.42.1.1/", 176 `<p><a href="http://142.42.1.1/" rel="nofollow">http://142.42.1.1/</a></p>`) 177 test( 178 "https://github.com/go-gitea/gitea/?p=aaa/bbb.html#ccc-ddd", 179 `<p><a href="https://github.com/go-gitea/gitea/?p=aaa/bbb.html#ccc-ddd" rel="nofollow">https://github.com/go-gitea/gitea/?p=aaa/bbb.html#ccc-ddd</a></p>`) 180 test( 181 "https://en.wikipedia.org/wiki/URL_(disambiguation)", 182 `<p><a href="https://en.wikipedia.org/wiki/URL_(disambiguation)" rel="nofollow">https://en.wikipedia.org/wiki/URL_(disambiguation)</a></p>`) 183 test( 184 "https://foo_bar.example.com/", 185 `<p><a href="https://foo_bar.example.com/" rel="nofollow">https://foo_bar.example.com/</a></p>`) 186 test( 187 "https://stackoverflow.com/questions/2896191/what-is-go-used-fore", 188 `<p><a href="https://stackoverflow.com/questions/2896191/what-is-go-used-fore" rel="nofollow">https://stackoverflow.com/questions/2896191/what-is-go-used-fore</a></p>`) 189 test( 190 "https://username:password@gitea.com", 191 `<p><a href="https://username:password@gitea.com" rel="nofollow">https://username:password@gitea.com</a></p>`) 192 test( 193 "ftp://gitea.com/file.txt", 194 `<p><a href="ftp://gitea.com/file.txt" rel="nofollow">ftp://gitea.com/file.txt</a></p>`) 195 test( 196 "magnet:?xt=urn:btih:5dee65101db281ac9c46344cd6b175cdcadabcde&dn=download", 197 `<p><a href="magnet:?xt=urn:btih:5dee65101db281ac9c46344cd6b175cdcadabcde&dn=download" rel="nofollow">magnet:?xt=urn:btih:5dee65101db281ac9c46344cd6b175cdcadabcde&dn=download</a></p>`) 198 199 // Test that should *not* be turned into URL 200 test( 201 "www.example.com", 202 `<p>www.example.com</p>`) 203 test( 204 "example.com", 205 `<p>example.com</p>`) 206 test( 207 "test.example.com", 208 `<p>test.example.com</p>`) 209 test( 210 "http://", 211 `<p>http://</p>`) 212 test( 213 "https://", 214 `<p>https://</p>`) 215 test( 216 "://", 217 `<p>://</p>`) 218 test( 219 "www", 220 `<p>www</p>`) 221 test( 222 "ftps://gitea.com", 223 `<p>ftps://gitea.com</p>`) 224 225 // Restore previous settings 226 setting.Markdown.CustomURLSchemes = defaultCustom 227 InitializeSanitizer() 228 CustomLinkURLSchemes(setting.Markdown.CustomURLSchemes) 229 } 230 231 func TestRender_email(t *testing.T) { 232 setting.AppURL = TestAppURL 233 234 test := func(input, expected string) { 235 res, err := RenderString(&RenderContext{ 236 RelativePath: "a.md", 237 URLPrefix: TestRepoURL, 238 }, input) 239 assert.NoError(t, err) 240 assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(res)) 241 } 242 // Text that should be turned into email link 243 244 test( 245 "info@gitea.com", 246 `<p><a href="mailto:info@gitea.com" rel="nofollow">info@gitea.com</a></p>`) 247 test( 248 "(info@gitea.com)", 249 `<p>(<a href="mailto:info@gitea.com" rel="nofollow">info@gitea.com</a>)</p>`) 250 test( 251 "[info@gitea.com]", 252 `<p>[<a href="mailto:info@gitea.com" rel="nofollow">info@gitea.com</a>]</p>`) 253 test( 254 "info@gitea.com.", 255 `<p><a href="mailto:info@gitea.com" rel="nofollow">info@gitea.com</a>.</p>`) 256 test( 257 "firstname+lastname@gitea.com", 258 `<p><a href="mailto:firstname+lastname@gitea.com" rel="nofollow">firstname+lastname@gitea.com</a></p>`) 259 test( 260 "send email to info@gitea.co.uk.", 261 `<p>send email to <a href="mailto:info@gitea.co.uk" rel="nofollow">info@gitea.co.uk</a>.</p>`) 262 263 // Test that should *not* be turned into email links 264 test( 265 "\"info@gitea.com\"", 266 `<p>"info@gitea.com"</p>`) 267 test( 268 "/home/gitea/mailstore/info@gitea/com", 269 `<p>/home/gitea/mailstore/info@gitea/com</p>`) 270 test( 271 "git@try.gitea.io:go-gitea/gitea.git", 272 `<p>git@try.gitea.io:go-gitea/gitea.git</p>`) 273 test( 274 "gitea@3", 275 `<p>gitea@3</p>`) 276 test( 277 "gitea@gmail.c", 278 `<p>gitea@gmail.c</p>`) 279 test( 280 "email@domain@domain.com", 281 `<p>email@domain@domain.com</p>`) 282 test( 283 "email@domain..com", 284 `<p>email@domain..com</p>`) 285 } 286 287 func TestRender_emoji(t *testing.T) { 288 setting.AppURL = TestAppURL 289 setting.StaticURLPrefix = TestAppURL 290 291 test := func(input, expected string) { 292 expected = strings.ReplaceAll(expected, "&", "&") 293 buffer, err := RenderString(&RenderContext{ 294 RelativePath: "a.md", 295 URLPrefix: TestRepoURL, 296 }, input) 297 assert.NoError(t, err) 298 assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) 299 } 300 301 // Make sure we can successfully match every emoji in our dataset with regex 302 for i := range emoji.GemojiData { 303 test( 304 emoji.GemojiData[i].Emoji, 305 `<p><span class="emoji" aria-label="`+emoji.GemojiData[i].Description+`">`+emoji.GemojiData[i].Emoji+`</span></p>`) 306 } 307 for i := range emoji.GemojiData { 308 test( 309 ":"+emoji.GemojiData[i].Aliases[0]+":", 310 `<p><span class="emoji" aria-label="`+emoji.GemojiData[i].Description+`">`+emoji.GemojiData[i].Emoji+`</span></p>`) 311 } 312 313 // Text that should be turned into or recognized as emoji 314 test( 315 ":gitea:", 316 `<p><span class="emoji" aria-label="gitea"><img alt=":gitea:" src="`+setting.StaticURLPrefix+`/assets/img/emoji/gitea.png"/></span></p>`) 317 test( 318 ":custom-emoji:", 319 `<p>:custom-emoji:</p>`) 320 setting.UI.CustomEmojisMap["custom-emoji"] = ":custom-emoji:" 321 test( 322 ":custom-emoji:", 323 `<p><span class="emoji" aria-label="custom-emoji"><img alt=":custom-emoji:" src="`+setting.StaticURLPrefix+`/assets/img/emoji/custom-emoji.png"/></span></p>`) 324 test( 325 "θΏζ―ε符:1::+1: someπ \U0001f44d:custom-emoji: :gitea:", 326 `<p>θΏζ―ε符:1:<span class="emoji" aria-label="thumbs up">π</span> some<span class="emoji" aria-label="crocodile">π</span> `+ 327 `<span class="emoji" aria-label="thumbs up">π</span><span class="emoji" aria-label="custom-emoji"><img alt=":custom-emoji:" src="`+setting.StaticURLPrefix+`/assets/img/emoji/custom-emoji.png"/></span> `+ 328 `<span class="emoji" aria-label="gitea"><img alt=":gitea:" src="`+setting.StaticURLPrefix+`/assets/img/emoji/gitea.png"/></span></p>`) 329 test( 330 "Some text with π in the middle", 331 `<p>Some text with <span class="emoji" aria-label="grinning face with smiling eyes">π</span> in the middle</p>`) 332 test( 333 "Some text with :smile: in the middle", 334 `<p>Some text with <span class="emoji" aria-label="grinning face with smiling eyes">π</span> in the middle</p>`) 335 test( 336 "Some text with ππ 2 emoji next to each other", 337 `<p>Some text with <span class="emoji" aria-label="grinning face with smiling eyes">π</span><span class="emoji" aria-label="grinning face with smiling eyes">π</span> 2 emoji next to each other</p>`) 338 test( 339 "ππ€ͺππ€β", 340 `<p><span class="emoji" aria-label="smiling face with sunglasses">π</span><span class="emoji" aria-label="zany face">π€ͺ</span><span class="emoji" aria-label="locked with key">π</span><span class="emoji" aria-label="money-mouth face">π€</span><span class="emoji" aria-label="question mark">β</span></p>`) 341 342 // should match nothing 343 test( 344 "2001:0db8:85a3:0000:0000:8a2e:0370:7334", 345 `<p>2001:0db8:85a3:0000:0000:8a2e:0370:7334</p>`) 346 test( 347 ":not exist:", 348 `<p>:not exist:</p>`) 349 } 350 351 func TestRender_ShortLinks(t *testing.T) { 352 setting.AppURL = TestAppURL 353 tree := util.URLJoin(TestRepoURL, "src", "master") 354 355 test := func(input, expected, expectedWiki string) { 356 buffer, err := markdown.RenderString(&RenderContext{ 357 URLPrefix: tree, 358 }, input) 359 assert.NoError(t, err) 360 assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) 361 buffer, err = markdown.RenderString(&RenderContext{ 362 URLPrefix: TestRepoURL, 363 Metas: localMetas, 364 IsWiki: true, 365 }, input) 366 assert.NoError(t, err) 367 assert.Equal(t, strings.TrimSpace(expectedWiki), strings.TrimSpace(buffer)) 368 } 369 370 rawtree := util.URLJoin(TestRepoURL, "raw", "master") 371 url := util.URLJoin(tree, "Link") 372 otherURL := util.URLJoin(tree, "Other-Link") 373 encodedURL := util.URLJoin(tree, "Link%3F") 374 imgurl := util.URLJoin(rawtree, "Link.jpg") 375 otherImgurl := util.URLJoin(rawtree, "Link+Other.jpg") 376 encodedImgurl := util.URLJoin(rawtree, "Link+%23.jpg") 377 notencodedImgurl := util.URLJoin(rawtree, "some", "path", "Link+#.jpg") 378 urlWiki := util.URLJoin(TestRepoURL, "wiki", "Link") 379 otherURLWiki := util.URLJoin(TestRepoURL, "wiki", "Other-Link") 380 encodedURLWiki := util.URLJoin(TestRepoURL, "wiki", "Link%3F") 381 imgurlWiki := util.URLJoin(TestRepoURL, "wiki", "raw", "Link.jpg") 382 otherImgurlWiki := util.URLJoin(TestRepoURL, "wiki", "raw", "Link+Other.jpg") 383 encodedImgurlWiki := util.URLJoin(TestRepoURL, "wiki", "raw", "Link+%23.jpg") 384 notencodedImgurlWiki := util.URLJoin(TestRepoURL, "wiki", "raw", "some", "path", "Link+#.jpg") 385 favicon := "http://google.com/favicon.ico" 386 387 test( 388 "[[Link]]", 389 `<p><a href="`+url+`" rel="nofollow">Link</a></p>`, 390 `<p><a href="`+urlWiki+`" rel="nofollow">Link</a></p>`) 391 test( 392 "[[Link.jpg]]", 393 `<p><a href="`+imgurl+`" rel="nofollow"><img src="`+imgurl+`" title="Link.jpg" alt="Link.jpg"/></a></p>`, 394 `<p><a href="`+imgurlWiki+`" rel="nofollow"><img src="`+imgurlWiki+`" title="Link.jpg" alt="Link.jpg"/></a></p>`) 395 test( 396 "[["+favicon+"]]", 397 `<p><a href="`+favicon+`" rel="nofollow"><img src="`+favicon+`" title="favicon.ico" alt="`+favicon+`"/></a></p>`, 398 `<p><a href="`+favicon+`" rel="nofollow"><img src="`+favicon+`" title="favicon.ico" alt="`+favicon+`"/></a></p>`) 399 test( 400 "[[Name|Link]]", 401 `<p><a href="`+url+`" rel="nofollow">Name</a></p>`, 402 `<p><a href="`+urlWiki+`" rel="nofollow">Name</a></p>`) 403 test( 404 "[[Name|Link.jpg]]", 405 `<p><a href="`+imgurl+`" rel="nofollow"><img src="`+imgurl+`" title="Name" alt="Name"/></a></p>`, 406 `<p><a href="`+imgurlWiki+`" rel="nofollow"><img src="`+imgurlWiki+`" title="Name" alt="Name"/></a></p>`) 407 test( 408 "[[Name|Link.jpg|alt=AltName]]", 409 `<p><a href="`+imgurl+`" rel="nofollow"><img src="`+imgurl+`" title="AltName" alt="AltName"/></a></p>`, 410 `<p><a href="`+imgurlWiki+`" rel="nofollow"><img src="`+imgurlWiki+`" title="AltName" alt="AltName"/></a></p>`) 411 test( 412 "[[Name|Link.jpg|title=Title]]", 413 `<p><a href="`+imgurl+`" rel="nofollow"><img src="`+imgurl+`" title="Title" alt="Title"/></a></p>`, 414 `<p><a href="`+imgurlWiki+`" rel="nofollow"><img src="`+imgurlWiki+`" title="Title" alt="Title"/></a></p>`) 415 test( 416 "[[Name|Link.jpg|alt=AltName|title=Title]]", 417 `<p><a href="`+imgurl+`" rel="nofollow"><img src="`+imgurl+`" title="Title" alt="AltName"/></a></p>`, 418 `<p><a href="`+imgurlWiki+`" rel="nofollow"><img src="`+imgurlWiki+`" title="Title" alt="AltName"/></a></p>`) 419 test( 420 "[[Name|Link.jpg|alt=\"AltName\"|title='Title']]", 421 `<p><a href="`+imgurl+`" rel="nofollow"><img src="`+imgurl+`" title="Title" alt="AltName"/></a></p>`, 422 `<p><a href="`+imgurlWiki+`" rel="nofollow"><img src="`+imgurlWiki+`" title="Title" alt="AltName"/></a></p>`) 423 test( 424 "[[Name|Link Other.jpg|alt=\"AltName\"|title='Title']]", 425 `<p><a href="`+otherImgurl+`" rel="nofollow"><img src="`+otherImgurl+`" title="Title" alt="AltName"/></a></p>`, 426 `<p><a href="`+otherImgurlWiki+`" rel="nofollow"><img src="`+otherImgurlWiki+`" title="Title" alt="AltName"/></a></p>`) 427 test( 428 "[[Link]] [[Other Link]]", 429 `<p><a href="`+url+`" rel="nofollow">Link</a> <a href="`+otherURL+`" rel="nofollow">Other Link</a></p>`, 430 `<p><a href="`+urlWiki+`" rel="nofollow">Link</a> <a href="`+otherURLWiki+`" rel="nofollow">Other Link</a></p>`) 431 test( 432 "[[Link?]]", 433 `<p><a href="`+encodedURL+`" rel="nofollow">Link?</a></p>`, 434 `<p><a href="`+encodedURLWiki+`" rel="nofollow">Link?</a></p>`) 435 test( 436 "[[Link]] [[Other Link]] [[Link?]]", 437 `<p><a href="`+url+`" rel="nofollow">Link</a> <a href="`+otherURL+`" rel="nofollow">Other Link</a> <a href="`+encodedURL+`" rel="nofollow">Link?</a></p>`, 438 `<p><a href="`+urlWiki+`" rel="nofollow">Link</a> <a href="`+otherURLWiki+`" rel="nofollow">Other Link</a> <a href="`+encodedURLWiki+`" rel="nofollow">Link?</a></p>`) 439 test( 440 "[[Link #.jpg]]", 441 `<p><a href="`+encodedImgurl+`" rel="nofollow"><img src="`+encodedImgurl+`" title="Link #.jpg" alt="Link #.jpg"/></a></p>`, 442 `<p><a href="`+encodedImgurlWiki+`" rel="nofollow"><img src="`+encodedImgurlWiki+`" title="Link #.jpg" alt="Link #.jpg"/></a></p>`) 443 test( 444 "[[Name|Link #.jpg|alt=\"AltName\"|title='Title']]", 445 `<p><a href="`+encodedImgurl+`" rel="nofollow"><img src="`+encodedImgurl+`" title="Title" alt="AltName"/></a></p>`, 446 `<p><a href="`+encodedImgurlWiki+`" rel="nofollow"><img src="`+encodedImgurlWiki+`" title="Title" alt="AltName"/></a></p>`) 447 test( 448 "[[some/path/Link #.jpg]]", 449 `<p><a href="`+notencodedImgurl+`" rel="nofollow"><img src="`+notencodedImgurl+`" title="Link #.jpg" alt="some/path/Link #.jpg"/></a></p>`, 450 `<p><a href="`+notencodedImgurlWiki+`" rel="nofollow"><img src="`+notencodedImgurlWiki+`" title="Link #.jpg" alt="some/path/Link #.jpg"/></a></p>`) 451 test( 452 "<p><a href=\"https://example.org\">[[foobar]]</a></p>", 453 `<p><a href="https://example.org" rel="nofollow">[[foobar]]</a></p>`, 454 `<p><a href="https://example.org" rel="nofollow">[[foobar]]</a></p>`) 455 } 456 457 func TestRender_RelativeImages(t *testing.T) { 458 setting.AppURL = TestAppURL 459 tree := util.URLJoin(TestRepoURL, "src", "master") 460 461 test := func(input, expected, expectedWiki string) { 462 buffer, err := markdown.RenderString(&RenderContext{ 463 URLPrefix: tree, 464 Metas: localMetas, 465 }, input) 466 assert.NoError(t, err) 467 assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) 468 buffer, err = markdown.RenderString(&RenderContext{ 469 URLPrefix: TestRepoURL, 470 Metas: localMetas, 471 IsWiki: true, 472 }, input) 473 assert.NoError(t, err) 474 assert.Equal(t, strings.TrimSpace(expectedWiki), strings.TrimSpace(buffer)) 475 } 476 477 rawwiki := util.URLJoin(TestRepoURL, "wiki", "raw") 478 mediatree := util.URLJoin(TestRepoURL, "media", "master") 479 480 test( 481 `<img src="Link">`, 482 `<img src="`+util.URLJoin(mediatree, "Link")+`"/>`, 483 `<img src="`+util.URLJoin(rawwiki, "Link")+`"/>`) 484 485 test( 486 `<img src="./icon.png">`, 487 `<img src="`+util.URLJoin(mediatree, "icon.png")+`"/>`, 488 `<img src="`+util.URLJoin(rawwiki, "icon.png")+`"/>`) 489 } 490 491 func Test_ParseClusterFuzz(t *testing.T) { 492 setting.AppURL = TestAppURL 493 494 localMetas := map[string]string{ 495 "user": "go-gitea", 496 "repo": "gitea", 497 } 498 499 data := "<A><maTH><tr><MN><bodY ΓΏ><temPlate></template><tH><tr></A><tH><d<bodY " 500 501 var res strings.Builder 502 err := PostProcess(&RenderContext{ 503 URLPrefix: "https://example.com", 504 Metas: localMetas, 505 }, strings.NewReader(data), &res) 506 assert.NoError(t, err) 507 assert.NotContains(t, res.String(), "<html") 508 509 data = "<!DOCTYPE html>\n<A><maTH><tr><MN><bodY ΓΏ><temPlate></template><tH><tr></A><tH><d<bodY " 510 511 res.Reset() 512 err = PostProcess(&RenderContext{ 513 URLPrefix: "https://example.com", 514 Metas: localMetas, 515 }, strings.NewReader(data), &res) 516 517 assert.NoError(t, err) 518 assert.NotContains(t, res.String(), "<html") 519 } 520 521 func TestIssue16020(t *testing.T) { 522 setting.AppURL = TestAppURL 523 524 localMetas := map[string]string{ 525 "user": "go-gitea", 526 "repo": "gitea", 527 } 528 529 data := `<img src="data:image/png;base64,i//V"/>` 530 531 var res strings.Builder 532 err := PostProcess(&RenderContext{ 533 URLPrefix: "https://example.com", 534 Metas: localMetas, 535 }, strings.NewReader(data), &res) 536 assert.NoError(t, err) 537 assert.Equal(t, data, res.String()) 538 } 539 540 func BenchmarkEmojiPostprocess(b *testing.B) { 541 data := "π₯° " 542 for len(data) < 1<<16 { 543 data += data 544 } 545 b.ResetTimer() 546 for i := 0; i < b.N; i++ { 547 var res strings.Builder 548 err := PostProcess(&RenderContext{ 549 URLPrefix: "https://example.com", 550 Metas: localMetas, 551 }, strings.NewReader(data), &res) 552 assert.NoError(b, err) 553 } 554 } 555 556 func TestFuzz(t *testing.T) { 557 s := "t/l/issues/8#/../../a" 558 renderContext := RenderContext{ 559 URLPrefix: "https://example.com/go-gitea/gitea", 560 Metas: map[string]string{ 561 "user": "go-gitea", 562 "repo": "gitea", 563 }, 564 } 565 566 err := PostProcess(&renderContext, strings.NewReader(s), io.Discard) 567 568 assert.NoError(t, err) 569 } 570 571 func TestIssue18471(t *testing.T) { 572 data := `http://domain/org/repo/compare/783b039...da951ce` 573 574 var res strings.Builder 575 err := PostProcess(&RenderContext{ 576 URLPrefix: "https://example.com", 577 Metas: localMetas, 578 }, strings.NewReader(data), &res) 579 580 assert.NoError(t, err) 581 assert.Equal(t, res.String(), "<a href=\"http://domain/org/repo/compare/783b039...da951ce\" class=\"compare\"><code class=\"nohighlight\">783b039...da951ce</code></a>") 582 }