-
-
Notifications
You must be signed in to change notification settings - Fork 5.8k
Rework special link parsing in the post-processing of markup #3354
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
38aca97
Get rid of autolink
thehowl 0c86b94
autolink in markdown
thehowl 4b2be0b
Replace email addresses with mailto links
thehowl 49c7156
better handling of links
thehowl f55b2d9
Remove autolink.js from footer
thehowl fcf05be
Refactor entire html.go
thehowl e0a3ee2
fix some bugs
thehowl 85f227f
Make tests green, move what we can to html_internal_test, various oth…
thehowl dc9f467
Make markdown tests work again
thehowl 00060dd
Fix failing markdown tests in routers/api/v1/misc
thehowl 746d28d
Add license headers, log errors, future-proof <body>
thehowl 886d945
fix formatting
thehowl ab78354
Merge branch 'master' into fix-3327
lunny File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or 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.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing license comment