You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

html_test.go 22KB

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