2017-09-16 11:17:57 -06:00
// Copyright 2017 The Gitea Authors. All rights reserved.
2022-11-27 11:20:29 -07:00
// SPDX-License-Identifier: MIT
2017-09-16 11:17:57 -06:00
package markup_test
import (
2021-08-12 16:22:05 -06:00
"io"
2017-09-16 11:17:57 -06:00
"strings"
"testing"
2020-04-28 12:05:39 -06:00
"code.gitea.io/gitea/modules/emoji"
2022-01-19 16:26:57 -07:00
"code.gitea.io/gitea/modules/git"
2024-05-30 01:04:01 -06:00
"code.gitea.io/gitea/modules/gitrepo"
2023-10-23 20:54:59 -06:00
"code.gitea.io/gitea/modules/markup"
2018-02-27 00:09:18 -07:00
"code.gitea.io/gitea/modules/markup/markdown"
2017-09-16 11:17:57 -06:00
"code.gitea.io/gitea/modules/setting"
2024-11-13 22:02:11 -07:00
testModule "code.gitea.io/gitea/modules/test"
2018-02-20 05:50:42 -07:00
"code.gitea.io/gitea/modules/util"
2017-09-16 11:17:57 -06:00
"github.com/stretchr/testify/assert"
)
2024-05-30 01:04:01 -06:00
var (
testRepoOwnerName = "user13"
testRepoName = "repo11"
localMetas = map [ string ] string {
"user" : testRepoOwnerName ,
"repo" : testRepoName ,
}
2024-11-16 01:41:44 -07:00
localWikiMetas = map [ string ] string {
"user" : testRepoOwnerName ,
"repo" : testRepoName ,
"markupContentMode" : "wiki" ,
}
2024-05-30 01:04:01 -06:00
)
type mockRepo struct {
OwnerName string
RepoName string
}
func ( m * mockRepo ) GetOwnerName ( ) string {
return m . OwnerName
}
func ( m * mockRepo ) GetName ( ) string {
return m . RepoName
2019-04-16 01:53:57 -06:00
}
2024-05-30 01:04:01 -06:00
func newMockRepo ( ownerName , repoName string ) gitrepo . Repository {
return & mockRepo {
OwnerName : ownerName ,
RepoName : repoName ,
2022-06-16 09:47:44 -06:00
}
}
2017-09-16 11:17:57 -06:00
func TestRender_Commits ( t * testing . T ) {
2023-10-23 20:54:59 -06:00
setting . AppURL = markup . TestAppURL
2017-09-16 11:17:57 -06:00
test := func ( input , expected string ) {
2023-10-23 20:54:59 -06:00
buffer , err := markup . RenderString ( & markup . RenderContext {
2022-06-15 21:33:23 -06:00
Ctx : git . DefaultContext ,
RelativePath : ".md" ,
2024-01-15 01:49:24 -07:00
Links : markup . Links {
2024-03-13 04:34:58 -06:00
AbsolutePrefix : true ,
Base : markup . TestRepoURL ,
2024-01-15 01:49:24 -07:00
} ,
2024-05-30 01:04:01 -06:00
Repo : newMockRepo ( testRepoOwnerName , testRepoName ) ,
2024-01-15 01:49:24 -07:00
Metas : localMetas ,
2021-04-19 16:25:08 -06:00
} , input )
assert . NoError ( t , err )
2019-07-23 12:50:39 -06:00
assert . Equal ( t , strings . TrimSpace ( expected ) , strings . TrimSpace ( buffer ) )
2017-09-16 11:17:57 -06:00
}
2022-01-20 10:46:10 -07:00
sha := "65f1bf27bc3bf70f64657658635e66094edbcb4d"
2024-05-30 01:04:01 -06:00
repo := markup . TestAppURL + testRepoOwnerName + "/" + testRepoName + "/"
2022-01-20 10:46:10 -07:00
commit := util . URLJoin ( repo , "commit" , sha )
tree := util . URLJoin ( repo , "tree" , sha , "src" )
file := util . URLJoin ( repo , "commit" , sha , "example.txt" )
fileWithExtra := file + ":"
fileWithHash := file + "#L2"
fileWithHasExtra := file + "#L2:"
commitCompare := util . URLJoin ( repo , "compare" , sha + "..." + sha )
commitCompareWithHash := commitCompare + "#L2"
2017-09-16 11:17:57 -06:00
2019-08-14 02:04:55 -06:00
test ( sha , ` <p><a href=" ` + commit + ` " rel="nofollow"><code>65f1bf27bc</code></a></p> ` )
test ( sha [ : 7 ] , ` <p><a href=" ` + commit [ : len ( commit ) - ( 40 - 7 ) ] + ` " rel="nofollow"><code>65f1bf2</code></a></p> ` )
test ( sha [ : 39 ] , ` <p><a href=" ` + commit [ : len ( commit ) - ( 40 - 39 ) ] + ` " rel="nofollow"><code>65f1bf27bc</code></a></p> ` )
test ( commit , ` <p><a href=" ` + commit + ` " rel="nofollow"><code>65f1bf27bc</code></a></p> ` )
test ( tree , ` <p><a href=" ` + tree + ` " rel="nofollow"><code>65f1bf27bc/src</code></a></p> ` )
2021-12-11 10:21:36 -07:00
test ( file , ` <p><a href=" ` + file + ` " rel="nofollow"><code>65f1bf27bc/example.txt</code></a></p> ` )
test ( fileWithExtra , ` <p><a href=" ` + file + ` " rel="nofollow"><code>65f1bf27bc/example.txt</code></a>:</p> ` )
test ( fileWithHash , ` <p><a href=" ` + fileWithHash + ` " rel="nofollow"><code>65f1bf27bc/example.txt (L2)</code></a></p> ` )
test ( fileWithHasExtra , ` <p><a href=" ` + fileWithHash + ` " rel="nofollow"><code>65f1bf27bc/example.txt (L2)</code></a>:</p> ` )
test ( commitCompare , ` <p><a href=" ` + commitCompare + ` " rel="nofollow"><code>65f1bf27bc...65f1bf27bc</code></a></p> ` )
test ( commitCompareWithHash , ` <p><a href=" ` + commitCompareWithHash + ` " rel="nofollow"><code>65f1bf27bc...65f1bf27bc (L2)</code></a></p> ` )
2019-08-14 02:04:55 -06:00
test ( "commit " + sha , ` <p>commit <a href=" ` + commit + ` " rel="nofollow"><code>65f1bf27bc</code></a></p> ` )
2019-04-07 05:18:16 -06:00
test ( "/home/gitea/" + sha , "<p>/home/gitea/" + sha + "</p>" )
2019-08-14 02:04:55 -06:00
test ( "deadbeef" , ` <p>deadbeef</p> ` )
test ( "d27ace93" , ` <p>d27ace93</p> ` )
2021-01-03 08:11:10 -07:00
test ( sha [ : 14 ] + ".x" , ` <p> ` + sha [ : 14 ] + ` .x</p> ` )
expected14 := ` <a href=" ` + commit [ : len ( commit ) - ( 40 - 14 ) ] + ` " rel="nofollow"><code> ` + sha [ : 10 ] + ` </code></a> `
test ( sha [ : 14 ] + "." , ` <p> ` + expected14 + ` .</p> ` )
test ( sha [ : 14 ] + "," , ` <p> ` + expected14 + ` ,</p> ` )
test ( "[" + sha [ : 14 ] + "]" , ` <p>[ ` + expected14 + ` ]</p> ` )
2017-09-16 11:17:57 -06:00
}
func TestRender_CrossReferences ( t * testing . T ) {
2023-10-23 20:54:59 -06:00
setting . AppURL = markup . TestAppURL
2024-11-13 22:02:11 -07:00
defer testModule . MockVariableValue ( & markup . RenderBehaviorForTesting . DisableInternalAttributes , true ) ( )
2017-09-16 11:17:57 -06:00
test := func ( input , expected string ) {
2023-10-23 20:54:59 -06:00
buffer , err := markup . RenderString ( & markup . RenderContext {
2023-03-05 14:59:05 -07:00
Ctx : git . DefaultContext ,
2022-06-15 21:33:23 -06:00
RelativePath : "a.md" ,
2024-01-15 01:49:24 -07:00
Links : markup . Links {
2024-03-13 04:34:58 -06:00
AbsolutePrefix : true ,
Base : setting . AppSubURL ,
2024-01-15 01:49:24 -07:00
} ,
Metas : localMetas ,
2021-04-19 16:25:08 -06:00
} , input )
assert . NoError ( t , err )
2024-11-13 22:02:11 -07:00
assert . Equal ( t , strings . TrimSpace ( expected ) , strings . TrimSpace ( buffer ) )
2017-09-16 11:17:57 -06:00
}
test (
2024-06-04 06:19:41 -06:00
"test-owner/test-repo#12345" ,
` <p><a href=" ` + util . URLJoin ( markup . TestAppURL , "test-owner" , "test-repo" , "issues" , "12345" ) + ` " class="ref-issue" rel="nofollow">test-owner/test-repo#12345</a></p> ` )
2018-01-27 05:33:32 -07:00
test (
"go-gitea/gitea#12345" ,
2023-10-23 20:54:59 -06:00
` <p><a href=" ` + util . URLJoin ( markup . TestAppURL , "go-gitea" , "gitea" , "issues" , "12345" ) + ` " class="ref-issue" rel="nofollow">go-gitea/gitea#12345</a></p> ` )
2019-04-07 05:18:16 -06:00
test (
"/home/gitea/go-gitea/gitea#12345" ,
` <p>/home/gitea/go-gitea/gitea#12345</p> ` )
2022-02-25 16:26:43 -07:00
test (
2023-10-23 20:54:59 -06:00
util . URLJoin ( markup . TestAppURL , "gogitea" , "gitea" , "issues" , "12345" ) ,
` <p><a href=" ` + util . URLJoin ( markup . TestAppURL , "gogitea" , "gitea" , "issues" , "12345" ) + ` " class="ref-issue" rel="nofollow">gogitea/gitea#12345</a></p> ` )
2022-02-25 16:26:43 -07:00
test (
2023-10-23 20:54:59 -06:00
util . URLJoin ( markup . TestAppURL , "go-gitea" , "gitea" , "issues" , "12345" ) ,
` <p><a href=" ` + util . URLJoin ( markup . TestAppURL , "go-gitea" , "gitea" , "issues" , "12345" ) + ` " class="ref-issue" rel="nofollow">go-gitea/gitea#12345</a></p> ` )
2022-02-25 16:26:43 -07:00
test (
2023-10-23 20:54:59 -06:00
util . URLJoin ( markup . TestAppURL , "gogitea" , "some-repo-name" , "issues" , "12345" ) ,
` <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> ` )
2024-05-03 19:48:16 -06:00
inputURL := "https://host/a/b/commit/0123456789012345678901234567890123456789/foo.txt?a=b#L2-L3"
test (
inputURL ,
` <p><a href=" ` + inputURL + ` " rel="nofollow"><code>0123456789/foo.txt (L2-L3)</code></a></p> ` )
2017-09-16 11:17:57 -06:00
}
2019-02-28 05:31:53 -07:00
func TestRender_links ( t * testing . T ) {
2023-10-23 20:54:59 -06:00
setting . AppURL = markup . TestAppURL
2024-11-13 22:02:11 -07:00
defer testModule . MockVariableValue ( & markup . RenderBehaviorForTesting . DisableInternalAttributes , true ) ( )
2019-02-28 05:31:53 -07:00
test := func ( input , expected string ) {
2023-10-23 20:54:59 -06:00
buffer , err := markup . RenderString ( & markup . RenderContext {
2023-03-05 14:59:05 -07:00
Ctx : git . DefaultContext ,
2022-06-15 21:33:23 -06:00
RelativePath : "a.md" ,
2024-01-15 01:49:24 -07:00
Links : markup . Links {
Base : markup . TestRepoURL ,
} ,
2021-04-19 16:25:08 -06:00
} , input )
assert . NoError ( t , err )
2024-11-13 22:02:11 -07:00
assert . Equal ( t , strings . TrimSpace ( expected ) , strings . TrimSpace ( buffer ) )
2019-02-28 05:31:53 -07:00
}
2024-05-31 07:26:01 -06:00
oldCustomURLSchemes := setting . Markdown . CustomURLSchemes
markup . ResetDefaultSanitizerForTesting ( )
defer func ( ) {
setting . Markdown . CustomURLSchemes = oldCustomURLSchemes
markup . ResetDefaultSanitizerForTesting ( )
markup . CustomLinkURLSchemes ( oldCustomURLSchemes )
} ( )
2019-10-14 19:31:09 -06:00
setting . Markdown . CustomURLSchemes = [ ] string { "ftp" , "magnet" }
2023-10-23 20:54:59 -06:00
markup . CustomLinkURLSchemes ( setting . Markdown . CustomURLSchemes )
2019-10-14 19:31:09 -06:00
2024-05-31 07:26:01 -06:00
// Text that should be turned into URL
2019-02-28 05:31:53 -07:00
test (
"https://www.example.com" ,
` <p><a href="https://www.example.com" rel="nofollow">https://www.example.com</a></p> ` )
test (
"http://www.example.com" ,
` <p><a href="http://www.example.com" rel="nofollow">http://www.example.com</a></p> ` )
test (
"https://example.com" ,
` <p><a href="https://example.com" rel="nofollow">https://example.com</a></p> ` )
test (
"http://example.com" ,
` <p><a href="http://example.com" rel="nofollow">http://example.com</a></p> ` )
test (
"http://foo.com/blah_blah" ,
` <p><a href="http://foo.com/blah_blah" rel="nofollow">http://foo.com/blah_blah</a></p> ` )
test (
"http://foo.com/blah_blah/" ,
` <p><a href="http://foo.com/blah_blah/" rel="nofollow">http://foo.com/blah_blah/</a></p> ` )
test (
"http://www.example.com/wpstyle/?p=364" ,
` <p><a href="http://www.example.com/wpstyle/?p=364" rel="nofollow">http://www.example.com/wpstyle/?p=364</a></p> ` )
test (
"https://www.example.com/foo/?bar=baz&inga=42&quux" ,
2021-07-08 19:30:31 -06:00
` <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> ` )
2019-02-28 05:31:53 -07:00
test (
"http://142.42.1.1/" ,
` <p><a href="http://142.42.1.1/" rel="nofollow">http://142.42.1.1/</a></p> ` )
2019-03-07 13:12:01 -07:00
test (
"https://github.com/go-gitea/gitea/?p=aaa/bbb.html#ccc-ddd" ,
2021-07-08 19:30:31 -06:00
` <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> ` )
2019-03-07 13:12:01 -07:00
test (
"https://en.wikipedia.org/wiki/URL_(disambiguation)" ,
` <p><a href="https://en.wikipedia.org/wiki/URL_(disambiguation)" rel="nofollow">https://en.wikipedia.org/wiki/URL_(disambiguation)</a></p> ` )
test (
"https://foo_bar.example.com/" ,
` <p><a href="https://foo_bar.example.com/" rel="nofollow">https://foo_bar.example.com/</a></p> ` )
2019-03-27 09:37:54 -06:00
test (
"https://stackoverflow.com/questions/2896191/what-is-go-used-fore" ,
` <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> ` )
test (
"https://username:password@gitea.com" ,
` <p><a href="https://username:password@gitea.com" rel="nofollow">https://username:password@gitea.com</a></p> ` )
2019-10-14 19:31:09 -06:00
test (
"ftp://gitea.com/file.txt" ,
` <p><a href="ftp://gitea.com/file.txt" rel="nofollow">ftp://gitea.com/file.txt</a></p> ` )
test (
"magnet:?xt=urn:btih:5dee65101db281ac9c46344cd6b175cdcadabcde&dn=download" ,
2021-07-08 19:30:31 -06:00
` <p><a href="magnet:?xt=urn:btih:5dee65101db281ac9c46344cd6b175cdcadabcde&dn=download" rel="nofollow">magnet:?xt=urn:btih:5dee65101db281ac9c46344cd6b175cdcadabcde&dn=download</a></p> ` )
2024-02-21 03:08:08 -07:00
test (
` [link](https://example.com) ` ,
` <p><a href="https://example.com" rel="nofollow">link</a></p> ` )
test (
` [link](mailto:test@example.com) ` ,
` <p><a href="mailto:test@example.com" rel="nofollow">link</a></p> ` )
test (
` [link](javascript:xss) ` ,
` <p>link</p> ` )
2019-02-28 05:31:53 -07:00
// Test that should *not* be turned into URL
test (
"www.example.com" ,
` <p>www.example.com</p> ` )
test (
"example.com" ,
` <p>example.com</p> ` )
test (
"test.example.com" ,
` <p>test.example.com</p> ` )
test (
"http://" ,
` <p>http://</p> ` )
test (
"https://" ,
` <p>https://</p> ` )
test (
"://" ,
` <p>://</p> ` )
test (
"www" ,
` <p>www</p> ` )
2019-10-14 19:31:09 -06:00
test (
"ftps://gitea.com" ,
` <p>ftps://gitea.com</p> ` )
2019-02-28 05:31:53 -07:00
}
2019-04-07 05:18:16 -06:00
func TestRender_email ( t * testing . T ) {
2023-10-23 20:54:59 -06:00
setting . AppURL = markup . TestAppURL
2024-11-13 22:02:11 -07:00
defer testModule . MockVariableValue ( & markup . RenderBehaviorForTesting . DisableInternalAttributes , true ) ( )
2019-04-07 05:18:16 -06:00
test := func ( input , expected string ) {
2023-10-23 20:54:59 -06:00
res , err := markup . RenderString ( & markup . RenderContext {
2023-03-05 14:59:05 -07:00
Ctx : git . DefaultContext ,
2022-06-15 21:33:23 -06:00
RelativePath : "a.md" ,
2024-01-15 01:49:24 -07:00
Links : markup . Links {
Base : markup . TestRepoURL ,
} ,
2021-04-19 16:25:08 -06:00
} , input )
assert . NoError ( t , err )
2024-11-13 22:02:11 -07:00
assert . Equal ( t , strings . TrimSpace ( expected ) , strings . TrimSpace ( res ) )
2019-04-07 05:18:16 -06:00
}
// Text that should be turned into email link
test (
"info@gitea.com" ,
` <p><a href="mailto:info@gitea.com" rel="nofollow">info@gitea.com</a></p> ` )
test (
"(info@gitea.com)" ,
` <p>(<a href="mailto:info@gitea.com" rel="nofollow">info@gitea.com</a>)</p> ` )
test (
"[info@gitea.com]" ,
` <p>[<a href="mailto:info@gitea.com" rel="nofollow">info@gitea.com</a>]</p> ` )
test (
"info@gitea.com." ,
` <p><a href="mailto:info@gitea.com" rel="nofollow">info@gitea.com</a>.</p> ` )
2019-04-24 19:53:42 -06:00
test (
"firstname+lastname@gitea.com" ,
` <p><a href="mailto:firstname+lastname@gitea.com" rel="nofollow">firstname+lastname@gitea.com</a></p> ` )
2019-04-07 05:18:16 -06:00
test (
"send email to info@gitea.co.uk." ,
` <p>send email to <a href="mailto:info@gitea.co.uk" rel="nofollow">info@gitea.co.uk</a>.</p> ` )
2023-11-10 21:08:19 -07:00
test (
` j . doe @ example . com ,
j . doe @ example . com .
j . doe @ example . com ;
j . doe @ example . com ?
j . doe @ example . com ! ` ,
2024-11-13 22:02:11 -07:00
` < p > < a href = "mailto:j.doe@example.com" rel = "nofollow" > j . doe @ example . com < / a > ,
< a href = "mailto:j.doe@example.com" rel = "nofollow" > j . doe @ example . com < / a > .
< a href = "mailto:j.doe@example.com" rel = "nofollow" > j . doe @ example . com < / a > ;
< a href = "mailto:j.doe@example.com" rel = "nofollow" > j . doe @ example . com < / a > ?
2023-11-10 21:08:19 -07:00
< a href = "mailto:j.doe@example.com" rel = "nofollow" > j . doe @ example . com < / a > ! < / p > ` )
2019-04-07 05:18:16 -06:00
// Test that should *not* be turned into email links
test (
"\"info@gitea.com\"" ,
2020-06-13 00:10:17 -06:00
` <p>"info@gitea.com"</p> ` )
2019-04-07 05:18:16 -06:00
test (
"/home/gitea/mailstore/info@gitea/com" ,
` <p>/home/gitea/mailstore/info@gitea/com</p> ` )
test (
"git@try.gitea.io:go-gitea/gitea.git" ,
` <p>git@try.gitea.io:go-gitea/gitea.git</p> ` )
2019-04-24 19:53:42 -06:00
test (
"gitea@3" ,
` <p>gitea@3</p> ` )
test (
"gitea@gmail.c" ,
` <p>gitea@gmail.c</p> ` )
test (
"email@domain@domain.com" ,
` <p>email@domain@domain.com</p> ` )
test (
"email@domain..com" ,
` <p>email@domain..com</p> ` )
2019-04-07 05:18:16 -06:00
}
2020-04-28 12:05:39 -06:00
func TestRender_emoji ( t * testing . T ) {
2023-10-23 20:54:59 -06:00
setting . AppURL = markup . TestAppURL
setting . StaticURLPrefix = markup . TestAppURL
2020-04-28 12:05:39 -06:00
test := func ( input , expected string ) {
2020-10-11 14:27:20 -06:00
expected = strings . ReplaceAll ( expected , "&" , "&" )
2023-10-23 20:54:59 -06:00
buffer , err := markup . RenderString ( & markup . RenderContext {
2023-03-05 14:59:05 -07:00
Ctx : git . DefaultContext ,
2022-06-15 21:33:23 -06:00
RelativePath : "a.md" ,
2024-01-15 01:49:24 -07:00
Links : markup . Links {
Base : markup . TestRepoURL ,
} ,
2021-04-19 16:25:08 -06:00
} , input )
assert . NoError ( t , err )
2020-04-28 12:05:39 -06:00
assert . Equal ( t , strings . TrimSpace ( expected ) , strings . TrimSpace ( buffer ) )
}
// Make sure we can successfully match every emoji in our dataset with regex
for i := range emoji . GemojiData {
test (
emoji . GemojiData [ i ] . Emoji ,
` <p><span class="emoji" aria-label=" ` + emoji . GemojiData [ i ] . Description + ` "> ` + emoji . GemojiData [ i ] . Emoji + ` </span></p> ` )
}
for i := range emoji . GemojiData {
test (
":" + emoji . GemojiData [ i ] . Aliases [ 0 ] + ":" ,
` <p><span class="emoji" aria-label=" ` + emoji . GemojiData [ i ] . Description + ` "> ` + emoji . GemojiData [ i ] . Emoji + ` </span></p> ` )
}
2022-01-20 10:46:10 -07:00
// Text that should be turned into or recognized as emoji
2020-04-28 12:05:39 -06:00
test (
":gitea:" ,
2021-05-07 09:34:33 -06:00
` <p><span class="emoji" aria-label="gitea"><img alt=":gitea:" src=" ` + setting . StaticURLPrefix + ` /assets/img/emoji/gitea.png"/></span></p> ` )
2021-06-29 08:28:38 -06:00
test (
":custom-emoji:" ,
` <p>:custom-emoji:</p> ` )
setting . UI . CustomEmojisMap [ "custom-emoji" ] = ":custom-emoji:"
test (
":custom-emoji:" ,
` <p><span class="emoji" aria-label="custom-emoji"><img alt=":custom-emoji:" src=" ` + setting . StaticURLPrefix + ` /assets/img/emoji/custom-emoji.png"/></span></p> ` )
test (
"这是字符:1::+1: some🐊 \U0001f44d:custom-emoji: :gitea:" ,
` <p>这是字符:1:<span class="emoji" aria-label="thumbs up">👍</span> some<span class="emoji" aria-label="crocodile">🐊</span> ` +
` <span class="emoji" aria-label="thumbs up">👍</span><span class="emoji" aria-label="custom-emoji"><img alt=":custom-emoji:" src=" ` + setting . StaticURLPrefix + ` /assets/img/emoji/custom-emoji.png"/></span> ` +
` <span class="emoji" aria-label="gitea"><img alt=":gitea:" src=" ` + setting . StaticURLPrefix + ` /assets/img/emoji/gitea.png"/></span></p> ` )
2020-04-28 12:05:39 -06:00
test (
"Some text with 😄 in the middle" ,
` <p>Some text with <span class="emoji" aria-label="grinning face with smiling eyes">😄</span> in the middle</p> ` )
test (
"Some text with :smile: in the middle" ,
` <p>Some text with <span class="emoji" aria-label="grinning face with smiling eyes">😄</span> in the middle</p> ` )
2020-05-29 10:08:36 -06:00
test (
"Some text with 😄😄 2 emoji next to each other" ,
` <p>Some text with <span class="emoji" aria-label="grinning face with smiling eyes">😄</span><span class="emoji" aria-label="grinning face with smiling eyes">😄</span> 2 emoji next to each other</p> ` )
2020-07-25 07:40:04 -06:00
test (
"😎🤪🔐🤑❓" ,
2022-10-05 12:55:36 -06:00
` <p><span class="emoji" aria-label="smiling face with sunglasses">😎</span><span class="emoji" aria-label="zany face">🤪</span><span class="emoji" aria-label="locked with key">🔐</span><span class="emoji" aria-label="money-mouth face">🤑</span><span class="emoji" aria-label="red question mark">❓</span></p> ` )
2020-07-25 07:40:04 -06:00
2020-04-28 12:05:39 -06:00
// should match nothing
test (
"2001:0db8:85a3:0000:0000:8a2e:0370:7334" ,
` <p>2001:0db8:85a3:0000:0000:8a2e:0370:7334</p> ` )
test (
":not exist:" ,
` <p>:not exist:</p> ` )
}
2018-02-27 00:09:18 -07:00
func TestRender_ShortLinks ( t * testing . T ) {
2023-10-23 20:54:59 -06:00
setting . AppURL = markup . TestAppURL
tree := util . URLJoin ( markup . TestRepoURL , "src" , "master" )
2018-02-27 00:09:18 -07:00
test := func ( input , expected , expectedWiki string ) {
2023-10-23 20:54:59 -06:00
buffer , err := markdown . RenderString ( & markup . RenderContext {
2024-01-15 01:49:24 -07:00
Ctx : git . DefaultContext ,
Links : markup . Links {
Base : markup . TestRepoURL ,
BranchPath : "master" ,
} ,
2021-04-19 16:25:08 -06:00
} , input )
assert . NoError ( t , err )
2024-03-01 00:11:51 -07:00
assert . Equal ( t , strings . TrimSpace ( expected ) , strings . TrimSpace ( string ( buffer ) ) )
2023-10-23 20:54:59 -06:00
buffer , err = markdown . RenderString ( & markup . RenderContext {
2024-01-15 01:49:24 -07:00
Ctx : git . DefaultContext ,
Links : markup . Links {
Base : markup . TestRepoURL ,
} ,
2024-11-16 01:41:44 -07:00
Metas : localWikiMetas ,
2021-04-19 16:25:08 -06:00
} , input )
assert . NoError ( t , err )
2024-03-01 00:11:51 -07:00
assert . Equal ( t , strings . TrimSpace ( expectedWiki ) , strings . TrimSpace ( string ( buffer ) ) )
2018-02-27 00:09:18 -07:00
}
2024-01-15 01:49:24 -07:00
mediatree := util . URLJoin ( markup . TestRepoURL , "media" , "master" )
2018-02-27 00:09:18 -07:00
url := util . URLJoin ( tree , "Link" )
2018-03-05 11:39:12 -07:00
otherURL := util . URLJoin ( tree , "Other-Link" )
2018-06-15 06:42:49 -06:00
encodedURL := util . URLJoin ( tree , "Link%3F" )
2024-01-15 01:49:24 -07:00
imgurl := util . URLJoin ( mediatree , "Link.jpg" )
otherImgurl := util . URLJoin ( mediatree , "Link+Other.jpg" )
encodedImgurl := util . URLJoin ( mediatree , "Link+%23.jpg" )
notencodedImgurl := util . URLJoin ( mediatree , "some" , "path" , "Link+#.jpg" )
2023-10-23 20:54:59 -06:00
urlWiki := util . URLJoin ( markup . TestRepoURL , "wiki" , "Link" )
otherURLWiki := util . URLJoin ( markup . TestRepoURL , "wiki" , "Other-Link" )
encodedURLWiki := util . URLJoin ( markup . TestRepoURL , "wiki" , "Link%3F" )
imgurlWiki := util . URLJoin ( markup . TestRepoURL , "wiki" , "raw" , "Link.jpg" )
otherImgurlWiki := util . URLJoin ( markup . TestRepoURL , "wiki" , "raw" , "Link+Other.jpg" )
encodedImgurlWiki := util . URLJoin ( markup . TestRepoURL , "wiki" , "raw" , "Link+%23.jpg" )
notencodedImgurlWiki := util . URLJoin ( markup . TestRepoURL , "wiki" , "raw" , "some" , "path" , "Link+#.jpg" )
2024-04-10 11:49:57 -06:00
renderableFileURL := util . URLJoin ( tree , "markdown_file.md" )
renderableFileURLWiki := util . URLJoin ( markup . TestRepoURL , "wiki" , "markdown_file.md" )
unrenderableFileURL := util . URLJoin ( tree , "file.zip" )
2024-08-16 06:40:51 -06:00
unrenderableFileURLWiki := util . URLJoin ( markup . TestRepoURL , "wiki" , "file.zip" )
2018-02-27 00:09:18 -07:00
favicon := "http://google.com/favicon.ico"
test (
"[[Link]]" ,
` <p><a href=" ` + url + ` " rel="nofollow">Link</a></p> ` ,
` <p><a href=" ` + urlWiki + ` " rel="nofollow">Link</a></p> ` )
2024-06-17 21:09:20 -06:00
test (
"[[Link.-]]" ,
` <p><a href="http://localhost:3000/test-owner/test-repo/src/master/Link.-" rel="nofollow">Link.-</a></p> ` ,
` <p><a href="http://localhost:3000/test-owner/test-repo/wiki/Link.-" rel="nofollow">Link.-</a></p> ` )
2018-02-27 00:09:18 -07:00
test (
"[[Link.jpg]]" ,
` <p><a href=" ` + imgurl + ` " rel="nofollow"><img src=" ` + imgurl + ` " title="Link.jpg" alt="Link.jpg"/></a></p> ` ,
` <p><a href=" ` + imgurlWiki + ` " rel="nofollow"><img src=" ` + imgurlWiki + ` " title="Link.jpg" alt="Link.jpg"/></a></p> ` )
test (
"[[" + favicon + "]]" ,
2020-02-28 13:05:12 -07:00
` <p><a href=" ` + favicon + ` " rel="nofollow"><img src=" ` + favicon + ` " title="favicon.ico" alt=" ` + favicon + ` "/></a></p> ` ,
` <p><a href=" ` + favicon + ` " rel="nofollow"><img src=" ` + favicon + ` " title="favicon.ico" alt=" ` + favicon + ` "/></a></p> ` )
2018-02-27 00:09:18 -07:00
test (
"[[Name|Link]]" ,
` <p><a href=" ` + url + ` " rel="nofollow">Name</a></p> ` ,
` <p><a href=" ` + urlWiki + ` " rel="nofollow">Name</a></p> ` )
test (
"[[Name|Link.jpg]]" ,
` <p><a href=" ` + imgurl + ` " rel="nofollow"><img src=" ` + imgurl + ` " title="Name" alt="Name"/></a></p> ` ,
` <p><a href=" ` + imgurlWiki + ` " rel="nofollow"><img src=" ` + imgurlWiki + ` " title="Name" alt="Name"/></a></p> ` )
test (
"[[Name|Link.jpg|alt=AltName]]" ,
` <p><a href=" ` + imgurl + ` " rel="nofollow"><img src=" ` + imgurl + ` " title="AltName" alt="AltName"/></a></p> ` ,
` <p><a href=" ` + imgurlWiki + ` " rel="nofollow"><img src=" ` + imgurlWiki + ` " title="AltName" alt="AltName"/></a></p> ` )
test (
"[[Name|Link.jpg|title=Title]]" ,
` <p><a href=" ` + imgurl + ` " rel="nofollow"><img src=" ` + imgurl + ` " title="Title" alt="Title"/></a></p> ` ,
` <p><a href=" ` + imgurlWiki + ` " rel="nofollow"><img src=" ` + imgurlWiki + ` " title="Title" alt="Title"/></a></p> ` )
test (
"[[Name|Link.jpg|alt=AltName|title=Title]]" ,
` <p><a href=" ` + imgurl + ` " rel="nofollow"><img src=" ` + imgurl + ` " title="Title" alt="AltName"/></a></p> ` ,
` <p><a href=" ` + imgurlWiki + ` " rel="nofollow"><img src=" ` + imgurlWiki + ` " title="Title" alt="AltName"/></a></p> ` )
test (
"[[Name|Link.jpg|alt=\"AltName\"|title='Title']]" ,
` <p><a href=" ` + imgurl + ` " rel="nofollow"><img src=" ` + imgurl + ` " title="Title" alt="AltName"/></a></p> ` ,
` <p><a href=" ` + imgurlWiki + ` " rel="nofollow"><img src=" ` + imgurlWiki + ` " title="Title" alt="AltName"/></a></p> ` )
test (
2018-03-05 11:39:12 -07:00
"[[Name|Link Other.jpg|alt=\"AltName\"|title='Title']]" ,
` <p><a href=" ` + otherImgurl + ` " rel="nofollow"><img src=" ` + otherImgurl + ` " title="Title" alt="AltName"/></a></p> ` ,
` <p><a href=" ` + otherImgurlWiki + ` " rel="nofollow"><img src=" ` + otherImgurlWiki + ` " title="Title" alt="AltName"/></a></p> ` )
test (
"[[Link]] [[Other Link]]" ,
` <p><a href=" ` + url + ` " rel="nofollow">Link</a> <a href=" ` + otherURL + ` " rel="nofollow">Other Link</a></p> ` ,
` <p><a href=" ` + urlWiki + ` " rel="nofollow">Link</a> <a href=" ` + otherURLWiki + ` " rel="nofollow">Other Link</a></p> ` )
2018-06-15 06:42:49 -06:00
test (
"[[Link?]]" ,
` <p><a href=" ` + encodedURL + ` " rel="nofollow">Link?</a></p> ` ,
` <p><a href=" ` + encodedURLWiki + ` " rel="nofollow">Link?</a></p> ` )
test (
"[[Link]] [[Other Link]] [[Link?]]" ,
` <p><a href=" ` + url + ` " rel="nofollow">Link</a> <a href=" ` + otherURL + ` " rel="nofollow">Other Link</a> <a href=" ` + encodedURL + ` " rel="nofollow">Link?</a></p> ` ,
` <p><a href=" ` + urlWiki + ` " rel="nofollow">Link</a> <a href=" ` + otherURLWiki + ` " rel="nofollow">Other Link</a> <a href=" ` + encodedURLWiki + ` " rel="nofollow">Link?</a></p> ` )
2024-04-10 11:49:57 -06:00
test (
"[[markdown_file.md]]" ,
` <p><a href=" ` + renderableFileURL + ` " rel="nofollow">markdown_file.md</a></p> ` ,
` <p><a href=" ` + renderableFileURLWiki + ` " rel="nofollow">markdown_file.md</a></p> ` )
test (
"[[file.zip]]" ,
` <p><a href=" ` + unrenderableFileURL + ` " rel="nofollow">file.zip</a></p> ` ,
` <p><a href=" ` + unrenderableFileURLWiki + ` " rel="nofollow">file.zip</a></p> ` )
2018-06-15 06:42:49 -06:00
test (
"[[Link #.jpg]]" ,
2020-02-28 13:05:12 -07:00
` <p><a href=" ` + encodedImgurl + ` " rel="nofollow"><img src=" ` + encodedImgurl + ` " title="Link #.jpg" alt="Link #.jpg"/></a></p> ` ,
` <p><a href=" ` + encodedImgurlWiki + ` " rel="nofollow"><img src=" ` + encodedImgurlWiki + ` " title="Link #.jpg" alt="Link #.jpg"/></a></p> ` )
2018-06-15 06:42:49 -06:00
test (
"[[Name|Link #.jpg|alt=\"AltName\"|title='Title']]" ,
` <p><a href=" ` + encodedImgurl + ` " rel="nofollow"><img src=" ` + encodedImgurl + ` " title="Title" alt="AltName"/></a></p> ` ,
` <p><a href=" ` + encodedImgurlWiki + ` " rel="nofollow"><img src=" ` + encodedImgurlWiki + ` " title="Title" alt="AltName"/></a></p> ` )
test (
"[[some/path/Link #.jpg]]" ,
2020-02-28 13:05:12 -07:00
` <p><a href=" ` + notencodedImgurl + ` " rel="nofollow"><img src=" ` + notencodedImgurl + ` " title="Link #.jpg" alt="some/path/Link #.jpg"/></a></p> ` ,
` <p><a href=" ` + notencodedImgurlWiki + ` " rel="nofollow"><img src=" ` + notencodedImgurlWiki + ` " title="Link #.jpg" alt="some/path/Link #.jpg"/></a></p> ` )
2019-03-07 12:13:44 -07:00
test (
"<p><a href=\"https://example.org\">[[foobar]]</a></p>" ,
2019-12-30 18:53:28 -07:00
` <p><a href="https://example.org" rel="nofollow">[[foobar]]</a></p> ` ,
` <p><a href="https://example.org" rel="nofollow">[[foobar]]</a></p> ` )
2018-02-27 00:09:18 -07:00
}
2021-01-20 08:10:50 -07:00
2024-06-21 12:23:54 -06:00
func TestRender_RelativeMedias ( t * testing . T ) {
2024-06-04 06:19:41 -06:00
render := func ( input string , isWiki bool , links markup . Links ) string {
2023-10-23 20:54:59 -06:00
buffer , err := markdown . RenderString ( & markup . RenderContext {
2024-11-16 01:41:44 -07:00
Ctx : git . DefaultContext ,
Links : links ,
Metas : util . Iif ( isWiki , localWikiMetas , localMetas ) ,
2021-07-04 03:26:04 -06:00
} , input )
assert . NoError ( t , err )
2024-06-04 06:19:41 -06:00
return strings . TrimSpace ( string ( buffer ) )
2021-07-04 03:26:04 -06:00
}
2024-06-04 06:19:41 -06:00
out := render ( ` <img src="LINK"> ` , false , markup . Links { Base : "/test-owner/test-repo" } )
assert . Equal ( t , ` <a href="/test-owner/test-repo/LINK" target="_blank" rel="nofollow noopener"><img src="/test-owner/test-repo/LINK"/></a> ` , out )
2021-07-04 03:26:04 -06:00
2024-06-04 06:19:41 -06:00
out = render ( ` <img src="LINK"> ` , true , markup . Links { Base : "/test-owner/test-repo" } )
assert . Equal ( t , ` <a href="/test-owner/test-repo/wiki/raw/LINK" target="_blank" rel="nofollow noopener"><img src="/test-owner/test-repo/wiki/raw/LINK"/></a> ` , out )
2021-07-04 03:26:04 -06:00
2024-06-04 06:19:41 -06:00
out = render ( ` <img src="LINK"> ` , false , markup . Links { Base : "/test-owner/test-repo" , BranchPath : "test-branch" } )
assert . Equal ( t , ` <a href="/test-owner/test-repo/media/test-branch/LINK" target="_blank" rel="nofollow noopener"><img src="/test-owner/test-repo/media/test-branch/LINK"/></a> ` , out )
out = render ( ` <img src="LINK"> ` , true , markup . Links { Base : "/test-owner/test-repo" , BranchPath : "test-branch" } )
assert . Equal ( t , ` <a href="/test-owner/test-repo/wiki/raw/LINK" target="_blank" rel="nofollow noopener"><img src="/test-owner/test-repo/wiki/raw/LINK"/></a> ` , out )
out = render ( ` <img src="/LINK"> ` , true , markup . Links { Base : "/test-owner/test-repo" , BranchPath : "test-branch" } )
assert . Equal ( t , ` <img src="/LINK"/> ` , out )
2024-06-21 12:23:54 -06:00
out = render ( ` <video src="LINK"> ` , false , markup . Links { Base : "/test-owner/test-repo" } )
assert . Equal ( t , ` <video src="/test-owner/test-repo/LINK"></video> ` , out )
out = render ( ` <video src="LINK"> ` , true , markup . Links { Base : "/test-owner/test-repo" } )
assert . Equal ( t , ` <video src="/test-owner/test-repo/wiki/raw/LINK"></video> ` , out )
out = render ( ` <video src="/LINK"> ` , false , markup . Links { Base : "/test-owner/test-repo" } )
assert . Equal ( t , ` <video src="/LINK"></video> ` , out )
2021-07-04 03:26:04 -06:00
}
2021-01-20 08:10:50 -07:00
func Test_ParseClusterFuzz ( t * testing . T ) {
2023-10-23 20:54:59 -06:00
setting . AppURL = markup . TestAppURL
2021-01-20 08:10:50 -07:00
2022-01-20 10:46:10 -07:00
localMetas := map [ string ] string {
2021-01-20 08:10:50 -07:00
"user" : "go-gitea" ,
"repo" : "gitea" ,
}
data := "<A><maTH><tr><MN><bodY ÿ><temPlate></template><tH><tr></A><tH><d<bodY "
2021-04-19 16:25:08 -06:00
var res strings . Builder
2023-10-23 20:54:59 -06:00
err := markup . PostProcess ( & markup . RenderContext {
2024-01-15 01:49:24 -07:00
Ctx : git . DefaultContext ,
Links : markup . Links {
Base : "https://example.com" ,
} ,
Metas : localMetas ,
2021-04-19 16:25:08 -06:00
} , strings . NewReader ( data ) , & res )
2021-01-20 08:10:50 -07:00
assert . NoError ( t , err )
2021-04-19 16:25:08 -06:00
assert . NotContains ( t , res . String ( ) , "<html" )
2021-01-20 08:10:50 -07:00
data = "<!DOCTYPE html>\n<A><maTH><tr><MN><bodY ÿ><temPlate></template><tH><tr></A><tH><d<bodY "
2021-04-19 16:25:08 -06:00
res . Reset ( )
2023-10-23 20:54:59 -06:00
err = markup . PostProcess ( & markup . RenderContext {
2024-01-15 01:49:24 -07:00
Ctx : git . DefaultContext ,
Links : markup . Links {
Base : "https://example.com" ,
} ,
Metas : localMetas ,
2021-04-19 16:25:08 -06:00
} , strings . NewReader ( data ) , & res )
2021-01-20 08:10:50 -07:00
assert . NoError ( t , err )
2021-04-19 16:25:08 -06:00
assert . NotContains ( t , res . String ( ) , "<html" )
2021-01-20 08:10:50 -07:00
}
2021-06-07 10:55:26 -06:00
2023-08-07 07:11:25 -06:00
func TestPostProcess_RenderDocument ( t * testing . T ) {
2023-10-23 20:54:59 -06:00
setting . AppURL = markup . TestAppURL
2024-01-15 01:49:24 -07:00
setting . StaticURLPrefix = markup . TestAppURL // can't run standalone
2024-11-13 22:02:11 -07:00
defer testModule . MockVariableValue ( & markup . RenderBehaviorForTesting . DisableInternalAttributes , true ) ( )
2023-08-07 07:11:25 -06:00
test := func ( input , expected string ) {
var res strings . Builder
2023-10-23 20:54:59 -06:00
err := markup . PostProcess ( & markup . RenderContext {
2024-01-15 01:49:24 -07:00
Ctx : git . DefaultContext ,
Links : markup . Links {
2024-03-13 04:34:58 -06:00
AbsolutePrefix : true ,
Base : "https://example.com" ,
2024-01-15 01:49:24 -07:00
} ,
2024-11-13 22:02:11 -07:00
Metas : map [ string ] string { "user" : "go-gitea" , "repo" : "gitea" } ,
2023-08-07 07:11:25 -06:00
} , strings . NewReader ( input ) , & res )
assert . NoError ( t , err )
2024-11-13 22:02:11 -07:00
assert . Equal ( t , strings . TrimSpace ( expected ) , strings . TrimSpace ( res . String ( ) ) )
2023-08-07 07:11:25 -06:00
}
2024-01-02 23:01:12 -07:00
// Issue index shouldn't be post processing in a document.
2023-08-07 07:11:25 -06:00
test (
"#1" ,
"#1" )
2024-01-02 23:01:12 -07:00
// But cross-referenced issue index should work.
test (
"go-gitea/gitea#12345" ,
` <a href=" ` + util . URLJoin ( markup . TestAppURL , "go-gitea" , "gitea" , "issues" , "12345" ) + ` " class="ref-issue">go-gitea/gitea#12345</a> ` )
2023-08-07 07:11:25 -06:00
// Test that other post processing still works.
test (
":gitea:" ,
` <span class="emoji" aria-label="gitea"><img alt=":gitea:" src=" ` + setting . StaticURLPrefix + ` /assets/img/emoji/gitea.png"/></span> ` )
test (
"Some text with 😄 in the middle" ,
` Some text with <span class="emoji" aria-label="grinning face with smiling eyes">😄</span> in the middle ` )
test ( "http://localhost:3000/person/repo/issues/4#issuecomment-1234" ,
` <a href="http://localhost:3000/person/repo/issues/4#issuecomment-1234" class="ref-issue">person/repo#4 (comment)</a> ` )
}
2021-06-07 10:55:26 -06:00
func TestIssue16020 ( t * testing . T ) {
2023-10-23 20:54:59 -06:00
setting . AppURL = markup . TestAppURL
2021-06-07 10:55:26 -06:00
2022-01-20 10:46:10 -07:00
localMetas := map [ string ] string {
2021-06-07 10:55:26 -06:00
"user" : "go-gitea" ,
"repo" : "gitea" ,
}
data := ` <img src="data:image/png;base64,i//V"/> `
var res strings . Builder
2023-10-23 20:54:59 -06:00
err := markup . PostProcess ( & markup . RenderContext {
2024-01-15 01:49:24 -07:00
Ctx : git . DefaultContext ,
Metas : localMetas ,
2021-06-07 10:55:26 -06:00
} , strings . NewReader ( data ) , & res )
assert . NoError ( t , err )
assert . Equal ( t , data , res . String ( ) )
}
2021-06-17 04:35:05 -06:00
func BenchmarkEmojiPostprocess ( b * testing . B ) {
data := "🥰 "
for len ( data ) < 1 << 16 {
data += data
}
b . ResetTimer ( )
for i := 0 ; i < b . N ; i ++ {
var res strings . Builder
2023-10-23 20:54:59 -06:00
err := markup . PostProcess ( & markup . RenderContext {
2024-01-15 01:49:24 -07:00
Ctx : git . DefaultContext ,
Metas : localMetas ,
2021-06-17 04:35:05 -06:00
} , strings . NewReader ( data ) , & res )
assert . NoError ( b , err )
}
}
2021-08-12 16:22:05 -06:00
func TestFuzz ( t * testing . T ) {
s := "t/l/issues/8#/../../a"
2023-10-23 20:54:59 -06:00
renderContext := markup . RenderContext {
2024-01-15 01:49:24 -07:00
Ctx : git . DefaultContext ,
Links : markup . Links {
Base : "https://example.com/go-gitea/gitea" ,
} ,
2021-08-12 16:22:05 -06:00
Metas : map [ string ] string {
"user" : "go-gitea" ,
"repo" : "gitea" ,
} ,
}
2023-10-23 20:54:59 -06:00
err := markup . PostProcess ( & renderContext , strings . NewReader ( s ) , io . Discard )
2021-08-12 16:22:05 -06:00
assert . NoError ( t , err )
}
2022-01-30 16:48:47 -07:00
func TestIssue18471 ( t * testing . T ) {
data := ` http://domain/org/repo/compare/783b039...da951ce `
var res strings . Builder
2023-10-23 20:54:59 -06:00
err := markup . PostProcess ( & markup . RenderContext {
2024-01-15 01:49:24 -07:00
Ctx : git . DefaultContext ,
Metas : localMetas ,
2022-01-30 16:48:47 -07:00
} , strings . NewReader ( data ) , & res )
assert . NoError ( t , err )
2024-05-03 19:48:16 -06:00
assert . Equal ( t , ` <a href="http://domain/org/repo/compare/783b039...da951ce" class="compare"><code class="nohighlight">783b039...da951ce</code></a> ` , res . String ( ) )
2022-01-30 16:48:47 -07:00
}
2024-02-21 03:08:08 -07:00
func TestIsFullURL ( t * testing . T ) {
assert . True ( t , markup . IsFullURLString ( "https://example.com" ) )
assert . True ( t , markup . IsFullURLString ( "mailto:test@example.com" ) )
2024-06-04 06:19:41 -06:00
assert . True ( t , markup . IsFullURLString ( "data:image/11111" ) )
2024-02-21 03:08:08 -07:00
assert . False ( t , markup . IsFullURLString ( "/foo:bar" ) )
}