code.gitea.io/gitea@v1.22.3/modules/markup/html_internal_test.go (about) 1 // Copyright 2018 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package markup 5 6 import ( 7 "fmt" 8 "strconv" 9 "strings" 10 "testing" 11 12 "code.gitea.io/gitea/modules/git" 13 "code.gitea.io/gitea/modules/setting" 14 "code.gitea.io/gitea/modules/util" 15 16 "github.com/stretchr/testify/assert" 17 ) 18 19 const ( 20 TestAppURL = "http://localhost:3000/" 21 TestRepoURL = TestAppURL + "test-owner/test-repo/" 22 ) 23 24 // externalIssueLink an HTML link to an alphanumeric-style issue 25 func externalIssueLink(baseURL, class, name string) string { 26 return link(util.URLJoin(baseURL, name), class, name) 27 } 28 29 // numericLink an HTML to a numeric-style issue 30 func numericIssueLink(baseURL, class string, index int, marker string) string { 31 return link(util.URLJoin(baseURL, strconv.Itoa(index)), class, fmt.Sprintf("%s%d", marker, index)) 32 } 33 34 // link an HTML link 35 func link(href, class, contents string) string { 36 if class != "" { 37 class = " class=\"" + class + "\"" 38 } 39 40 return fmt.Sprintf("<a href=\"%s\"%s>%s</a>", href, class, contents) 41 } 42 43 var numericMetas = map[string]string{ 44 "format": "https://someurl.com/{user}/{repo}/{index}", 45 "user": "someUser", 46 "repo": "someRepo", 47 "style": IssueNameStyleNumeric, 48 } 49 50 var alphanumericMetas = map[string]string{ 51 "format": "https://someurl.com/{user}/{repo}/{index}", 52 "user": "someUser", 53 "repo": "someRepo", 54 "style": IssueNameStyleAlphanumeric, 55 } 56 57 var regexpMetas = map[string]string{ 58 "format": "https://someurl.com/{user}/{repo}/{index}", 59 "user": "someUser", 60 "repo": "someRepo", 61 "style": IssueNameStyleRegexp, 62 } 63 64 // these values should match the TestOrgRepo const above 65 var localMetas = map[string]string{ 66 "user": "test-owner", 67 "repo": "test-repo", 68 } 69 70 func TestRender_IssueIndexPattern(t *testing.T) { 71 // numeric: render inputs without valid mentions 72 test := func(s string) { 73 testRenderIssueIndexPattern(t, s, s, &RenderContext{ 74 Ctx: git.DefaultContext, 75 }) 76 testRenderIssueIndexPattern(t, s, s, &RenderContext{ 77 Ctx: git.DefaultContext, 78 Metas: numericMetas, 79 }) 80 } 81 82 // should not render anything when there are no mentions 83 test("") 84 test("this is a test") 85 test("test 123 123 1234") 86 test("#") 87 test("# # #") 88 test("# 123") 89 test("#abcd") 90 test("test#1234") 91 test("#1234test") 92 test("#abcd") 93 test("test!1234") 94 test("!1234test") 95 test(" test !1234test") 96 test("/home/gitea/#1234") 97 test("/home/gitea/!1234") 98 99 // should not render issue mention without leading space 100 test("test#54321 issue") 101 102 // should not render issue mention without trailing space 103 test("test #54321issue") 104 } 105 106 func TestRender_IssueIndexPattern2(t *testing.T) { 107 setting.AppURL = TestAppURL 108 109 // numeric: render inputs with valid mentions 110 test := func(s, expectedFmt, marker string, indices ...int) { 111 var path, prefix string 112 isExternal := false 113 if marker == "!" { 114 path = "pulls" 115 prefix = "http://localhost:3000/someUser/someRepo/pulls/" 116 } else { 117 path = "issues" 118 prefix = "https://someurl.com/someUser/someRepo/" 119 isExternal = true 120 } 121 122 links := make([]any, len(indices)) 123 for i, index := range indices { 124 links[i] = numericIssueLink(util.URLJoin(TestRepoURL, path), "ref-issue", index, marker) 125 } 126 expectedNil := fmt.Sprintf(expectedFmt, links...) 127 testRenderIssueIndexPattern(t, s, expectedNil, &RenderContext{ 128 Ctx: git.DefaultContext, 129 Metas: localMetas, 130 }) 131 132 class := "ref-issue" 133 if isExternal { 134 class += " ref-external-issue" 135 } 136 137 for i, index := range indices { 138 links[i] = numericIssueLink(prefix, class, index, marker) 139 } 140 expectedNum := fmt.Sprintf(expectedFmt, links...) 141 testRenderIssueIndexPattern(t, s, expectedNum, &RenderContext{ 142 Ctx: git.DefaultContext, 143 Metas: numericMetas, 144 }) 145 } 146 147 // should render freestanding mentions 148 test("#1234 test", "%s test", "#", 1234) 149 test("test #8 issue", "test %s issue", "#", 8) 150 test("!1234 test", "%s test", "!", 1234) 151 test("test !8 issue", "test %s issue", "!", 8) 152 test("test issue #1234", "test issue %s", "#", 1234) 153 test("fixes issue #1234.", "fixes issue %s.", "#", 1234) 154 155 // should render mentions in parentheses / brackets 156 test("(#54321 issue)", "(%s issue)", "#", 54321) 157 test("[#54321 issue]", "[%s issue]", "#", 54321) 158 test("test (#9801 extra) issue", "test (%s extra) issue", "#", 9801) 159 test("test (!9801 extra) issue", "test (%s extra) issue", "!", 9801) 160 test("test (#1)", "test (%s)", "#", 1) 161 162 // should render multiple issue mentions in the same line 163 test("#54321 #1243", "%s %s", "#", 54321, 1243) 164 test("wow (#54321 #1243)", "wow (%s %s)", "#", 54321, 1243) 165 test("(#4)(#5)", "(%s)(%s)", "#", 4, 5) 166 test("#1 (#4321) test", "%s (%s) test", "#", 1, 4321) 167 168 // should render with : 169 test("#1234: test", "%s: test", "#", 1234) 170 test("wow (#54321: test)", "wow (%s: test)", "#", 54321) 171 } 172 173 func TestRender_IssueIndexPattern3(t *testing.T) { 174 setting.AppURL = TestAppURL 175 176 // alphanumeric: render inputs without valid mentions 177 test := func(s string) { 178 testRenderIssueIndexPattern(t, s, s, &RenderContext{ 179 Ctx: git.DefaultContext, 180 Metas: alphanumericMetas, 181 }) 182 } 183 test("") 184 test("this is a test") 185 test("test 123 123 1234") 186 test("#") 187 test("# 123") 188 test("#abcd") 189 test("test #123") 190 test("abc-1234") // issue prefix must be capital 191 test("ABc-1234") // issue prefix must be _all_ capital 192 test("ABCDEFGHIJK-1234") // the limit is 10 characters in the prefix 193 test("ABC1234") // dash is required 194 test("test ABC- test") // number is required 195 test("test -1234 test") // prefix is required 196 test("testABC-123 test") // leading space is required 197 test("test ABC-123test") // trailing space is required 198 test("ABC-0123") // no leading zero 199 } 200 201 func TestRender_IssueIndexPattern4(t *testing.T) { 202 setting.AppURL = TestAppURL 203 204 // alphanumeric: render inputs with valid mentions 205 test := func(s, expectedFmt string, names ...string) { 206 links := make([]any, len(names)) 207 for i, name := range names { 208 links[i] = externalIssueLink("https://someurl.com/someUser/someRepo/", "ref-issue ref-external-issue", name) 209 } 210 expected := fmt.Sprintf(expectedFmt, links...) 211 testRenderIssueIndexPattern(t, s, expected, &RenderContext{ 212 Ctx: git.DefaultContext, 213 Metas: alphanumericMetas, 214 }) 215 } 216 test("OTT-1234 test", "%s test", "OTT-1234") 217 test("test T-12 issue", "test %s issue", "T-12") 218 test("test issue ABCDEFGHIJ-1234567890", "test issue %s", "ABCDEFGHIJ-1234567890") 219 } 220 221 func TestRender_IssueIndexPattern5(t *testing.T) { 222 setting.AppURL = TestAppURL 223 224 // regexp: render inputs without valid mentions 225 test := func(s, expectedFmt, pattern string, ids, names []string) { 226 metas := regexpMetas 227 metas["regexp"] = pattern 228 links := make([]any, len(ids)) 229 for i, id := range ids { 230 links[i] = link(util.URLJoin("https://someurl.com/someUser/someRepo/", id), "ref-issue ref-external-issue", names[i]) 231 } 232 233 expected := fmt.Sprintf(expectedFmt, links...) 234 testRenderIssueIndexPattern(t, s, expected, &RenderContext{ 235 Ctx: git.DefaultContext, 236 Metas: metas, 237 }) 238 } 239 240 test("abc ISSUE-123 def", "abc %s def", 241 "ISSUE-(\\d+)", 242 []string{"123"}, 243 []string{"ISSUE-123"}, 244 ) 245 246 test("abc (ISSUE 123) def", "abc %s def", 247 "\\(ISSUE (\\d+)\\)", 248 []string{"123"}, 249 []string{"(ISSUE 123)"}, 250 ) 251 252 test("abc ISSUE-123 def", "abc %s def", 253 "(ISSUE-(\\d+))", 254 []string{"ISSUE-123"}, 255 []string{"ISSUE-123"}, 256 ) 257 258 testRenderIssueIndexPattern(t, "will not match", "will not match", &RenderContext{ 259 Ctx: git.DefaultContext, 260 Metas: regexpMetas, 261 }) 262 } 263 264 func TestRender_IssueIndexPattern_Document(t *testing.T) { 265 setting.AppURL = TestAppURL 266 metas := map[string]string{ 267 "format": "https://someurl.com/{user}/{repo}/{index}", 268 "user": "someUser", 269 "repo": "someRepo", 270 "style": IssueNameStyleNumeric, 271 "mode": "document", 272 } 273 274 testRenderIssueIndexPattern(t, "#1", "#1", &RenderContext{ 275 Ctx: git.DefaultContext, 276 Metas: metas, 277 }) 278 testRenderIssueIndexPattern(t, "#1312", "#1312", &RenderContext{ 279 Ctx: git.DefaultContext, 280 Metas: metas, 281 }) 282 testRenderIssueIndexPattern(t, "!1", "!1", &RenderContext{ 283 Ctx: git.DefaultContext, 284 Metas: metas, 285 }) 286 } 287 288 func testRenderIssueIndexPattern(t *testing.T, input, expected string, ctx *RenderContext) { 289 ctx.Links.AbsolutePrefix = true 290 if ctx.Links.Base == "" { 291 ctx.Links.Base = TestRepoURL 292 } 293 294 var buf strings.Builder 295 err := postProcess(ctx, []processor{issueIndexPatternProcessor}, strings.NewReader(input), &buf) 296 assert.NoError(t, err) 297 assert.Equal(t, expected, buf.String(), "input=%q", input) 298 } 299 300 func TestRender_AutoLink(t *testing.T) { 301 setting.AppURL = TestAppURL 302 303 test := func(input, expected string) { 304 var buffer strings.Builder 305 err := PostProcess(&RenderContext{ 306 Ctx: git.DefaultContext, 307 Links: Links{ 308 Base: TestRepoURL, 309 }, 310 Metas: localMetas, 311 }, strings.NewReader(input), &buffer) 312 assert.Equal(t, err, nil) 313 assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer.String())) 314 315 buffer.Reset() 316 err = PostProcess(&RenderContext{ 317 Ctx: git.DefaultContext, 318 Links: Links{ 319 Base: TestRepoURL, 320 }, 321 Metas: localMetas, 322 IsWiki: true, 323 }, strings.NewReader(input), &buffer) 324 assert.Equal(t, err, nil) 325 assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer.String())) 326 } 327 328 // render valid issue URLs 329 test(util.URLJoin(TestRepoURL, "issues", "3333"), 330 numericIssueLink(util.URLJoin(TestRepoURL, "issues"), "ref-issue", 3333, "#")) 331 332 // render valid commit URLs 333 tmp := util.URLJoin(TestRepoURL, "commit", "d8a994ef243349f321568f9e36d5c3f444b99cae") 334 test(tmp, "<a href=\""+tmp+"\" class=\"commit\"><code class=\"nohighlight\">d8a994ef24</code></a>") 335 tmp += "#diff-2" 336 test(tmp, "<a href=\""+tmp+"\" class=\"commit\"><code class=\"nohighlight\">d8a994ef24 (diff-2)</code></a>") 337 338 // render other commit URLs 339 tmp = "https://external-link.gitea.io/go-gitea/gitea/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2" 340 test(tmp, "<a href=\""+tmp+"\" class=\"commit\"><code class=\"nohighlight\">d8a994ef24 (diff-2)</code></a>") 341 } 342 343 func TestRender_FullIssueURLs(t *testing.T) { 344 setting.AppURL = TestAppURL 345 346 test := func(input, expected string) { 347 var result strings.Builder 348 err := postProcess(&RenderContext{ 349 Ctx: git.DefaultContext, 350 Links: Links{ 351 Base: TestRepoURL, 352 }, 353 Metas: localMetas, 354 }, []processor{fullIssuePatternProcessor}, strings.NewReader(input), &result) 355 assert.NoError(t, err) 356 assert.Equal(t, expected, result.String()) 357 } 358 test("Here is a link https://git.osgeo.org/gogs/postgis/postgis/pulls/6", 359 "Here is a link https://git.osgeo.org/gogs/postgis/postgis/pulls/6") 360 test("Look here http://localhost:3000/person/repo/issues/4", 361 `Look here <a href="http://localhost:3000/person/repo/issues/4" class="ref-issue">person/repo#4</a>`) 362 test("http://localhost:3000/person/repo/issues/4#issuecomment-1234", 363 `<a href="http://localhost:3000/person/repo/issues/4#issuecomment-1234" class="ref-issue">person/repo#4 (comment)</a>`) 364 test("http://localhost:3000/test-owner/test-repo/issues/4", 365 `<a href="http://localhost:3000/test-owner/test-repo/issues/4" class="ref-issue">#4</a>`) 366 test("http://localhost:3000/test-owner/test-repo/issues/4 test", 367 `<a href="http://localhost:3000/test-owner/test-repo/issues/4" class="ref-issue">#4</a> test`) 368 test("http://localhost:3000/test-owner/test-repo/issues/4?a=1&b=2#comment-123 test", 369 `<a href="http://localhost:3000/test-owner/test-repo/issues/4?a=1&b=2#comment-123" class="ref-issue">#4 (comment)</a> test`) 370 test("http://localhost:3000/testOrg/testOrgRepo/pulls/2/files#issuecomment-24", 371 "http://localhost:3000/testOrg/testOrgRepo/pulls/2/files#issuecomment-24") 372 test("http://localhost:3000/testOrg/testOrgRepo/pulls/2/files", 373 "http://localhost:3000/testOrg/testOrgRepo/pulls/2/files") 374 } 375 376 func TestRegExp_sha1CurrentPattern(t *testing.T) { 377 trueTestCases := []string{ 378 "d8a994ef243349f321568f9e36d5c3f444b99cae", 379 "abcdefabcdefabcdefabcdefabcdefabcdefabcd", 380 "(abcdefabcdefabcdefabcdefabcdefabcdefabcd)", 381 "[abcdefabcdefabcdefabcdefabcdefabcdefabcd]", 382 "abcdefabcdefabcdefabcdefabcdefabcdefabcd.", 383 "abcdefabcdefabcdefabcdefabcdefabcdefabcd:", 384 } 385 falseTestCases := []string{ 386 "test", 387 "abcdefg", 388 "e59ff077-2d03-4e6b-964d-63fbaea81f", 389 "abcdefghijklmnopqrstuvwxyzabcdefghijklmn", 390 "abcdefghijklmnopqrstuvwxyzabcdefghijklmO", 391 } 392 393 for _, testCase := range trueTestCases { 394 assert.True(t, hashCurrentPattern.MatchString(testCase)) 395 } 396 for _, testCase := range falseTestCases { 397 assert.False(t, hashCurrentPattern.MatchString(testCase)) 398 } 399 } 400 401 func TestRegExp_anySHA1Pattern(t *testing.T) { 402 testCases := map[string]anyHashPatternResult{ 403 "https://github.com/jquery/jquery/blob/a644101ed04d0beacea864ce805e0c4f86ba1cd1/test/unit/event.js#L2703": { 404 CommitID: "a644101ed04d0beacea864ce805e0c4f86ba1cd1", 405 SubPath: "/test/unit/event.js", 406 QueryHash: "L2703", 407 }, 408 "https://github.com/jquery/jquery/blob/a644101ed04d0beacea864ce805e0c4f86ba1cd1/test/unit/event.js": { 409 CommitID: "a644101ed04d0beacea864ce805e0c4f86ba1cd1", 410 SubPath: "/test/unit/event.js", 411 }, 412 "https://github.com/jquery/jquery/commit/0705be475092aede1eddae01319ec931fb9c65fc": { 413 CommitID: "0705be475092aede1eddae01319ec931fb9c65fc", 414 }, 415 "https://github.com/jquery/jquery/tree/0705be475092aede1eddae01319ec931fb9c65fc/src": { 416 CommitID: "0705be475092aede1eddae01319ec931fb9c65fc", 417 SubPath: "/src", 418 }, 419 "https://try.gogs.io/gogs/gogs/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2": { 420 CommitID: "d8a994ef243349f321568f9e36d5c3f444b99cae", 421 QueryHash: "diff-2", 422 }, 423 "non-url": {}, 424 "http://a/b/c/d/e/1234567812345678123456781234567812345678123456781234567812345678?a=b#L1-L2": { 425 CommitID: "1234567812345678123456781234567812345678123456781234567812345678", 426 QueryHash: "L1-L2", 427 }, 428 "http://a/b/c/d/e/1234567812345678123456781234567812345678123456781234567812345678.": { 429 CommitID: "1234567812345678123456781234567812345678123456781234567812345678", 430 }, 431 "http://a/b/c/d/e/1234567812345678123456781234567812345678123456781234567812345678/sub.": { 432 CommitID: "1234567812345678123456781234567812345678123456781234567812345678", 433 SubPath: "/sub", 434 }, 435 "http://a/b/c/d/e/1234567812345678123456781234567812345678123456781234567812345678?a=b.": { 436 CommitID: "1234567812345678123456781234567812345678123456781234567812345678", 437 }, 438 "http://a/b/c/d/e/1234567812345678123456781234567812345678123456781234567812345678?a=b&c=d": { 439 CommitID: "1234567812345678123456781234567812345678123456781234567812345678", 440 }, 441 "http://a/b/c/d/e/1234567812345678123456781234567812345678123456781234567812345678#hash.": { 442 CommitID: "1234567812345678123456781234567812345678123456781234567812345678", 443 QueryHash: "hash", 444 }, 445 } 446 447 for k, v := range testCases { 448 ret, ok := anyHashPatternExtract(k) 449 if v.CommitID == "" { 450 assert.False(t, ok) 451 } else { 452 assert.EqualValues(t, strings.TrimSuffix(k, "."), ret.FullURL) 453 assert.EqualValues(t, v.CommitID, ret.CommitID) 454 assert.EqualValues(t, v.SubPath, ret.SubPath) 455 assert.EqualValues(t, v.QueryHash, ret.QueryHash) 456 } 457 } 458 } 459 460 func TestRegExp_shortLinkPattern(t *testing.T) { 461 trueTestCases := []string{ 462 "[[stuff]]", 463 "[[]]", 464 "[[stuff|title=Difficult name with spaces*!]]", 465 } 466 falseTestCases := []string{ 467 "test", 468 "abcdefg", 469 "[[]", 470 "[[", 471 "[]", 472 "]]", 473 "abcdefghijklmnopqrstuvwxyz", 474 } 475 476 for _, testCase := range trueTestCases { 477 assert.True(t, shortLinkPattern.MatchString(testCase)) 478 } 479 for _, testCase := range falseTestCases { 480 assert.False(t, shortLinkPattern.MatchString(testCase)) 481 } 482 }