loading up the forgejo repo on tangled to test page performance
1// Copyright 2017 The Gitea Authors. All rights reserved.
2// Copyright 2025 The Forgejo Authors.
3// SPDX-License-Identifier: MIT
4
5package markup_test
6
7import (
8 "context"
9 "io"
10 "os"
11 "strings"
12 "testing"
13
14 "forgejo.org/models/unittest"
15 "forgejo.org/modules/emoji"
16 "forgejo.org/modules/git"
17 "forgejo.org/modules/log"
18 "forgejo.org/modules/markup"
19 "forgejo.org/modules/markup/markdown"
20 "forgejo.org/modules/setting"
21 "forgejo.org/modules/test"
22 "forgejo.org/modules/translation"
23 "forgejo.org/modules/util"
24
25 "github.com/stretchr/testify/assert"
26 "github.com/stretchr/testify/require"
27)
28
29var localMetas = map[string]string{
30 "user": "gogits",
31 "repo": "gogs",
32 "repoPath": "../../tests/gitea-repositories-meta/user13/repo11.git/",
33}
34
35func TestMain(m *testing.M) {
36 unittest.InitSettings()
37 if err := git.InitSimple(context.Background()); err != nil {
38 log.Fatal("git init failed, err: %v", err)
39 }
40 os.Exit(m.Run())
41}
42
43func TestRender_Commits(t *testing.T) {
44 setting.AppURL = markup.TestAppURL
45 test := func(input, expected string) {
46 buffer, err := markup.RenderString(&markup.RenderContext{
47 Ctx: git.DefaultContext,
48 RelativePath: ".md",
49 Links: markup.Links{
50 AbsolutePrefix: true,
51 Base: markup.TestRepoURL,
52 },
53 Metas: localMetas,
54 }, input)
55 require.NoError(t, err)
56 assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
57 }
58
59 sha := "65f1bf27bc3bf70f64657658635e66094edbcb4d"
60 repo := markup.TestRepoURL
61 commit := util.URLJoin(repo, "commit", sha)
62 tree := util.URLJoin(repo, "tree", sha, "src")
63
64 file := util.URLJoin(repo, "commit", sha, "example.txt")
65 fileWithExtra := file + ":"
66 fileWithHash := file + "#L2"
67 fileWithHasExtra := file + "#L2:"
68 commitCompare := util.URLJoin(repo, "compare", sha+"..."+sha)
69 commitCompareWithHash := commitCompare + "#L2"
70
71 test(sha, `<p><a href="`+commit+`" rel="nofollow"><code>65f1bf27bc</code></a></p>`)
72 test(sha[:7], `<p><a href="`+commit[:len(commit)-(40-7)]+`" rel="nofollow"><code>65f1bf2</code></a></p>`)
73 test(sha[:39], `<p><a href="`+commit[:len(commit)-(40-39)]+`" rel="nofollow"><code>65f1bf27bc</code></a></p>`)
74 test(commit, `<p><a href="`+commit+`" rel="nofollow"><code>65f1bf27bc</code></a></p>`)
75 test(tree, `<p><a href="`+tree+`" rel="nofollow"><code>65f1bf27bc/src</code></a></p>`)
76
77 test(file, `<p><a href="`+file+`" rel="nofollow"><code>65f1bf27bc/example.txt</code></a></p>`)
78 test(fileWithExtra, `<p><a href="`+file+`" rel="nofollow"><code>65f1bf27bc/example.txt</code></a>:</p>`)
79 test(fileWithHash, `<p><a href="`+fileWithHash+`" rel="nofollow"><code>65f1bf27bc/example.txt (L2)</code></a></p>`)
80 test(fileWithHasExtra, `<p><a href="`+fileWithHash+`" rel="nofollow"><code>65f1bf27bc/example.txt (L2)</code></a>:</p>`)
81 test(commitCompare, `<p><a href="`+commitCompare+`" rel="nofollow"><code>65f1bf27bc...65f1bf27bc</code></a></p>`)
82 test(commitCompareWithHash, `<p><a href="`+commitCompareWithHash+`" rel="nofollow"><code>65f1bf27bc...65f1bf27bc (L2)</code></a></p>`)
83
84 test("commit "+sha, `<p>commit <a href="`+commit+`" rel="nofollow"><code>65f1bf27bc</code></a></p>`)
85 test("/home/gitea/"+sha, "<p>/home/gitea/"+sha+"</p>")
86 test("deadbeef", `<p>deadbeef</p>`)
87 test("d27ace93", `<p>d27ace93</p>`)
88 test(sha[:14]+".x", `<p>`+sha[:14]+`.x</p>`)
89
90 expected14 := `<a href="` + commit[:len(commit)-(40-14)] + `" rel="nofollow"><code>` + sha[:10] + `</code></a>`
91 test(sha[:14]+".", `<p>`+expected14+`.</p>`)
92 test(sha[:14]+",", `<p>`+expected14+`,</p>`)
93 test("["+sha[:14]+"]", `<p>[`+expected14+`]</p>`)
94}
95
96func TestRender_CrossReferences(t *testing.T) {
97 setting.AppURL = markup.TestAppURL
98
99 test := func(input, expected string) {
100 buffer, err := markup.RenderString(&markup.RenderContext{
101 Ctx: git.DefaultContext,
102 RelativePath: "a.md",
103 Links: markup.Links{
104 AbsolutePrefix: true,
105 Base: setting.AppSubURL,
106 },
107 Metas: localMetas,
108 }, input)
109 require.NoError(t, err)
110 assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
111 }
112
113 test(
114 "gogits/gogs#12345",
115 `<p><a href="`+util.URLJoin(markup.TestAppURL, "gogits", "gogs", "issues", "12345")+`" class="ref-issue" rel="nofollow">gogits/gogs#12345</a></p>`)
116 test(
117 "go-gitea/gitea#12345",
118 `<p><a href="`+util.URLJoin(markup.TestAppURL, "go-gitea", "gitea", "issues", "12345")+`" class="ref-issue" rel="nofollow">go-gitea/gitea#12345</a></p>`)
119 test(
120 "/home/gitea/go-gitea/gitea#12345",
121 `<p>/home/gitea/go-gitea/gitea#12345</p>`)
122 test(
123 util.URLJoin(markup.TestAppURL, "gogitea", "gitea", "issues", "12345"),
124 `<p><a href="`+util.URLJoin(markup.TestAppURL, "gogitea", "gitea", "issues", "12345")+`" class="ref-issue" rel="nofollow">gogitea/gitea#12345</a></p>`)
125 test(
126 util.URLJoin(markup.TestAppURL, "go-gitea", "gitea", "issues", "12345"),
127 `<p><a href="`+util.URLJoin(markup.TestAppURL, "go-gitea", "gitea", "issues", "12345")+`" class="ref-issue" rel="nofollow">go-gitea/gitea#12345</a></p>`)
128 test(
129 util.URLJoin(markup.TestAppURL, "gogitea", "some-repo-name", "issues", "12345"),
130 `<p><a href="`+util.URLJoin(markup.TestAppURL, "gogitea", "some-repo-name", "issues", "12345")+`" class="ref-issue" rel="nofollow">gogitea/some-repo-name#12345</a></p>`)
131
132 sha := "65f1bf27bc3bf70f64657658635e66094edbcb4d"
133 urlWithQuery := util.URLJoin(markup.TestAppURL, "forgejo", "some-repo-name", "commit", sha, "README.md") + "?display=source#L1-L5"
134 test(
135 urlWithQuery,
136 `<p><a href="`+urlWithQuery+`" rel="nofollow"><code>`+sha[:10]+`/README.md (L1-L5)</code></a></p>`)
137}
138
139func TestRender_links(t *testing.T) {
140 setting.AppURL = markup.TestAppURL
141
142 test := func(input, expected string) {
143 buffer, err := markup.RenderString(&markup.RenderContext{
144 Ctx: git.DefaultContext,
145 RelativePath: "a.md",
146 Links: markup.Links{
147 Base: markup.TestRepoURL,
148 },
149 }, input)
150 require.NoError(t, err)
151 assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
152 }
153 // Text that should be turned into URL
154
155 defaultCustom := setting.Markdown.CustomURLSchemes
156 setting.Markdown.CustomURLSchemes = []string{"ftp", "magnet"}
157 markup.InitializeSanitizer()
158 markup.CustomLinkURLSchemes(setting.Markdown.CustomURLSchemes)
159
160 test(
161 "https://www.example.com",
162 `<p><a href="https://www.example.com" rel="nofollow">https://www.example.com</a></p>`)
163 test(
164 "http://www.example.com",
165 `<p><a href="http://www.example.com" rel="nofollow">http://www.example.com</a></p>`)
166 test(
167 "https://example.com",
168 `<p><a href="https://example.com" rel="nofollow">https://example.com</a></p>`)
169 test(
170 "http://example.com",
171 `<p><a href="http://example.com" rel="nofollow">http://example.com</a></p>`)
172 test(
173 "http://foo.com/blah_blah",
174 `<p><a href="http://foo.com/blah_blah" rel="nofollow">http://foo.com/blah_blah</a></p>`)
175 test(
176 "http://foo.com/blah_blah/",
177 `<p><a href="http://foo.com/blah_blah/" rel="nofollow">http://foo.com/blah_blah/</a></p>`)
178 test(
179 "http://www.example.com/wpstyle/?p=364",
180 `<p><a href="http://www.example.com/wpstyle/?p=364" rel="nofollow">http://www.example.com/wpstyle/?p=364</a></p>`)
181 test(
182 "https://www.example.com/foo/?bar=baz&inga=42&quux",
183 `<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>`)
184 test(
185 "http://142.42.1.1/",
186 `<p><a href="http://142.42.1.1/" rel="nofollow">http://142.42.1.1/</a></p>`)
187 test(
188 "https://github.com/go-gitea/gitea/?p=aaa/bbb.html#ccc-ddd",
189 `<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>`)
190 test(
191 "https://en.wikipedia.org/wiki/URL_(disambiguation)",
192 `<p><a href="https://en.wikipedia.org/wiki/URL_(disambiguation)" rel="nofollow">https://en.wikipedia.org/wiki/URL_(disambiguation)</a></p>`)
193 test(
194 "https://foo_bar.example.com/",
195 `<p><a href="https://foo_bar.example.com/" rel="nofollow">https://foo_bar.example.com/</a></p>`)
196 test(
197 "https://stackoverflow.com/questions/2896191/what-is-go-used-fore",
198 `<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>`)
199 test(
200 "https://username:password@gitea.com",
201 `<p><a href="https://username:password@gitea.com" rel="nofollow">https://username:password@gitea.com</a></p>`)
202 test(
203 "ftp://gitea.com/file.txt",
204 `<p><a href="ftp://gitea.com/file.txt" rel="nofollow">ftp://gitea.com/file.txt</a></p>`)
205 test(
206 "magnet:?xt=urn:btih:5dee65101db281ac9c46344cd6b175cdcadabcde&dn=download",
207 `<p><a href="magnet:?xt=urn:btih:5dee65101db281ac9c46344cd6b175cdcadabcde&dn=download" rel="nofollow">magnet:?xt=urn:btih:5dee65101db281ac9c46344cd6b175cdcadabcde&dn=download</a></p>`)
208
209 // Test that should *not* be turned into URL
210 test(
211 "www.example.com",
212 `<p>www.example.com</p>`)
213 test(
214 "example.com",
215 `<p>example.com</p>`)
216 test(
217 "test.example.com",
218 `<p>test.example.com</p>`)
219 test(
220 "http://",
221 `<p>http://</p>`)
222 test(
223 "https://",
224 `<p>https://</p>`)
225 test(
226 "://",
227 `<p>://</p>`)
228 test(
229 "www",
230 `<p>www</p>`)
231 test(
232 "ftps://gitea.com",
233 `<p>ftps://gitea.com</p>`)
234
235 // Restore previous settings
236 setting.Markdown.CustomURLSchemes = defaultCustom
237 markup.InitializeSanitizer()
238 markup.CustomLinkURLSchemes(setting.Markdown.CustomURLSchemes)
239}
240
241func TestRender_email(t *testing.T) {
242 setting.AppURL = markup.TestAppURL
243
244 test := func(input, expected string) {
245 res, err := markup.RenderString(&markup.RenderContext{
246 Ctx: git.DefaultContext,
247 RelativePath: "a.md",
248 Links: markup.Links{
249 Base: markup.TestRepoURL,
250 },
251 }, input)
252 require.NoError(t, err)
253 assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(res))
254 }
255 // Text that should be turned into email link
256
257 test(
258 "info@gitea.com",
259 `<p><a href="mailto:info@gitea.com" rel="nofollow">info@gitea.com</a></p>`)
260 test(
261 "(info@gitea.com)",
262 `<p>(<a href="mailto:info@gitea.com" rel="nofollow">info@gitea.com</a>)</p>`)
263 test(
264 "[info@gitea.com]",
265 `<p>[<a href="mailto:info@gitea.com" rel="nofollow">info@gitea.com</a>]</p>`)
266 test(
267 "info@gitea.com.",
268 `<p><a href="mailto:info@gitea.com" rel="nofollow">info@gitea.com</a>.</p>`)
269 test(
270 "firstname+lastname@gitea.com",
271 `<p><a href="mailto:firstname+lastname@gitea.com" rel="nofollow">firstname+lastname@gitea.com</a></p>`)
272 test(
273 "send email to info@gitea.co.uk.",
274 `<p>send email to <a href="mailto:info@gitea.co.uk" rel="nofollow">info@gitea.co.uk</a>.</p>`)
275
276 test(
277 `j.doe@example.com,
278 j.doe@example.com.
279 j.doe@example.com;
280 j.doe@example.com?
281 j.doe@example.com!`,
282 `<p><a href="mailto:j.doe@example.com" rel="nofollow">j.doe@example.com</a>,<br/>
283<a href="mailto:j.doe@example.com" rel="nofollow">j.doe@example.com</a>.<br/>
284<a href="mailto:j.doe@example.com" rel="nofollow">j.doe@example.com</a>;<br/>
285<a href="mailto:j.doe@example.com" rel="nofollow">j.doe@example.com</a>?<br/>
286<a href="mailto:j.doe@example.com" rel="nofollow">j.doe@example.com</a>!</p>`)
287
288 // Test that should *not* be turned into email links
289 test(
290 "\"info@gitea.com\"",
291 `<p>"info@gitea.com"</p>`)
292 test(
293 "/home/gitea/mailstore/info@gitea/com",
294 `<p>/home/gitea/mailstore/info@gitea/com</p>`)
295 test(
296 "git@try.gitea.io:go-gitea/gitea.git",
297 `<p>git@try.gitea.io:go-gitea/gitea.git</p>`)
298 test(
299 "gitea@3",
300 `<p>gitea@3</p>`)
301 test(
302 "gitea@gmail.c",
303 `<p>gitea@gmail.c</p>`)
304 test(
305 "email@domain@domain.com",
306 `<p>email@domain@domain.com</p>`)
307 test(
308 "email@domain..com",
309 `<p>email@domain..com</p>`)
310}
311
312func TestRender_emoji(t *testing.T) {
313 setting.AppURL = markup.TestAppURL
314 setting.StaticURLPrefix = markup.TestAppURL
315
316 test := func(input, expected string) {
317 expected = strings.ReplaceAll(expected, "&", "&")
318 buffer, err := markup.RenderString(&markup.RenderContext{
319 Ctx: git.DefaultContext,
320 RelativePath: "a.md",
321 Links: markup.Links{
322 Base: markup.TestRepoURL,
323 },
324 }, input)
325 require.NoError(t, err)
326 assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
327 }
328
329 // Make sure we can successfully match every emoji in our dataset with regex
330 for i := range emoji.GemojiData {
331 test(
332 emoji.GemojiData[i].Emoji,
333 `<p><span class="emoji" aria-label="`+emoji.GemojiData[i].Description+`" data-alias="`+emoji.GemojiData[i].Aliases[0]+`">`+emoji.GemojiData[i].Emoji+`</span></p>`)
334 }
335 for i := range emoji.GemojiData {
336 test(
337 ":"+emoji.GemojiData[i].Aliases[0]+":",
338 `<p><span class="emoji" aria-label="`+emoji.GemojiData[i].Description+`" data-alias="`+emoji.GemojiData[i].Aliases[0]+`">`+emoji.GemojiData[i].Emoji+`</span></p>`)
339 }
340
341 // Text that should be turned into or recognized as emoji
342 test(
343 ":gitea:",
344 `<p><span class="emoji" aria-label="gitea" data-alias="gitea"><img alt=":gitea:" src="`+setting.StaticURLPrefix+`/assets/img/emoji/gitea.png"/></span></p>`)
345 test(
346 ":custom-emoji:",
347 `<p>:custom-emoji:</p>`)
348 setting.UI.CustomEmojisMap["custom-emoji"] = ":custom-emoji:"
349 test(
350 ":custom-emoji:",
351 `<p><span class="emoji" aria-label="custom-emoji" data-alias="custom-emoji"><img alt=":custom-emoji:" src="`+setting.StaticURLPrefix+`/assets/img/emoji/custom-emoji.png"/></span></p>`)
352 test(
353 "这是字符:1::+1: some🐊 \U0001f44d:custom-emoji: :gitea:",
354 `<p>这是字符:1:<span class="emoji" aria-label="thumbs up" data-alias="+1">👍</span> some<span class="emoji" aria-label="crocodile" data-alias="crocodile">🐊</span> `+
355 `<span class="emoji" aria-label="thumbs up" data-alias="+1">👍</span><span class="emoji" aria-label="custom-emoji" data-alias="custom-emoji"><img alt=":custom-emoji:" src="`+setting.StaticURLPrefix+`/assets/img/emoji/custom-emoji.png"/></span> `+
356 `<span class="emoji" aria-label="gitea" data-alias="gitea"><img alt=":gitea:" src="`+setting.StaticURLPrefix+`/assets/img/emoji/gitea.png"/></span></p>`)
357 test(
358 "Some text with 😄 in the middle",
359 `<p>Some text with <span class="emoji" aria-label="grinning face with smiling eyes" data-alias="smile">😄</span> in the middle</p>`)
360 test(
361 "Some text with :smile: in the middle",
362 `<p>Some text with <span class="emoji" aria-label="grinning face with smiling eyes" data-alias="smile">😄</span> in the middle</p>`)
363 test(
364 "Some text with 😄😄 2 emoji next to each other",
365 `<p>Some text with <span class="emoji" aria-label="grinning face with smiling eyes" data-alias="smile">😄</span><span class="emoji" aria-label="grinning face with smiling eyes" data-alias="smile">😄</span> 2 emoji next to each other</p>`)
366 test(
367 "😎🤪🔐🤑❓",
368 `<p><span class="emoji" aria-label="smiling face with sunglasses" data-alias="sunglasses">😎</span><span class="emoji" aria-label="zany face" data-alias="zany_face">🤪</span><span class="emoji" aria-label="locked with key" data-alias="closed_lock_with_key">🔐</span><span class="emoji" aria-label="money-mouth face" data-alias="money_mouth_face">🤑</span><span class="emoji" aria-label="red question mark" data-alias="question">❓</span></p>`)
369
370 // should match nothing
371 test(
372 "2001:0db8:85a3:0000:0000:8a2e:0370:7334",
373 `<p>2001:0db8:85a3:0000:0000:8a2e:0370:7334</p>`)
374 test(
375 ":not exist:",
376 `<p>:not exist:</p>`)
377}
378
379func TestRender_ShortLinks(t *testing.T) {
380 setting.AppURL = markup.TestAppURL
381 tree := util.URLJoin(markup.TestRepoURL, "src", "master")
382
383 test := func(input, expected, expectedWiki string) {
384 buffer, err := markdown.RenderString(&markup.RenderContext{
385 Ctx: git.DefaultContext,
386 Links: markup.Links{
387 Base: markup.TestRepoURL,
388 BranchPath: "master",
389 },
390 }, input)
391 require.NoError(t, err)
392 assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(buffer)))
393 buffer, err = markdown.RenderString(&markup.RenderContext{
394 Ctx: git.DefaultContext,
395 Links: markup.Links{
396 Base: markup.TestRepoURL,
397 },
398 Metas: localMetas,
399 IsWiki: true,
400 }, input)
401 require.NoError(t, err)
402 assert.Equal(t, strings.TrimSpace(expectedWiki), strings.TrimSpace(string(buffer)))
403 }
404
405 mediatree := util.URLJoin(markup.TestRepoURL, "media", "master")
406 url := util.URLJoin(tree, "Link")
407 otherURL := util.URLJoin(tree, "Other-Link")
408 encodedURL := util.URLJoin(tree, "Link%3F")
409 imgurl := util.URLJoin(mediatree, "Link.jpg")
410 otherImgurl := util.URLJoin(mediatree, "Link+Other.jpg")
411 encodedImgurl := util.URLJoin(mediatree, "Link+%23.jpg")
412 notencodedImgurl := util.URLJoin(mediatree, "some", "path", "Link+#.jpg")
413 urlWiki := util.URLJoin(markup.TestRepoURL, "wiki", "Link")
414 otherURLWiki := util.URLJoin(markup.TestRepoURL, "wiki", "Other-Link")
415 encodedURLWiki := util.URLJoin(markup.TestRepoURL, "wiki", "Link%3F")
416 imgurlWiki := util.URLJoin(markup.TestRepoURL, "wiki", "raw", "Link.jpg")
417 otherImgurlWiki := util.URLJoin(markup.TestRepoURL, "wiki", "raw", "Link+Other.jpg")
418 encodedImgurlWiki := util.URLJoin(markup.TestRepoURL, "wiki", "raw", "Link+%23.jpg")
419 notencodedImgurlWiki := util.URLJoin(markup.TestRepoURL, "wiki", "raw", "some", "path", "Link+#.jpg")
420 favicon := "https://forgejo.org/favicon.ico"
421
422 test(
423 "[[Link]]",
424 `<p><a href="`+url+`" rel="nofollow">Link</a></p>`,
425 `<p><a href="`+urlWiki+`" rel="nofollow">Link</a></p>`)
426 test(
427 "[[Link.jpg]]",
428 `<p><a href="`+imgurl+`" rel="nofollow"><img src="`+imgurl+`" title="Link.jpg" alt=""/></a></p>`,
429 `<p><a href="`+imgurlWiki+`" rel="nofollow"><img src="`+imgurlWiki+`" title="Link.jpg" alt=""/></a></p>`)
430 test(
431 "[["+favicon+"]]",
432 `<p><a href="`+favicon+`" rel="nofollow"><img src="`+favicon+`" title="favicon.ico" alt=""/></a></p>`,
433 `<p><a href="`+favicon+`" rel="nofollow"><img src="`+favicon+`" title="favicon.ico" alt=""/></a></p>`)
434 test(
435 "[[Name|Link]]",
436 `<p><a href="`+url+`" rel="nofollow">Name</a></p>`,
437 `<p><a href="`+urlWiki+`" rel="nofollow">Name</a></p>`)
438 test(
439 "[[Name|Link.jpg]]",
440 `<p><a href="`+imgurl+`" rel="nofollow"><img src="`+imgurl+`" title="Name" alt=""/></a></p>`,
441 `<p><a href="`+imgurlWiki+`" rel="nofollow"><img src="`+imgurlWiki+`" title="Name" alt=""/></a></p>`)
442 test(
443 "[[Name|Link.jpg|alt=AltName]]",
444 `<p><a href="`+imgurl+`" rel="nofollow"><img src="`+imgurl+`" title="AltName" alt="AltName"/></a></p>`,
445 `<p><a href="`+imgurlWiki+`" rel="nofollow"><img src="`+imgurlWiki+`" title="AltName" alt="AltName"/></a></p>`)
446 test(
447 "[[Name|Link.jpg|title=Title]]",
448 `<p><a href="`+imgurl+`" rel="nofollow"><img src="`+imgurl+`" title="Title" alt=""/></a></p>`,
449 `<p><a href="`+imgurlWiki+`" rel="nofollow"><img src="`+imgurlWiki+`" title="Title" alt=""/></a></p>`)
450 test(
451 "[[Name|Link.jpg|alt=AltName|title=Title]]",
452 `<p><a href="`+imgurl+`" rel="nofollow"><img src="`+imgurl+`" title="Title" alt="AltName"/></a></p>`,
453 `<p><a href="`+imgurlWiki+`" rel="nofollow"><img src="`+imgurlWiki+`" title="Title" alt="AltName"/></a></p>`)
454 test(
455 "[[Name|Link.jpg|alt=\"AltName\"|title='Title']]",
456 `<p><a href="`+imgurl+`" rel="nofollow"><img src="`+imgurl+`" title="Title" alt="AltName"/></a></p>`,
457 `<p><a href="`+imgurlWiki+`" rel="nofollow"><img src="`+imgurlWiki+`" title="Title" alt="AltName"/></a></p>`)
458 test(
459 "[[Name|Link Other.jpg|alt=\"AltName\"|title='Title']]",
460 `<p><a href="`+otherImgurl+`" rel="nofollow"><img src="`+otherImgurl+`" title="Title" alt="AltName"/></a></p>`,
461 `<p><a href="`+otherImgurlWiki+`" rel="nofollow"><img src="`+otherImgurlWiki+`" title="Title" alt="AltName"/></a></p>`)
462 test(
463 "[[Link]] [[Other Link]]",
464 `<p><a href="`+url+`" rel="nofollow">Link</a> <a href="`+otherURL+`" rel="nofollow">Other Link</a></p>`,
465 `<p><a href="`+urlWiki+`" rel="nofollow">Link</a> <a href="`+otherURLWiki+`" rel="nofollow">Other Link</a></p>`)
466 test(
467 "[[Link?]]",
468 `<p><a href="`+encodedURL+`" rel="nofollow">Link?</a></p>`,
469 `<p><a href="`+encodedURLWiki+`" rel="nofollow">Link?</a></p>`)
470 test(
471 "[[Link]] [[Other Link]] [[Link?]]",
472 `<p><a href="`+url+`" rel="nofollow">Link</a> <a href="`+otherURL+`" rel="nofollow">Other Link</a> <a href="`+encodedURL+`" rel="nofollow">Link?</a></p>`,
473 `<p><a href="`+urlWiki+`" rel="nofollow">Link</a> <a href="`+otherURLWiki+`" rel="nofollow">Other Link</a> <a href="`+encodedURLWiki+`" rel="nofollow">Link?</a></p>`)
474 test(
475 "[[Link #.jpg]]",
476 `<p><a href="`+encodedImgurl+`" rel="nofollow"><img src="`+encodedImgurl+`" title="Link #.jpg" alt=""/></a></p>`,
477 `<p><a href="`+encodedImgurlWiki+`" rel="nofollow"><img src="`+encodedImgurlWiki+`" title="Link #.jpg" alt=""/></a></p>`)
478 test(
479 "[[Name|Link #.jpg|alt=\"AltName\"|title='Title']]",
480 `<p><a href="`+encodedImgurl+`" rel="nofollow"><img src="`+encodedImgurl+`" title="Title" alt="AltName"/></a></p>`,
481 `<p><a href="`+encodedImgurlWiki+`" rel="nofollow"><img src="`+encodedImgurlWiki+`" title="Title" alt="AltName"/></a></p>`)
482 test(
483 "[[some/path/Link #.jpg]]",
484 `<p><a href="`+notencodedImgurl+`" rel="nofollow"><img src="`+notencodedImgurl+`" title="Link #.jpg" alt=""/></a></p>`,
485 `<p><a href="`+notencodedImgurlWiki+`" rel="nofollow"><img src="`+notencodedImgurlWiki+`" title="Link #.jpg" alt=""/></a></p>`)
486 test(
487 "<p><a href=\"https://example.org\">[[foobar]]</a></p>",
488 `<p><a href="https://example.org" rel="nofollow">[[foobar]]</a></p>`,
489 `<p><a href="https://example.org" rel="nofollow">[[foobar]]</a></p>`)
490}
491
492func TestRender_RelativeImages(t *testing.T) {
493 setting.AppURL = markup.TestAppURL
494
495 test := func(input, expected, expectedWiki string) {
496 buffer, err := markdown.RenderString(&markup.RenderContext{
497 Ctx: git.DefaultContext,
498 Links: markup.Links{
499 Base: markup.TestRepoURL,
500 BranchPath: "master",
501 },
502 Metas: localMetas,
503 }, input)
504 require.NoError(t, err)
505 assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(buffer)))
506 buffer, err = markdown.RenderString(&markup.RenderContext{
507 Ctx: git.DefaultContext,
508 Links: markup.Links{
509 Base: markup.TestRepoURL,
510 },
511 Metas: localMetas,
512 IsWiki: true,
513 }, input)
514 require.NoError(t, err)
515 assert.Equal(t, strings.TrimSpace(expectedWiki), strings.TrimSpace(string(buffer)))
516 }
517
518 rawwiki := util.URLJoin(markup.TestRepoURL, "wiki", "raw")
519 mediatree := util.URLJoin(markup.TestRepoURL, "media", "master")
520
521 test(
522 `<img src="Link">`,
523 `<img src="`+util.URLJoin(mediatree, "Link")+`"/>`,
524 `<img src="`+util.URLJoin(rawwiki, "Link")+`"/>`)
525
526 test(
527 `<img src="./icon.png">`,
528 `<img src="`+util.URLJoin(mediatree, "icon.png")+`"/>`,
529 `<img src="`+util.URLJoin(rawwiki, "icon.png")+`"/>`)
530}
531
532func Test_ParseClusterFuzz(t *testing.T) {
533 setting.AppURL = markup.TestAppURL
534
535 localMetas := map[string]string{
536 "user": "go-gitea",
537 "repo": "gitea",
538 }
539
540 data := "<A><maTH><tr><MN><bodY ÿ><temPlate></template><tH><tr></A><tH><d<bodY "
541
542 var res strings.Builder
543 err := markup.PostProcess(&markup.RenderContext{
544 Ctx: git.DefaultContext,
545 Links: markup.Links{
546 Base: "https://example.com",
547 },
548 Metas: localMetas,
549 }, strings.NewReader(data), &res)
550 require.NoError(t, err)
551 assert.NotContains(t, res.String(), "<html")
552
553 data = "<!DOCTYPE html>\n<A><maTH><tr><MN><bodY ÿ><temPlate></template><tH><tr></A><tH><d<bodY "
554
555 res.Reset()
556 err = markup.PostProcess(&markup.RenderContext{
557 Ctx: git.DefaultContext,
558 Links: markup.Links{
559 Base: "https://example.com",
560 },
561 Metas: localMetas,
562 }, strings.NewReader(data), &res)
563
564 require.NoError(t, err)
565 assert.NotContains(t, res.String(), "<html")
566}
567
568func TestPostProcess_RenderDocument(t *testing.T) {
569 setting.AppURL = markup.TestAppURL
570 setting.StaticURLPrefix = markup.TestAppURL // can't run standalone
571
572 localMetas := map[string]string{
573 "user": "go-gitea",
574 "repo": "gitea",
575 "mode": "document",
576 }
577
578 test := func(input, expected string) {
579 var res strings.Builder
580 err := markup.PostProcess(&markup.RenderContext{
581 Ctx: git.DefaultContext,
582 Links: markup.Links{
583 AbsolutePrefix: true,
584 Base: "https://example.com",
585 },
586 Metas: localMetas,
587 }, strings.NewReader(input), &res)
588 require.NoError(t, err)
589 assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(res.String()))
590 }
591
592 // Issue index shouldn't be post processing in a document.
593 test(
594 "#1",
595 "#1")
596
597 // But cross-referenced issue index should work.
598 test(
599 "go-gitea/gitea#12345",
600 `<a href="`+util.URLJoin(markup.TestAppURL, "go-gitea", "gitea", "issues", "12345")+`" class="ref-issue">go-gitea/gitea#12345</a>`)
601
602 // Test that other post processing still works.
603 test(
604 ":gitea:",
605 `<span class="emoji" aria-label="gitea" data-alias="gitea"><img alt=":gitea:" src="`+setting.StaticURLPrefix+`/assets/img/emoji/gitea.png"/></span>`)
606 test(
607 "Some text with 😄 in the middle",
608 `Some text with <span class="emoji" aria-label="grinning face with smiling eyes" data-alias="smile">😄</span> in the middle`)
609 test("http://localhost:3000/person/repo/issues/4#issuecomment-1234",
610 `<a href="http://localhost:3000/person/repo/issues/4#issuecomment-1234" class="ref-issue">person/repo#4 (comment)</a>`)
611}
612
613func TestIssue16020(t *testing.T) {
614 setting.AppURL = markup.TestAppURL
615
616 localMetas := map[string]string{
617 "user": "go-gitea",
618 "repo": "gitea",
619 }
620
621 data := `<img src="data:image/png;base64,i//V"/>`
622
623 var res strings.Builder
624 err := markup.PostProcess(&markup.RenderContext{
625 Ctx: git.DefaultContext,
626 Metas: localMetas,
627 }, strings.NewReader(data), &res)
628 require.NoError(t, err)
629 assert.Equal(t, data, res.String())
630}
631
632func BenchmarkEmojiPostprocess(b *testing.B) {
633 data := "🥰 "
634 for len(data) < 1<<16 {
635 data += data
636 }
637 b.ResetTimer()
638 for i := 0; i < b.N; i++ {
639 var res strings.Builder
640 err := markup.PostProcess(&markup.RenderContext{
641 Ctx: git.DefaultContext,
642 Metas: localMetas,
643 }, strings.NewReader(data), &res)
644 require.NoError(b, err)
645 }
646}
647
648func TestFuzz(t *testing.T) {
649 s := "t/l/issues/8#/../../a"
650 renderContext := markup.RenderContext{
651 Ctx: git.DefaultContext,
652 Links: markup.Links{
653 Base: "https://example.com/go-gitea/gitea",
654 },
655 Metas: map[string]string{
656 "user": "go-gitea",
657 "repo": "gitea",
658 },
659 }
660
661 err := markup.PostProcess(&renderContext, strings.NewReader(s), io.Discard)
662
663 require.NoError(t, err)
664}
665
666func TestIssue18471(t *testing.T) {
667 data := `http://domain/org/repo/compare/783b039...da951ce`
668
669 var res strings.Builder
670 err := markup.PostProcess(&markup.RenderContext{
671 Ctx: git.DefaultContext,
672 Metas: localMetas,
673 }, strings.NewReader(data), &res)
674
675 require.NoError(t, err)
676 assert.Equal(t, "<a href=\"http://domain/org/repo/compare/783b039...da951ce\" class=\"compare\"><code class=\"nohighlight\">783b039...da951ce</code></a>", res.String())
677}
678
679func TestRender_FilePreview(t *testing.T) {
680 defer test.MockVariableValue(&setting.StaticRootPath, "../../")()
681 defer test.MockVariableValue(&setting.Names, []string{"english"})()
682 defer test.MockVariableValue(&setting.Langs, []string{"en-US"})()
683 translation.InitLocales(t.Context())
684
685 setting.AppURL = markup.TestAppURL
686 markup.Init(&markup.ProcessorHelper{
687 GetRepoFileBlob: func(ctx context.Context, ownerName, repoName, commitSha, filePath string, language *string) (*git.Blob, error) {
688 gitRepo, err := git.OpenRepository(git.DefaultContext, "./tests/repo/repo1_filepreview")
689 require.NoError(t, err)
690 defer gitRepo.Close()
691
692 commit, err := gitRepo.GetCommit(commitSha)
693 require.NoError(t, err)
694
695 blob, err := commit.GetBlobByPath(filePath)
696 require.NoError(t, err)
697
698 return blob, nil
699 },
700 })
701
702 sha := "190d9492934af498c3f669d6a2431dc5459e5b20"
703 commitFilePreview := util.URLJoin(markup.TestRepoURL, "src", "commit", sha, "path", "to", "file.go") + "#L2-L3"
704
705 testRender := func(input, expected string, metas map[string]string) {
706 buffer, err := markup.RenderString(&markup.RenderContext{
707 Ctx: git.DefaultContext,
708 RelativePath: ".md",
709 Metas: metas,
710 }, input)
711 require.NoError(t, err)
712 assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
713 }
714
715 t.Run("single", func(t *testing.T) {
716 testRender(
717 commitFilePreview,
718 `<p></p>`+
719 `<div class="file-preview-box">`+
720 `<div class="header">`+
721 `<div>`+
722 `<a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3" class="muted" rel="nofollow">path/to/file.go</a>`+
723 `</div>`+
724 `<span class="text grey">`+
725 `Lines 2 to 3 in <a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" class="text black" rel="nofollow">190d949</a>`+
726 `</span>`+
727 `</div>`+
728 `<div class="ui table">`+
729 `<table class="file-preview">`+
730 `<tbody>`+
731 `<tr>`+
732 `<td class="lines-num"><span data-line-number="2"></span></td>`+
733 `<td class="lines-code chroma"><code class="code-inner"><span class="nx">B</span>`+"\n"+`</code></td>`+
734 `</tr>`+
735 `<tr>`+
736 `<td class="lines-num"><span data-line-number="3"></span></td>`+
737 `<td class="lines-code chroma"><code class="code-inner"><span class="nx">C</span>`+"\n"+`</code></td>`+
738 `</tr>`+
739 `</tbody>`+
740 `</table>`+
741 `</div>`+
742 `</div>`+
743 `<p></p>`,
744 localMetas,
745 )
746 })
747
748 t.Run("cross-repo", func(t *testing.T) {
749 testRender(
750 commitFilePreview,
751 `<p></p>`+
752 `<div class="file-preview-box">`+
753 `<div class="header">`+
754 `<div>`+
755 `<a href="http://localhost:3000/gogits/gogs/" rel="nofollow">gogits/gogs</a> – `+
756 `<a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3" class="muted" rel="nofollow">path/to/file.go</a>`+
757 `</div>`+
758 `<span class="text grey">`+
759 `Lines 2 to 3 in <a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" class="text black" rel="nofollow">gogits/gogs@190d949</a>`+
760 `</span>`+
761 `</div>`+
762 `<div class="ui table">`+
763 `<table class="file-preview">`+
764 `<tbody>`+
765 `<tr>`+
766 `<td class="lines-num"><span data-line-number="2"></span></td>`+
767 `<td class="lines-code chroma"><code class="code-inner"><span class="nx">B</span>`+"\n"+`</code></td>`+
768 `</tr>`+
769 `<tr>`+
770 `<td class="lines-num"><span data-line-number="3"></span></td>`+
771 `<td class="lines-code chroma"><code class="code-inner"><span class="nx">C</span>`+"\n"+`</code></td>`+
772 `</tr>`+
773 `</tbody>`+
774 `</table>`+
775 `</div>`+
776 `</div>`+
777 `<p></p>`,
778 map[string]string{
779 "user": "gogits",
780 "repo": "gogs2",
781 },
782 )
783 })
784 t.Run("single-line", func(t *testing.T) {
785 testRender(
786 util.URLJoin(markup.TestRepoURL, "src", "commit", "4c1aaf56bcb9f39dcf65f3f250726850aed13cd6", "single-line.txt")+"#L1",
787 `<p></p>`+
788 `<div class="file-preview-box">`+
789 `<div class="header">`+
790 `<div>`+
791 `<a href="http://localhost:3000/gogits/gogs/" rel="nofollow">gogits/gogs</a> – `+
792 `<a href="http://localhost:3000/gogits/gogs/src/commit/4c1aaf56bcb9f39dcf65f3f250726850aed13cd6/single-line.txt#L1" class="muted" rel="nofollow">single-line.txt</a>`+
793 `</div>`+
794 `<span class="text grey">`+
795 `Line 1 in <a href="http://localhost:3000/gogits/gogs/src/commit/4c1aaf56bcb9f39dcf65f3f250726850aed13cd6" class="text black" rel="nofollow">gogits/gogs@4c1aaf5</a>`+
796 `</span>`+
797 `</div>`+
798 `<div class="ui table">`+
799 `<table class="file-preview">`+
800 `<tbody>`+
801 `<tr>`+
802 `<td class="lines-num"><span data-line-number="1"></span></td>`+
803 `<td class="lines-code chroma"><code class="code-inner">A`+`</code></td>`+
804 `</tr>`+
805 `</tbody>`+
806 `</table>`+
807 `</div>`+
808 `</div>`+
809 `<p></p>`,
810 map[string]string{
811 "user": "gogits",
812 "repo": "gogs2",
813 },
814 )
815 })
816
817 t.Run("AppSubURL", func(t *testing.T) {
818 urlWithSub := util.URLJoin(markup.TestAppURL, "sub", markup.TestOrgRepo, "src", "commit", sha, "path", "to", "file.go") + "#L2-L3"
819
820 testRender(
821 urlWithSub,
822 `<p><a href="http://localhost:3000/sub/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3" rel="nofollow"><code>190d949293/path/to/file.go (L2-L3)</code></a></p>`,
823 localMetas,
824 )
825
826 defer test.MockVariableValue(&setting.AppURL, markup.TestAppURL+"sub/")()
827 defer test.MockVariableValue(&setting.AppSubURL, "/sub")()
828
829 testRender(
830 urlWithSub,
831 `<p></p>`+
832 `<div class="file-preview-box">`+
833 `<div class="header">`+
834 `<div>`+
835 `<a href="http://localhost:3000/sub/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3" class="muted" rel="nofollow">path/to/file.go</a>`+
836 `</div>`+
837 `<span class="text grey">`+
838 `Lines 2 to 3 in <a href="http://localhost:3000/sub/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" class="text black" rel="nofollow">190d949</a>`+
839 `</span>`+
840 `</div>`+
841 `<div class="ui table">`+
842 `<table class="file-preview">`+
843 `<tbody>`+
844 `<tr>`+
845 `<td class="lines-num"><span data-line-number="2"></span></td>`+
846 `<td class="lines-code chroma"><code class="code-inner"><span class="nx">B</span>`+"\n"+`</code></td>`+
847 `</tr>`+
848 `<tr>`+
849 `<td class="lines-num"><span data-line-number="3"></span></td>`+
850 `<td class="lines-code chroma"><code class="code-inner"><span class="nx">C</span>`+"\n"+`</code></td>`+
851 `</tr>`+
852 `</tbody>`+
853 `</table>`+
854 `</div>`+
855 `</div>`+
856 `<p></p>`,
857 localMetas,
858 )
859
860 testRender(
861 "first without sub "+commitFilePreview+" second "+urlWithSub,
862 `<p>first without sub <a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3" rel="nofollow"><code>190d949293/path/to/file.go (L2-L3)</code></a> second </p>`+
863 `<div class="file-preview-box">`+
864 `<div class="header">`+
865 `<div>`+
866 `<a href="http://localhost:3000/sub/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3" class="muted" rel="nofollow">path/to/file.go</a>`+
867 `</div>`+
868 `<span class="text grey">`+
869 `Lines 2 to 3 in <a href="http://localhost:3000/sub/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" class="text black" rel="nofollow">190d949</a>`+
870 `</span>`+
871 `</div>`+
872 `<div class="ui table">`+
873 `<table class="file-preview">`+
874 `<tbody>`+
875 `<tr>`+
876 `<td class="lines-num"><span data-line-number="2"></span></td>`+
877 `<td class="lines-code chroma"><code class="code-inner"><span class="nx">B</span>`+"\n"+`</code></td>`+
878 `</tr>`+
879 `<tr>`+
880 `<td class="lines-num"><span data-line-number="3"></span></td>`+
881 `<td class="lines-code chroma"><code class="code-inner"><span class="nx">C</span>`+"\n"+`</code></td>`+
882 `</tr>`+
883 `</tbody>`+
884 `</table>`+
885 `</div>`+
886 `</div>`+
887 `<p></p>`,
888 localMetas,
889 )
890 })
891
892 t.Run("multiples", func(t *testing.T) {
893 testRender(
894 "first "+commitFilePreview+" second "+commitFilePreview,
895 `<p>first </p>`+
896 `<div class="file-preview-box">`+
897 `<div class="header">`+
898 `<div>`+
899 `<a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3" class="muted" rel="nofollow">path/to/file.go</a>`+
900 `</div>`+
901 `<span class="text grey">`+
902 `Lines 2 to 3 in <a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" class="text black" rel="nofollow">190d949</a>`+
903 `</span>`+
904 `</div>`+
905 `<div class="ui table">`+
906 `<table class="file-preview">`+
907 `<tbody>`+
908 `<tr>`+
909 `<td class="lines-num"><span data-line-number="2"></span></td>`+
910 `<td class="lines-code chroma"><code class="code-inner"><span class="nx">B</span>`+"\n"+`</code></td>`+
911 `</tr>`+
912 `<tr>`+
913 `<td class="lines-num"><span data-line-number="3"></span></td>`+
914 `<td class="lines-code chroma"><code class="code-inner"><span class="nx">C</span>`+"\n"+`</code></td>`+
915 `</tr>`+
916 `</tbody>`+
917 `</table>`+
918 `</div>`+
919 `</div>`+
920 `<p> second </p>`+
921 `<div class="file-preview-box">`+
922 `<div class="header">`+
923 `<div>`+
924 `<a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3" class="muted" rel="nofollow">path/to/file.go</a>`+
925 `</div>`+
926 `<span class="text grey">`+
927 `Lines 2 to 3 in <a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" class="text black" rel="nofollow">190d949</a>`+
928 `</span>`+
929 `</div>`+
930 `<div class="ui table">`+
931 `<table class="file-preview">`+
932 `<tbody>`+
933 `<tr>`+
934 `<td class="lines-num"><span data-line-number="2"></span></td>`+
935 `<td class="lines-code chroma"><code class="code-inner"><span class="nx">B</span>`+"\n"+`</code></td>`+
936 `</tr>`+
937 `<tr>`+
938 `<td class="lines-num"><span data-line-number="3"></span></td>`+
939 `<td class="lines-code chroma"><code class="code-inner"><span class="nx">C</span>`+"\n"+`</code></td>`+
940 `</tr>`+
941 `</tbody>`+
942 `</table>`+
943 `</div>`+
944 `</div>`+
945 `<p></p>`,
946 localMetas,
947 )
948
949 testRender(
950 "first "+commitFilePreview+" second "+commitFilePreview+" third "+commitFilePreview,
951 `<p>first </p>`+
952 `<div class="file-preview-box">`+
953 `<div class="header">`+
954 `<div>`+
955 `<a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3" class="muted" rel="nofollow">path/to/file.go</a>`+
956 `</div>`+
957 `<span class="text grey">`+
958 `Lines 2 to 3 in <a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" class="text black" rel="nofollow">190d949</a>`+
959 `</span>`+
960 `</div>`+
961 `<div class="ui table">`+
962 `<table class="file-preview">`+
963 `<tbody>`+
964 `<tr>`+
965 `<td class="lines-num"><span data-line-number="2"></span></td>`+
966 `<td class="lines-code chroma"><code class="code-inner"><span class="nx">B</span>`+"\n"+`</code></td>`+
967 `</tr>`+
968 `<tr>`+
969 `<td class="lines-num"><span data-line-number="3"></span></td>`+
970 `<td class="lines-code chroma"><code class="code-inner"><span class="nx">C</span>`+"\n"+`</code></td>`+
971 `</tr>`+
972 `</tbody>`+
973 `</table>`+
974 `</div>`+
975 `</div>`+
976 `<p> second </p>`+
977 `<div class="file-preview-box">`+
978 `<div class="header">`+
979 `<div>`+
980 `<a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3" class="muted" rel="nofollow">path/to/file.go</a>`+
981 `</div>`+
982 `<span class="text grey">`+
983 `Lines 2 to 3 in <a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" class="text black" rel="nofollow">190d949</a>`+
984 `</span>`+
985 `</div>`+
986 `<div class="ui table">`+
987 `<table class="file-preview">`+
988 `<tbody>`+
989 `<tr>`+
990 `<td class="lines-num"><span data-line-number="2"></span></td>`+
991 `<td class="lines-code chroma"><code class="code-inner"><span class="nx">B</span>`+"\n"+`</code></td>`+
992 `</tr>`+
993 `<tr>`+
994 `<td class="lines-num"><span data-line-number="3"></span></td>`+
995 `<td class="lines-code chroma"><code class="code-inner"><span class="nx">C</span>`+"\n"+`</code></td>`+
996 `</tr>`+
997 `</tbody>`+
998 `</table>`+
999 `</div>`+
1000 `</div>`+
1001 `<p> third </p>`+
1002 `<div class="file-preview-box">`+
1003 `<div class="header">`+
1004 `<div>`+
1005 `<a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3" class="muted" rel="nofollow">path/to/file.go</a>`+
1006 `</div>`+
1007 `<span class="text grey">`+
1008 `Lines 2 to 3 in <a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" class="text black" rel="nofollow">190d949</a>`+
1009 `</span>`+
1010 `</div>`+
1011 `<div class="ui table">`+
1012 `<table class="file-preview">`+
1013 `<tbody>`+
1014 `<tr>`+
1015 `<td class="lines-num"><span data-line-number="2"></span></td>`+
1016 `<td class="lines-code chroma"><code class="code-inner"><span class="nx">B</span>`+"\n"+`</code></td>`+
1017 `</tr>`+
1018 `<tr>`+
1019 `<td class="lines-num"><span data-line-number="3"></span></td>`+
1020 `<td class="lines-code chroma"><code class="code-inner"><span class="nx">C</span>`+"\n"+`</code></td>`+
1021 `</tr>`+
1022 `</tbody>`+
1023 `</table>`+
1024 `</div>`+
1025 `</div>`+
1026 `<p></p>`,
1027 localMetas,
1028 )
1029 })
1030
1031 commitFileURL := util.URLJoin(markup.TestRepoURL, "src", "commit", "c9913120ed2c1e27c1d7752ecdb7a504dc7cf6be", "path", "to", "file.md")
1032
1033 t.Run("rendered file with ?display=source", func(t *testing.T) {
1034 testRender(
1035 commitFileURL+"?display=source"+"#L1-L2",
1036 `<p></p>`+
1037 `<div class="file-preview-box">`+
1038 `<div class="header">`+
1039 `<div>`+
1040 `<a href="http://localhost:3000/gogits/gogs/src/commit/c9913120ed2c1e27c1d7752ecdb7a504dc7cf6be/path/to/file.md?display=source#L1-L2" class="muted" rel="nofollow">path/to/file.md</a>`+
1041 `</div>`+
1042 `<span class="text grey">`+
1043 `Lines 1 to 2 in <a href="http://localhost:3000/gogits/gogs/src/commit/c9913120ed2c1e27c1d7752ecdb7a504dc7cf6be" class="text black" rel="nofollow">c991312</a>`+
1044 `</span>`+
1045 `</div>`+
1046 `<div class="ui table">`+
1047 `<table class="file-preview">`+
1048 `<tbody>`+
1049 `<tr>`+
1050 `<td class="lines-num"><span data-line-number="1"></span></td>`+
1051 `<td class="lines-code chroma"><code class="code-inner"><span class="gh"># A`+"\n"+`</span></code></td>`+
1052 `</tr>`+
1053 `<tr>`+
1054 `<td class="lines-num"><span data-line-number="2"></span></td>`+
1055 `<td class="lines-code chroma"><code class="code-inner"><span class="gh"></span>B`+"\n"+`</code></td>`+
1056 `</tr>`+
1057 `</tbody>`+
1058 `</table>`+
1059 `</div>`+
1060 `</div>`+
1061 `<p></p>`,
1062 localMetas,
1063 )
1064 })
1065
1066 t.Run("rendered file without ?display=source", func(t *testing.T) {
1067 testRender(
1068 commitFileURL+"#L1-L2",
1069 `<p></p>`+
1070 `<div class="file-preview-box">`+
1071 `<div class="header">`+
1072 `<div>`+
1073 `<a href="http://localhost:3000/gogits/gogs/src/commit/c9913120ed2c1e27c1d7752ecdb7a504dc7cf6be/path/to/file.md?display=source#L1-L2" class="muted" rel="nofollow">path/to/file.md</a>`+
1074 `</div>`+
1075 `<span class="text grey">`+
1076 `Lines 1 to 2 in <a href="http://localhost:3000/gogits/gogs/src/commit/c9913120ed2c1e27c1d7752ecdb7a504dc7cf6be" class="text black" rel="nofollow">c991312</a>`+
1077 `</span>`+
1078 `</div>`+
1079 `<div class="ui table">`+
1080 `<table class="file-preview">`+
1081 `<tbody>`+
1082 `<tr>`+
1083 `<td class="lines-num"><span data-line-number="1"></span></td>`+
1084 `<td class="lines-code chroma"><code class="code-inner"><span class="gh"># A`+"\n"+`</span></code></td>`+
1085 `</tr>`+
1086 `<tr>`+
1087 `<td class="lines-num"><span data-line-number="2"></span></td>`+
1088 `<td class="lines-code chroma"><code class="code-inner"><span class="gh"></span>B`+"\n"+`</code></td>`+
1089 `</tr>`+
1090 `</tbody>`+
1091 `</table>`+
1092 `</div>`+
1093 `</div>`+
1094 `<p></p>`,
1095 localMetas,
1096 )
1097 })
1098
1099 commitFileURL = util.URLJoin(markup.TestRepoURL, "src", "commit", "190d9492934af498c3f669d6a2431dc5459e5b20", "path", "to", "file.go")
1100
1101 t.Run("normal file with ?display=source", func(t *testing.T) {
1102 testRender(
1103 commitFileURL+"?display=source"+"#L2-L3",
1104 `<p></p>`+
1105 `<div class="file-preview-box">`+
1106 `<div class="header">`+
1107 `<div>`+
1108 `<a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go?display=source#L2-L3" class="muted" rel="nofollow">path/to/file.go</a>`+
1109 `</div>`+
1110 `<span class="text grey">`+
1111 `Lines 2 to 3 in <a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" class="text black" rel="nofollow">190d949</a>`+
1112 `</span>`+
1113 `</div>`+
1114 `<div class="ui table">`+
1115 `<table class="file-preview">`+
1116 `<tbody>`+
1117 `<tr>`+
1118 `<td class="lines-num"><span data-line-number="2"></span></td>`+
1119 `<td class="lines-code chroma"><code class="code-inner"><span class="nx">B</span>`+"\n"+`</code></td>`+
1120 `</tr>`+
1121 `<tr>`+
1122 `<td class="lines-num"><span data-line-number="3"></span></td>`+
1123 `<td class="lines-code chroma"><code class="code-inner"><span class="nx">C</span>`+"\n"+`</code></td>`+
1124 `</tr>`+
1125 `</tbody>`+
1126 `</table>`+
1127 `</div>`+
1128 `</div>`+
1129 `<p></p>`,
1130 localMetas,
1131 )
1132 })
1133
1134 commitFileURL = util.URLJoin(markup.TestRepoURL, "src", "commit", "eeb243c3395e1921c5d90e73bd739827251fc99d", "path", "to", "file%20%23.txt")
1135
1136 t.Run("file with strange characters in name", func(t *testing.T) {
1137 testRender(
1138 commitFileURL+"#L1",
1139 `<p></p>`+
1140 `<div class="file-preview-box">`+
1141 `<div class="header">`+
1142 `<div>`+
1143 `<a href="http://localhost:3000/gogits/gogs/src/commit/eeb243c3395e1921c5d90e73bd739827251fc99d/path/to/file%20%23.txt#L1" class="muted" rel="nofollow">path/to/file #.txt</a>`+
1144 `</div>`+
1145 `<span class="text grey">`+
1146 `Line 1 in <a href="http://localhost:3000/gogits/gogs/src/commit/eeb243c3395e1921c5d90e73bd739827251fc99d" class="text black" rel="nofollow">eeb243c</a>`+
1147 `</span>`+
1148 `</div>`+
1149 `<div class="ui table">`+
1150 `<table class="file-preview">`+
1151 `<tbody>`+
1152 `<tr>`+
1153 `<td class="lines-num"><span data-line-number="1"></span></td>`+
1154 `<td class="lines-code chroma"><code class="code-inner">A`+"\n"+`</code></td>`+
1155 `</tr>`+
1156 `</tbody>`+
1157 `</table>`+
1158 `</div>`+
1159 `</div>`+
1160 `<p></p>`,
1161 localMetas,
1162 )
1163 })
1164}