-
-
Notifications
You must be signed in to change notification settings - Fork 5.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Rework special link parsing in the post-processing of markup (#3354)
* Get rid of autolink * autolink in markdown * Replace email addresses with mailto links * better handling of links * Remove autolink.js from footer * Refactor entire html.go * fix some bugs * Make tests green, move what we can to html_internal_test, various other changes to processor logic * Make markdown tests work again This is just a description to allow me to force push in order to restart the drone build. * Fix failing markdown tests in routers/api/v1/misc * Add license headers, log errors, future-proof <body> * fix formatting
- Loading branch information
Showing
12 changed files
with
1,010 additions
and
1,006 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,382 @@ | ||
// Copyright 2018 The Gitea Authors. All rights reserved. | ||
// Use of this source code is governed by a MIT-style | ||
// license that can be found in the LICENSE file. | ||
package markup | ||
|
||
import ( | ||
"fmt" | ||
"strconv" | ||
"strings" | ||
"testing" | ||
|
||
"code.gitea.io/gitea/modules/setting" | ||
"code.gitea.io/gitea/modules/util" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
const AppURL = "http://localhost:3000/" | ||
const Repo = "gogits/gogs" | ||
const AppSubURL = AppURL + Repo + "/" | ||
|
||
// alphanumLink an HTML link to an alphanumeric-style issue | ||
func alphanumIssueLink(baseURL string, name string) string { | ||
return link(util.URLJoin(baseURL, name), name) | ||
} | ||
|
||
// numericLink an HTML to a numeric-style issue | ||
func numericIssueLink(baseURL string, index int) string { | ||
return link(util.URLJoin(baseURL, strconv.Itoa(index)), fmt.Sprintf("#%d", index)) | ||
} | ||
|
||
// urlContentsLink an HTML link whose contents is the target URL | ||
func urlContentsLink(href string) string { | ||
return link(href, href) | ||
} | ||
|
||
// link an HTML link | ||
func link(href, contents string) string { | ||
return fmt.Sprintf("<a href=\"%s\">%s</a>", href, contents) | ||
} | ||
|
||
var numericMetas = map[string]string{ | ||
"format": "https://someurl.com/{user}/{repo}/{index}", | ||
"user": "someUser", | ||
"repo": "someRepo", | ||
"style": IssueNameStyleNumeric, | ||
} | ||
|
||
var alphanumericMetas = map[string]string{ | ||
"format": "https://someurl.com/{user}/{repo}/{index}", | ||
"user": "someUser", | ||
"repo": "someRepo", | ||
"style": IssueNameStyleAlphanumeric, | ||
} | ||
|
||
func TestRender_IssueIndexPattern(t *testing.T) { | ||
// numeric: render inputs without valid mentions | ||
test := func(s string) { | ||
testRenderIssueIndexPattern(t, s, s, nil) | ||
testRenderIssueIndexPattern(t, s, s, &postProcessCtx{metas: numericMetas}) | ||
} | ||
|
||
// should not render anything when there are no mentions | ||
test("") | ||
test("this is a test") | ||
test("test 123 123 1234") | ||
test("#") | ||
test("# # #") | ||
test("# 123") | ||
test("#abcd") | ||
test("test#1234") | ||
test("#1234test") | ||
test(" test #1234test") | ||
|
||
// should not render issue mention without leading space | ||
test("test#54321 issue") | ||
|
||
// should not render issue mention without trailing space | ||
test("test #54321issue") | ||
} | ||
|
||
func TestRender_IssueIndexPattern2(t *testing.T) { | ||
setting.AppURL = AppURL | ||
setting.AppSubURL = AppSubURL | ||
|
||
// numeric: render inputs with valid mentions | ||
test := func(s, expectedFmt string, indices ...int) { | ||
links := make([]interface{}, len(indices)) | ||
for i, index := range indices { | ||
links[i] = numericIssueLink(util.URLJoin(setting.AppSubURL, "issues"), index) | ||
} | ||
expectedNil := fmt.Sprintf(expectedFmt, links...) | ||
testRenderIssueIndexPattern(t, s, expectedNil, nil) | ||
|
||
for i, index := range indices { | ||
links[i] = numericIssueLink("https://someurl.com/someUser/someRepo/", index) | ||
} | ||
expectedNum := fmt.Sprintf(expectedFmt, links...) | ||
testRenderIssueIndexPattern(t, s, expectedNum, &postProcessCtx{metas: numericMetas}) | ||
} | ||
|
||
// should render freestanding mentions | ||
test("#1234 test", "%s test", 1234) | ||
test("test #8 issue", "test %s issue", 8) | ||
test("test issue #1234", "test issue %s", 1234) | ||
|
||
// should render mentions in parentheses | ||
test("(#54321 issue)", "(%s issue)", 54321) | ||
test("test (#9801 extra) issue", "test (%s extra) issue", 9801) | ||
test("test (#1)", "test (%s)", 1) | ||
|
||
// should render multiple issue mentions in the same line | ||
test("#54321 #1243", "%s %s", 54321, 1243) | ||
test("wow (#54321 #1243)", "wow (%s %s)", 54321, 1243) | ||
test("(#4)(#5)", "(%s)(%s)", 4, 5) | ||
test("#1 (#4321) test", "%s (%s) test", 1, 4321) | ||
} | ||
|
||
func TestRender_IssueIndexPattern3(t *testing.T) { | ||
setting.AppURL = AppURL | ||
setting.AppSubURL = AppSubURL | ||
|
||
// alphanumeric: render inputs without valid mentions | ||
test := func(s string) { | ||
testRenderIssueIndexPattern(t, s, s, &postProcessCtx{metas: alphanumericMetas}) | ||
} | ||
test("") | ||
test("this is a test") | ||
test("test 123 123 1234") | ||
test("#") | ||
test("# 123") | ||
test("#abcd") | ||
test("test #123") | ||
test("abc-1234") // issue prefix must be capital | ||
test("ABc-1234") // issue prefix must be _all_ capital | ||
test("ABCDEFGHIJK-1234") // the limit is 10 characters in the prefix | ||
test("ABC1234") // dash is required | ||
test("test ABC- test") // number is required | ||
test("test -1234 test") // prefix is required | ||
test("testABC-123 test") // leading space is required | ||
test("test ABC-123test") // trailing space is required | ||
test("ABC-0123") // no leading zero | ||
} | ||
|
||
func TestRender_IssueIndexPattern4(t *testing.T) { | ||
setting.AppURL = AppURL | ||
setting.AppSubURL = AppSubURL | ||
|
||
// alphanumeric: render inputs with valid mentions | ||
test := func(s, expectedFmt string, names ...string) { | ||
links := make([]interface{}, len(names)) | ||
for i, name := range names { | ||
links[i] = alphanumIssueLink("https://someurl.com/someUser/someRepo/", name) | ||
} | ||
expected := fmt.Sprintf(expectedFmt, links...) | ||
testRenderIssueIndexPattern(t, s, expected, &postProcessCtx{metas: alphanumericMetas}) | ||
} | ||
test("OTT-1234 test", "%s test", "OTT-1234") | ||
test("test T-12 issue", "test %s issue", "T-12") | ||
test("test issue ABCDEFGHIJ-1234567890", "test issue %s", "ABCDEFGHIJ-1234567890") | ||
} | ||
|
||
func testRenderIssueIndexPattern(t *testing.T, input, expected string, ctx *postProcessCtx) { | ||
if ctx == nil { | ||
ctx = new(postProcessCtx) | ||
} | ||
ctx.procs = []processor{issueIndexPatternProcessor} | ||
if ctx.urlPrefix == "" { | ||
ctx.urlPrefix = AppSubURL | ||
} | ||
res, err := ctx.postProcess([]byte(input)) | ||
assert.NoError(t, err) | ||
assert.Equal(t, expected, string(res)) | ||
} | ||
|
||
func TestRender_AutoLink(t *testing.T) { | ||
setting.AppURL = AppURL | ||
setting.AppSubURL = AppSubURL | ||
|
||
test := func(input, expected string) { | ||
buffer, err := PostProcess([]byte(input), setting.AppSubURL, nil, false) | ||
assert.Equal(t, err, nil) | ||
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(buffer))) | ||
buffer, err = PostProcess([]byte(input), setting.AppSubURL, nil, true) | ||
assert.Equal(t, err, nil) | ||
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(buffer))) | ||
} | ||
|
||
// render valid issue URLs | ||
test(util.URLJoin(setting.AppSubURL, "issues", "3333"), | ||
numericIssueLink(util.URLJoin(setting.AppSubURL, "issues"), 3333)) | ||
|
||
// render valid commit URLs | ||
tmp := util.URLJoin(AppSubURL, "commit", "d8a994ef243349f321568f9e36d5c3f444b99cae") | ||
test(tmp, "<a href=\""+tmp+"\">d8a994ef24</a>") | ||
tmp += "#diff-2" | ||
test(tmp, "<a href=\""+tmp+"\">d8a994ef24 (diff-2)</a>") | ||
|
||
// render other commit URLs | ||
tmp = "https://external-link.gogs.io/gogs/gogs/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2" | ||
test(tmp, "<a href=\""+tmp+"\">d8a994ef24 (diff-2)</a>") | ||
} | ||
|
||
func TestRender_FullIssueURLs(t *testing.T) { | ||
setting.AppURL = AppURL | ||
setting.AppSubURL = AppSubURL | ||
|
||
test := func(input, expected string) { | ||
ctx := new(postProcessCtx) | ||
ctx.procs = []processor{fullIssuePatternProcessor} | ||
if ctx.urlPrefix == "" { | ||
ctx.urlPrefix = AppSubURL | ||
} | ||
result, err := ctx.postProcess([]byte(input)) | ||
assert.NoError(t, err) | ||
assert.Equal(t, expected, string(result)) | ||
} | ||
test("Here is a link https://git.osgeo.org/gogs/postgis/postgis/pulls/6", | ||
"Here is a link https://git.osgeo.org/gogs/postgis/postgis/pulls/6") | ||
test("Look here http://localhost:3000/person/repo/issues/4", | ||
`Look here <a href="http://localhost:3000/person/repo/issues/4">#4</a>`) | ||
test("http://localhost:3000/person/repo/issues/4#issuecomment-1234", | ||
`<a href="http://localhost:3000/person/repo/issues/4#issuecomment-1234">#4</a>`) | ||
} | ||
|
||
func TestRegExp_issueNumericPattern(t *testing.T) { | ||
trueTestCases := []string{ | ||
"#1234", | ||
"#0", | ||
"#1234567890987654321", | ||
" #12", | ||
} | ||
falseTestCases := []string{ | ||
"# 1234", | ||
"# 0", | ||
"# ", | ||
"#", | ||
"#ABC", | ||
"#1A2B", | ||
"", | ||
"ABC", | ||
} | ||
|
||
for _, testCase := range trueTestCases { | ||
assert.True(t, issueNumericPattern.MatchString(testCase)) | ||
} | ||
for _, testCase := range falseTestCases { | ||
assert.False(t, issueNumericPattern.MatchString(testCase)) | ||
} | ||
} | ||
|
||
func TestRegExp_sha1CurrentPattern(t *testing.T) { | ||
trueTestCases := []string{ | ||
"d8a994ef243349f321568f9e36d5c3f444b99cae", | ||
"abcdefabcdefabcdefabcdefabcdefabcdefabcd", | ||
} | ||
falseTestCases := []string{ | ||
"test", | ||
"abcdefg", | ||
"abcdefghijklmnopqrstuvwxyzabcdefghijklmn", | ||
"abcdefghijklmnopqrstuvwxyzabcdefghijklmO", | ||
} | ||
|
||
for _, testCase := range trueTestCases { | ||
assert.True(t, sha1CurrentPattern.MatchString(testCase)) | ||
} | ||
for _, testCase := range falseTestCases { | ||
assert.False(t, sha1CurrentPattern.MatchString(testCase)) | ||
} | ||
} | ||
|
||
func TestRegExp_anySHA1Pattern(t *testing.T) { | ||
testCases := map[string][]string{ | ||
"https://github.com/jquery/jquery/blob/a644101ed04d0beacea864ce805e0c4f86ba1cd1/test/unit/event.js#L2703": { | ||
"a644101ed04d0beacea864ce805e0c4f86ba1cd1", | ||
"test/unit/event.js", | ||
"L2703", | ||
}, | ||
"https://github.com/jquery/jquery/blob/a644101ed04d0beacea864ce805e0c4f86ba1cd1/test/unit/event.js": { | ||
"a644101ed04d0beacea864ce805e0c4f86ba1cd1", | ||
"test/unit/event.js", | ||
"", | ||
}, | ||
"https://github.com/jquery/jquery/commit/0705be475092aede1eddae01319ec931fb9c65fc": { | ||
"0705be475092aede1eddae01319ec931fb9c65fc", | ||
"", | ||
"", | ||
}, | ||
"https://github.com/jquery/jquery/tree/0705be475092aede1eddae01319ec931fb9c65fc/src": { | ||
"0705be475092aede1eddae01319ec931fb9c65fc", | ||
"src", | ||
"", | ||
}, | ||
"https://try.gogs.io/gogs/gogs/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2": { | ||
"d8a994ef243349f321568f9e36d5c3f444b99cae", | ||
"", | ||
"diff-2", | ||
}, | ||
} | ||
|
||
for k, v := range testCases { | ||
assert.Equal(t, anySHA1Pattern.FindStringSubmatch(k)[1:], v) | ||
} | ||
} | ||
|
||
func TestRegExp_mentionPattern(t *testing.T) { | ||
trueTestCases := []string{ | ||
"@Unknwon", | ||
"@ANT_123", | ||
"@xxx-DiN0-z-A..uru..s-xxx", | ||
" @lol ", | ||
" @Te/st", | ||
} | ||
falseTestCases := []string{ | ||
"@ 0", | ||
"@ ", | ||
"@", | ||
"", | ||
"ABC", | ||
} | ||
|
||
for _, testCase := range trueTestCases { | ||
res := mentionPattern.MatchString(testCase) | ||
assert.True(t, res) | ||
} | ||
for _, testCase := range falseTestCases { | ||
res := mentionPattern.MatchString(testCase) | ||
assert.False(t, res) | ||
} | ||
} | ||
|
||
func TestRegExp_issueAlphanumericPattern(t *testing.T) { | ||
trueTestCases := []string{ | ||
"ABC-1234", | ||
"A-1", | ||
"RC-80", | ||
"ABCDEFGHIJ-1234567890987654321234567890", | ||
} | ||
falseTestCases := []string{ | ||
"RC-08", | ||
"PR-0", | ||
"ABCDEFGHIJK-1", | ||
"PR_1", | ||
"", | ||
"#ABC", | ||
"", | ||
"ABC", | ||
"GG-", | ||
"rm-1", | ||
} | ||
|
||
for _, testCase := range trueTestCases { | ||
assert.True(t, issueAlphanumericPattern.MatchString(testCase)) | ||
} | ||
for _, testCase := range falseTestCases { | ||
assert.False(t, issueAlphanumericPattern.MatchString(testCase)) | ||
} | ||
} | ||
|
||
func TestRegExp_shortLinkPattern(t *testing.T) { | ||
trueTestCases := []string{ | ||
"[[stuff]]", | ||
"[[]]", | ||
"[[stuff|title=Difficult name with spaces*!]]", | ||
} | ||
falseTestCases := []string{ | ||
"test", | ||
"abcdefg", | ||
"[[]", | ||
"[[", | ||
"[]", | ||
"]]", | ||
"abcdefghijklmnopqrstuvwxyz", | ||
} | ||
|
||
for _, testCase := range trueTestCases { | ||
assert.True(t, shortLinkPattern.MatchString(testCase)) | ||
} | ||
for _, testCase := range falseTestCases { | ||
assert.False(t, shortLinkPattern.MatchString(testCase)) | ||
} | ||
} |
Oops, something went wrong.