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

html_internal_test.go 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. // Copyright 2018 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package markup
  4. import (
  5. "fmt"
  6. "strconv"
  7. "strings"
  8. "testing"
  9. "code.gitea.io/gitea/modules/setting"
  10. "code.gitea.io/gitea/modules/util"
  11. "github.com/stretchr/testify/assert"
  12. )
  13. const (
  14. TestAppURL = "http://localhost:3000/"
  15. TestOrgRepo = "gogits/gogs"
  16. TestRepoURL = TestAppURL + TestOrgRepo + "/"
  17. )
  18. // externalIssueLink an HTML link to an alphanumeric-style issue
  19. func externalIssueLink(baseURL, class, name string) string {
  20. return link(util.URLJoin(baseURL, name), class, name)
  21. }
  22. // numericLink an HTML to a numeric-style issue
  23. func numericIssueLink(baseURL, class string, index int, marker string) string {
  24. return link(util.URLJoin(baseURL, strconv.Itoa(index)), class, fmt.Sprintf("%s%d", marker, index))
  25. }
  26. // link an HTML link
  27. func link(href, class, contents string) string {
  28. if class != "" {
  29. class = " class=\"" + class + "\""
  30. }
  31. return fmt.Sprintf("<a href=\"%s\"%s>%s</a>", href, class, contents)
  32. }
  33. var numericMetas = map[string]string{
  34. "format": "https://someurl.com/{user}/{repo}/{index}",
  35. "user": "someUser",
  36. "repo": "someRepo",
  37. "style": IssueNameStyleNumeric,
  38. }
  39. var alphanumericMetas = map[string]string{
  40. "format": "https://someurl.com/{user}/{repo}/{index}",
  41. "user": "someUser",
  42. "repo": "someRepo",
  43. "style": IssueNameStyleAlphanumeric,
  44. }
  45. var regexpMetas = map[string]string{
  46. "format": "https://someurl.com/{user}/{repo}/{index}",
  47. "user": "someUser",
  48. "repo": "someRepo",
  49. "style": IssueNameStyleRegexp,
  50. }
  51. // these values should match the TestOrgRepo const above
  52. var localMetas = map[string]string{
  53. "user": "gogits",
  54. "repo": "gogs",
  55. }
  56. func TestRender_IssueIndexPattern(t *testing.T) {
  57. // numeric: render inputs without valid mentions
  58. test := func(s string) {
  59. testRenderIssueIndexPattern(t, s, s, &RenderContext{})
  60. testRenderIssueIndexPattern(t, s, s, &RenderContext{Metas: numericMetas})
  61. }
  62. // should not render anything when there are no mentions
  63. test("")
  64. test("this is a test")
  65. test("test 123 123 1234")
  66. test("#")
  67. test("# # #")
  68. test("# 123")
  69. test("#abcd")
  70. test("test#1234")
  71. test("#1234test")
  72. test("#abcd")
  73. test("test!1234")
  74. test("!1234test")
  75. test(" test !1234test")
  76. test("/home/gitea/#1234")
  77. test("/home/gitea/!1234")
  78. // should not render issue mention without leading space
  79. test("test#54321 issue")
  80. // should not render issue mention without trailing space
  81. test("test #54321issue")
  82. }
  83. func TestRender_IssueIndexPattern2(t *testing.T) {
  84. setting.AppURL = TestAppURL
  85. // numeric: render inputs with valid mentions
  86. test := func(s, expectedFmt, marker string, indices ...int) {
  87. var path, prefix string
  88. isExternal := false
  89. if marker == "!" {
  90. path = "pulls"
  91. prefix = "http://localhost:3000/someUser/someRepo/pulls/"
  92. } else {
  93. path = "issues"
  94. prefix = "https://someurl.com/someUser/someRepo/"
  95. isExternal = true
  96. }
  97. links := make([]interface{}, len(indices))
  98. for i, index := range indices {
  99. links[i] = numericIssueLink(util.URLJoin(TestRepoURL, path), "ref-issue", index, marker)
  100. }
  101. expectedNil := fmt.Sprintf(expectedFmt, links...)
  102. testRenderIssueIndexPattern(t, s, expectedNil, &RenderContext{Metas: localMetas})
  103. class := "ref-issue"
  104. if isExternal {
  105. class += " ref-external-issue"
  106. }
  107. for i, index := range indices {
  108. links[i] = numericIssueLink(prefix, class, index, marker)
  109. }
  110. expectedNum := fmt.Sprintf(expectedFmt, links...)
  111. testRenderIssueIndexPattern(t, s, expectedNum, &RenderContext{Metas: numericMetas})
  112. }
  113. // should render freestanding mentions
  114. test("#1234 test", "%s test", "#", 1234)
  115. test("test #8 issue", "test %s issue", "#", 8)
  116. test("!1234 test", "%s test", "!", 1234)
  117. test("test !8 issue", "test %s issue", "!", 8)
  118. test("test issue #1234", "test issue %s", "#", 1234)
  119. test("fixes issue #1234.", "fixes issue %s.", "#", 1234)
  120. // should render mentions in parentheses / brackets
  121. test("(#54321 issue)", "(%s issue)", "#", 54321)
  122. test("[#54321 issue]", "[%s issue]", "#", 54321)
  123. test("test (#9801 extra) issue", "test (%s extra) issue", "#", 9801)
  124. test("test (!9801 extra) issue", "test (%s extra) issue", "!", 9801)
  125. test("test (#1)", "test (%s)", "#", 1)
  126. // should render multiple issue mentions in the same line
  127. test("#54321 #1243", "%s %s", "#", 54321, 1243)
  128. test("wow (#54321 #1243)", "wow (%s %s)", "#", 54321, 1243)
  129. test("(#4)(#5)", "(%s)(%s)", "#", 4, 5)
  130. test("#1 (#4321) test", "%s (%s) test", "#", 1, 4321)
  131. // should render with :
  132. test("#1234: test", "%s: test", "#", 1234)
  133. test("wow (#54321: test)", "wow (%s: test)", "#", 54321)
  134. }
  135. func TestRender_IssueIndexPattern3(t *testing.T) {
  136. setting.AppURL = TestAppURL
  137. // alphanumeric: render inputs without valid mentions
  138. test := func(s string) {
  139. testRenderIssueIndexPattern(t, s, s, &RenderContext{Metas: alphanumericMetas})
  140. }
  141. test("")
  142. test("this is a test")
  143. test("test 123 123 1234")
  144. test("#")
  145. test("# 123")
  146. test("#abcd")
  147. test("test #123")
  148. test("abc-1234") // issue prefix must be capital
  149. test("ABc-1234") // issue prefix must be _all_ capital
  150. test("ABCDEFGHIJK-1234") // the limit is 10 characters in the prefix
  151. test("ABC1234") // dash is required
  152. test("test ABC- test") // number is required
  153. test("test -1234 test") // prefix is required
  154. test("testABC-123 test") // leading space is required
  155. test("test ABC-123test") // trailing space is required
  156. test("ABC-0123") // no leading zero
  157. }
  158. func TestRender_IssueIndexPattern4(t *testing.T) {
  159. setting.AppURL = TestAppURL
  160. // alphanumeric: render inputs with valid mentions
  161. test := func(s, expectedFmt string, names ...string) {
  162. links := make([]interface{}, len(names))
  163. for i, name := range names {
  164. links[i] = externalIssueLink("https://someurl.com/someUser/someRepo/", "ref-issue ref-external-issue", name)
  165. }
  166. expected := fmt.Sprintf(expectedFmt, links...)
  167. testRenderIssueIndexPattern(t, s, expected, &RenderContext{Metas: alphanumericMetas})
  168. }
  169. test("OTT-1234 test", "%s test", "OTT-1234")
  170. test("test T-12 issue", "test %s issue", "T-12")
  171. test("test issue ABCDEFGHIJ-1234567890", "test issue %s", "ABCDEFGHIJ-1234567890")
  172. }
  173. func TestRender_IssueIndexPattern5(t *testing.T) {
  174. setting.AppURL = TestAppURL
  175. // regexp: render inputs without valid mentions
  176. test := func(s, expectedFmt, pattern string, ids, names []string) {
  177. metas := regexpMetas
  178. metas["regexp"] = pattern
  179. links := make([]interface{}, len(ids))
  180. for i, id := range ids {
  181. links[i] = link(util.URLJoin("https://someurl.com/someUser/someRepo/", id), "ref-issue ref-external-issue", names[i])
  182. }
  183. expected := fmt.Sprintf(expectedFmt, links...)
  184. testRenderIssueIndexPattern(t, s, expected, &RenderContext{Metas: metas})
  185. }
  186. test("abc ISSUE-123 def", "abc %s def",
  187. "ISSUE-(\\d+)",
  188. []string{"123"},
  189. []string{"ISSUE-123"},
  190. )
  191. test("abc (ISSUE 123) def", "abc %s def",
  192. "\\(ISSUE (\\d+)\\)",
  193. []string{"123"},
  194. []string{"(ISSUE 123)"},
  195. )
  196. test("abc ISSUE-123 def", "abc %s def",
  197. "(ISSUE-(\\d+))",
  198. []string{"ISSUE-123"},
  199. []string{"ISSUE-123"},
  200. )
  201. testRenderIssueIndexPattern(t, "will not match", "will not match", &RenderContext{Metas: regexpMetas})
  202. }
  203. func testRenderIssueIndexPattern(t *testing.T, input, expected string, ctx *RenderContext) {
  204. if ctx.URLPrefix == "" {
  205. ctx.URLPrefix = TestAppURL
  206. }
  207. var buf strings.Builder
  208. err := postProcess(ctx, []processor{issueIndexPatternProcessor}, strings.NewReader(input), &buf)
  209. assert.NoError(t, err)
  210. assert.Equal(t, expected, buf.String(), "input=%q", input)
  211. }
  212. func TestRender_AutoLink(t *testing.T) {
  213. setting.AppURL = TestAppURL
  214. test := func(input, expected string) {
  215. var buffer strings.Builder
  216. err := PostProcess(&RenderContext{
  217. URLPrefix: TestRepoURL,
  218. Metas: localMetas,
  219. }, strings.NewReader(input), &buffer)
  220. assert.Equal(t, err, nil)
  221. assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer.String()))
  222. buffer.Reset()
  223. err = PostProcess(&RenderContext{
  224. URLPrefix: TestRepoURL,
  225. Metas: localMetas,
  226. IsWiki: true,
  227. }, strings.NewReader(input), &buffer)
  228. assert.Equal(t, err, nil)
  229. assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer.String()))
  230. }
  231. // render valid issue URLs
  232. test(util.URLJoin(TestRepoURL, "issues", "3333"),
  233. numericIssueLink(util.URLJoin(TestRepoURL, "issues"), "ref-issue", 3333, "#"))
  234. // render valid commit URLs
  235. tmp := util.URLJoin(TestRepoURL, "commit", "d8a994ef243349f321568f9e36d5c3f444b99cae")
  236. test(tmp, "<a href=\""+tmp+"\" class=\"commit\"><code class=\"nohighlight\">d8a994ef24</code></a>")
  237. tmp += "#diff-2"
  238. test(tmp, "<a href=\""+tmp+"\" class=\"commit\"><code class=\"nohighlight\">d8a994ef24 (diff-2)</code></a>")
  239. // render other commit URLs
  240. tmp = "https://external-link.gitea.io/go-gitea/gitea/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2"
  241. test(tmp, "<a href=\""+tmp+"\" class=\"commit\"><code class=\"nohighlight\">d8a994ef24 (diff-2)</code></a>")
  242. }
  243. func TestRender_FullIssueURLs(t *testing.T) {
  244. setting.AppURL = TestAppURL
  245. test := func(input, expected string) {
  246. var result strings.Builder
  247. err := postProcess(&RenderContext{
  248. URLPrefix: TestRepoURL,
  249. Metas: localMetas,
  250. }, []processor{fullIssuePatternProcessor}, strings.NewReader(input), &result)
  251. assert.NoError(t, err)
  252. assert.Equal(t, expected, result.String())
  253. }
  254. test("Here is a link https://git.osgeo.org/gogs/postgis/postgis/pulls/6",
  255. "Here is a link https://git.osgeo.org/gogs/postgis/postgis/pulls/6")
  256. test("Look here http://localhost:3000/person/repo/issues/4",
  257. `Look here <a href="http://localhost:3000/person/repo/issues/4" class="ref-issue">person/repo#4</a>`)
  258. test("http://localhost:3000/person/repo/issues/4#issuecomment-1234",
  259. `<a href="http://localhost:3000/person/repo/issues/4#issuecomment-1234" class="ref-issue">person/repo#4</a>`)
  260. test("http://localhost:3000/gogits/gogs/issues/4",
  261. `<a href="http://localhost:3000/gogits/gogs/issues/4" class="ref-issue">#4</a>`)
  262. test("http://localhost:3000/gogits/gogs/issues/4 test",
  263. `<a href="http://localhost:3000/gogits/gogs/issues/4" class="ref-issue">#4</a> test`)
  264. test("http://localhost:3000/gogits/gogs/issues/4?a=1&b=2#comment-123 test",
  265. `<a href="http://localhost:3000/gogits/gogs/issues/4?a=1&amp;b=2#comment-123" class="ref-issue">#4</a> test`)
  266. }
  267. func TestRegExp_sha1CurrentPattern(t *testing.T) {
  268. trueTestCases := []string{
  269. "d8a994ef243349f321568f9e36d5c3f444b99cae",
  270. "abcdefabcdefabcdefabcdefabcdefabcdefabcd",
  271. "(abcdefabcdefabcdefabcdefabcdefabcdefabcd)",
  272. "[abcdefabcdefabcdefabcdefabcdefabcdefabcd]",
  273. "abcdefabcdefabcdefabcdefabcdefabcdefabcd.",
  274. }
  275. falseTestCases := []string{
  276. "test",
  277. "abcdefg",
  278. "e59ff077-2d03-4e6b-964d-63fbaea81f",
  279. "abcdefghijklmnopqrstuvwxyzabcdefghijklmn",
  280. "abcdefghijklmnopqrstuvwxyzabcdefghijklmO",
  281. }
  282. for _, testCase := range trueTestCases {
  283. assert.True(t, sha1CurrentPattern.MatchString(testCase))
  284. }
  285. for _, testCase := range falseTestCases {
  286. assert.False(t, sha1CurrentPattern.MatchString(testCase))
  287. }
  288. }
  289. func TestRegExp_anySHA1Pattern(t *testing.T) {
  290. testCases := map[string][]string{
  291. "https://github.com/jquery/jquery/blob/a644101ed04d0beacea864ce805e0c4f86ba1cd1/test/unit/event.js#L2703": {
  292. "a644101ed04d0beacea864ce805e0c4f86ba1cd1",
  293. "/test/unit/event.js",
  294. "#L2703",
  295. },
  296. "https://github.com/jquery/jquery/blob/a644101ed04d0beacea864ce805e0c4f86ba1cd1/test/unit/event.js": {
  297. "a644101ed04d0beacea864ce805e0c4f86ba1cd1",
  298. "/test/unit/event.js",
  299. "",
  300. },
  301. "https://github.com/jquery/jquery/commit/0705be475092aede1eddae01319ec931fb9c65fc": {
  302. "0705be475092aede1eddae01319ec931fb9c65fc",
  303. "",
  304. "",
  305. },
  306. "https://github.com/jquery/jquery/tree/0705be475092aede1eddae01319ec931fb9c65fc/src": {
  307. "0705be475092aede1eddae01319ec931fb9c65fc",
  308. "/src",
  309. "",
  310. },
  311. "https://try.gogs.io/gogs/gogs/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2": {
  312. "d8a994ef243349f321568f9e36d5c3f444b99cae",
  313. "",
  314. "#diff-2",
  315. },
  316. }
  317. for k, v := range testCases {
  318. assert.Equal(t, anySHA1Pattern.FindStringSubmatch(k)[1:], v)
  319. }
  320. }
  321. func TestRegExp_shortLinkPattern(t *testing.T) {
  322. trueTestCases := []string{
  323. "[[stuff]]",
  324. "[[]]",
  325. "[[stuff|title=Difficult name with spaces*!]]",
  326. }
  327. falseTestCases := []string{
  328. "test",
  329. "abcdefg",
  330. "[[]",
  331. "[[",
  332. "[]",
  333. "]]",
  334. "abcdefghijklmnopqrstuvwxyz",
  335. }
  336. for _, testCase := range trueTestCases {
  337. assert.True(t, shortLinkPattern.MatchString(testCase))
  338. }
  339. for _, testCase := range falseTestCases {
  340. assert.False(t, shortLinkPattern.MatchString(testCase))
  341. }
  342. }