2017-04-25 15:24:51 +08:00
// Copyright 2017 The Gitea Authors. All rights reserved.
2022-11-28 02:20:29 +08:00
// SPDX-License-Identifier: MIT
2017-04-25 15:24:51 +08:00
2022-09-03 03:18:23 +08:00
package integration
2017-04-25 15:24:51 +08:00
import (
2017-07-15 22:21:51 +08:00
"fmt"
2017-04-25 15:24:51 +08:00
"net/http"
2020-02-02 03:11:32 +08:00
"path"
2018-05-01 15:04:36 +08:00
"strings"
2017-04-25 15:24:51 +08:00
"testing"
2020-02-02 03:11:32 +08:00
"time"
2017-07-15 22:21:51 +08:00
"code.gitea.io/gitea/modules/setting"
2022-09-03 03:18:23 +08:00
"code.gitea.io/gitea/tests"
2017-07-15 22:21:51 +08:00
2018-05-01 15:04:36 +08:00
"github.com/PuerkitoBio/goquery"
2017-07-15 22:21:51 +08:00
"github.com/stretchr/testify/assert"
2017-04-25 15:24:51 +08:00
)
func TestViewRepo ( t * testing . T ) {
2022-09-03 03:18:23 +08:00
defer tests . PrepareTestEnv ( t ) ( )
2017-04-25 15:24:51 +08:00
2022-10-13 16:31:10 +08:00
session := loginUser ( t , "user2" )
2017-06-10 08:41:36 +08:00
req := NewRequest ( t , "GET" , "/user2/repo1" )
2022-10-13 16:31:10 +08:00
resp := session . MakeRequest ( t , req , http . StatusOK )
htmlDoc := NewHTMLParser ( t , resp . Body )
noDescription := htmlDoc . doc . Find ( "#repo-desc" ) . Children ( )
repoTopics := htmlDoc . doc . Find ( "#repo-topics" ) . Children ( )
repoSummary := htmlDoc . doc . Find ( ".repository-summary" ) . Children ( )
assert . True ( t , noDescription . HasClass ( "no-description" ) )
assert . True ( t , repoTopics . HasClass ( "repo-topic" ) )
assert . True ( t , repoSummary . HasClass ( "repository-menu" ) )
2017-06-15 10:50:12 +08:00
req = NewRequest ( t , "GET" , "/user3/repo3" )
2017-07-08 03:36:47 +08:00
MakeRequest ( t , req , http . StatusNotFound )
2017-06-15 10:50:12 +08:00
2022-10-13 16:31:10 +08:00
session = loginUser ( t , "user1" )
2017-07-08 03:36:47 +08:00
session . MakeRequest ( t , req , http . StatusNotFound )
2017-06-15 10:50:12 +08:00
}
2020-02-02 03:11:32 +08:00
func testViewRepo ( t * testing . T ) {
2022-09-03 03:18:23 +08:00
defer tests . PrepareTestEnv ( t ) ( )
2017-06-15 10:50:12 +08:00
req := NewRequest ( t , "GET" , "/user3/repo3" )
2017-06-17 12:49:45 +08:00
session := loginUser ( t , "user2" )
2020-02-02 03:11:32 +08:00
resp := session . MakeRequest ( t , req , http . StatusOK )
htmlDoc := NewHTMLParser ( t , resp . Body )
files := htmlDoc . doc . Find ( "#repo-files-table > TBODY > TR" )
type file struct {
fileName string
commitID string
commitMsg string
commitTime string
}
var items [ ] file
files . Each ( func ( i int , s * goquery . Selection ) {
tds := s . Find ( "td" )
var f file
tds . Each ( func ( i int , s * goquery . Selection ) {
if i == 0 {
f . fileName = strings . TrimSpace ( s . Text ( ) )
} else if i == 1 {
a := s . Find ( "a" )
f . commitMsg = strings . TrimSpace ( a . Text ( ) )
l , _ := a . Attr ( "href" )
f . commitID = path . Base ( l )
}
} )
2023-03-24 18:35:38 +08:00
f . commitTime , _ = s . Find ( "span.time-since" ) . Attr ( "data-tooltip-content" )
2020-02-02 03:11:32 +08:00
items = append ( items , f )
} )
2020-02-25 11:05:00 +08:00
commitT := time . Date ( 2017 , time . June , 14 , 13 , 54 , 21 , 0 , time . UTC ) . In ( time . Local ) . Format ( time . RFC1123 )
2020-02-02 03:11:32 +08:00
assert . EqualValues ( t , [ ] file {
{
fileName : "doc" ,
commitID : "2a47ca4b614a9f5a43abbd5ad851a54a616ffee6" ,
commitMsg : "init project" ,
2020-02-25 11:05:00 +08:00
commitTime : commitT ,
2020-02-02 03:11:32 +08:00
} ,
{
fileName : "README.md" ,
commitID : "2a47ca4b614a9f5a43abbd5ad851a54a616ffee6" ,
commitMsg : "init project" ,
2020-02-25 11:05:00 +08:00
commitTime : commitT ,
2020-02-02 03:11:32 +08:00
} ,
} , items )
}
func TestViewRepo2 ( t * testing . T ) {
// no last commit cache
testViewRepo ( t )
// enable last commit cache for all repositories
oldCommitsCount := setting . CacheService . LastCommit . CommitsCount
setting . CacheService . LastCommit . CommitsCount = 0
// first view will not hit the cache
testViewRepo ( t )
// second view will hit the cache
testViewRepo ( t )
setting . CacheService . LastCommit . CommitsCount = oldCommitsCount
2017-06-15 10:50:12 +08:00
}
func TestViewRepo3 ( t * testing . T ) {
2022-09-03 03:18:23 +08:00
defer tests . PrepareTestEnv ( t ) ( )
2017-06-15 10:50:12 +08:00
req := NewRequest ( t , "GET" , "/user3/repo3" )
2017-07-17 10:04:43 +08:00
session := loginUser ( t , "user4" )
2017-07-08 03:36:47 +08:00
session . MakeRequest ( t , req , http . StatusOK )
2017-04-25 15:24:51 +08:00
}
2017-07-15 22:21:51 +08:00
func TestViewRepo1CloneLinkAnonymous ( t * testing . T ) {
2022-09-03 03:18:23 +08:00
defer tests . PrepareTestEnv ( t ) ( )
2017-07-15 22:21:51 +08:00
req := NewRequest ( t , "GET" , "/user2/repo1" )
resp := MakeRequest ( t , req , http . StatusOK )
htmlDoc := NewHTMLParser ( t , resp . Body )
link , exists := htmlDoc . doc . Find ( "#repo-clone-https" ) . Attr ( "data-link" )
assert . True ( t , exists , "The template has changed" )
assert . Equal ( t , setting . AppURL + "user2/repo1.git" , link )
_ , exists = htmlDoc . doc . Find ( "#repo-clone-ssh" ) . Attr ( "data-link" )
assert . False ( t , exists )
}
func TestViewRepo1CloneLinkAuthorized ( t * testing . T ) {
2022-09-03 03:18:23 +08:00
defer tests . PrepareTestEnv ( t ) ( )
2017-07-15 22:21:51 +08:00
session := loginUser ( t , "user2" )
req := NewRequest ( t , "GET" , "/user2/repo1" )
resp := session . MakeRequest ( t , req , http . StatusOK )
htmlDoc := NewHTMLParser ( t , resp . Body )
link , exists := htmlDoc . doc . Find ( "#repo-clone-https" ) . Attr ( "data-link" )
assert . True ( t , exists , "The template has changed" )
assert . Equal ( t , setting . AppURL + "user2/repo1.git" , link )
link , exists = htmlDoc . doc . Find ( "#repo-clone-ssh" ) . Attr ( "data-link" )
assert . True ( t , exists , "The template has changed" )
2022-02-08 05:56:45 +08:00
sshURL := fmt . Sprintf ( "ssh://%s@%s:%d/user2/repo1.git" , setting . SSH . User , setting . SSH . Domain , setting . SSH . Port )
2017-07-15 22:21:51 +08:00
assert . Equal ( t , sshURL , link )
}
2018-05-01 15:04:36 +08:00
func TestViewRepoWithSymlinks ( t * testing . T ) {
2022-09-03 03:18:23 +08:00
defer tests . PrepareTestEnv ( t ) ( )
2018-05-01 15:04:36 +08:00
session := loginUser ( t , "user2" )
req := NewRequest ( t , "GET" , "/user2/repo20.git" )
resp := session . MakeRequest ( t , req , http . StatusOK )
htmlDoc := NewHTMLParser ( t , resp . Body )
2020-08-26 23:52:44 +08:00
files := htmlDoc . doc . Find ( "#repo-files-table > TBODY > TR > TD.name > SPAN.truncate" )
2018-05-01 15:04:36 +08:00
items := files . Map ( func ( i int , s * goquery . Selection ) string {
2020-02-12 01:02:41 +08:00
cls , _ := s . Find ( "SVG" ) . Attr ( "class" )
2018-05-01 15:04:36 +08:00
file := strings . Trim ( s . Find ( "A" ) . Text ( ) , " \t\n" )
return fmt . Sprintf ( "%s: %s" , file , cls )
} )
2021-06-07 13:27:09 +08:00
assert . Len ( t , items , 5 )
2022-04-01 08:15:46 +08:00
assert . Equal ( t , "a: svg octicon-file-directory-fill" , items [ 0 ] )
2021-06-07 13:27:09 +08:00
assert . Equal ( t , "link_b: svg octicon-file-submodule" , items [ 1 ] )
assert . Equal ( t , "link_d: svg octicon-file-symlink-file" , items [ 2 ] )
assert . Equal ( t , "link_hi: svg octicon-file-symlink-file" , items [ 3 ] )
assert . Equal ( t , "link_link: svg octicon-file-symlink-file" , items [ 4 ] )
2018-05-01 15:04:36 +08:00
}
2019-01-28 05:13:15 +08:00
// TestViewAsRepoAdmin tests PR #2167
func TestViewAsRepoAdmin ( t * testing . T ) {
for user , expectedNoDescription := range map [ string ] bool {
"user2" : true ,
2019-02-19 15:19:28 +08:00
"user4" : false ,
2019-01-28 05:13:15 +08:00
} {
2022-09-03 03:18:23 +08:00
defer tests . PrepareTestEnv ( t ) ( )
2019-01-28 05:13:15 +08:00
session := loginUser ( t , user )
req := NewRequest ( t , "GET" , "/user2/repo1.git" )
resp := session . MakeRequest ( t , req , http . StatusOK )
htmlDoc := NewHTMLParser ( t , resp . Body )
noDescription := htmlDoc . doc . Find ( "#repo-desc" ) . Children ( )
2022-10-13 16:31:10 +08:00
repoTopics := htmlDoc . doc . Find ( "#repo-topics" ) . Children ( )
repoSummary := htmlDoc . doc . Find ( ".repository-summary" ) . Children ( )
2019-01-28 05:13:15 +08:00
assert . Equal ( t , expectedNoDescription , noDescription . HasClass ( "no-description" ) )
2022-10-13 16:31:10 +08:00
assert . True ( t , repoTopics . HasClass ( "repo-topic" ) )
assert . True ( t , repoSummary . HasClass ( "repository-menu" ) )
2019-01-28 05:13:15 +08:00
}
}
2022-10-13 16:31:10 +08:00
// TestViewFileInRepo repo description, topics and summary should not be displayed when viewing a file
func TestViewFileInRepo ( t * testing . T ) {
defer tests . PrepareTestEnv ( t ) ( )
session := loginUser ( t , "user2" )
req := NewRequest ( t , "GET" , "/user2/repo1/src/branch/master/README.md" )
resp := session . MakeRequest ( t , req , http . StatusOK )
htmlDoc := NewHTMLParser ( t , resp . Body )
description := htmlDoc . doc . Find ( "#repo-desc" )
repoTopics := htmlDoc . doc . Find ( "#repo-topics" )
repoSummary := htmlDoc . doc . Find ( ".repository-summary" )
assert . EqualValues ( t , 0 , description . Length ( ) )
assert . EqualValues ( t , 0 , repoTopics . Length ( ) )
assert . EqualValues ( t , 0 , repoSummary . Length ( ) )
}
// TestBlameFileInRepo repo description, topics and summary should not be displayed when running blame on a file
func TestBlameFileInRepo ( t * testing . T ) {
defer tests . PrepareTestEnv ( t ) ( )
session := loginUser ( t , "user2" )
req := NewRequest ( t , "GET" , "/user2/repo1/blame/branch/master/README.md" )
resp := session . MakeRequest ( t , req , http . StatusOK )
htmlDoc := NewHTMLParser ( t , resp . Body )
description := htmlDoc . doc . Find ( "#repo-desc" )
repoTopics := htmlDoc . doc . Find ( "#repo-topics" )
repoSummary := htmlDoc . doc . Find ( ".repository-summary" )
assert . EqualValues ( t , 0 , description . Length ( ) )
assert . EqualValues ( t , 0 , repoTopics . Length ( ) )
assert . EqualValues ( t , 0 , repoSummary . Length ( ) )
}
// TestViewRepoDirectory repo description, topics and summary should not be displayed when within a directory
func TestViewRepoDirectory ( t * testing . T ) {
defer tests . PrepareTestEnv ( t ) ( )
session := loginUser ( t , "user2" )
req := NewRequest ( t , "GET" , "/user2/repo20/src/branch/master/a" )
resp := session . MakeRequest ( t , req , http . StatusOK )
htmlDoc := NewHTMLParser ( t , resp . Body )
description := htmlDoc . doc . Find ( "#repo-desc" )
repoTopics := htmlDoc . doc . Find ( "#repo-topics" )
repoSummary := htmlDoc . doc . Find ( ".repository-summary" )
repoFilesTable := htmlDoc . doc . Find ( "#repo-files-table" )
assert . NotZero ( t , len ( repoFilesTable . Nodes ) )
assert . Zero ( t , description . Length ( ) )
assert . Zero ( t , repoTopics . Length ( ) )
assert . Zero ( t , repoSummary . Length ( ) )
}
2023-03-03 18:01:33 +08:00
2023-03-09 09:24:23 +08:00
// ensure that the all the different ways to find and render a README work
func TestViewRepoDirectoryReadme ( t * testing . T ) {
defer tests . PrepareTestEnv ( t ) ( )
// there are many combinations:
// - READMEs can be .md, .txt, or have no extension
// - READMEs can be tagged with a language and even a country code
// - READMEs can be stored in docs/, .gitea/, or .github/
// - READMEs can be symlinks to other files
// - READMEs can be broken symlinks which should not render
//
// this doesn't cover all possible cases, just the major branches of the code
session := loginUser ( t , "user2" )
check := func ( name , url , expectedFilename , expectedReadmeType , expectedContent string ) {
t . Run ( name , func ( t * testing . T ) {
defer tests . PrintCurrentTest ( t ) ( )
req := NewRequest ( t , "GET" , url )
resp := session . MakeRequest ( t , req , http . StatusOK )
htmlDoc := NewHTMLParser ( t , resp . Body )
readmeName := htmlDoc . doc . Find ( "h4.file-header" )
readmeContent := htmlDoc . doc . Find ( ".file-view" ) // TODO: add a id="readme" to the output to make this test more precise
readmeType , _ := readmeContent . Attr ( "class" )
assert . Equal ( t , expectedFilename , strings . TrimSpace ( readmeName . Text ( ) ) )
assert . Contains ( t , readmeType , expectedReadmeType )
assert . Contains ( t , readmeContent . Text ( ) , expectedContent )
} )
}
// viewing the top level
check ( "Home" , "/user2/readme-test/" , "README.md" , "markdown" , "The cake is a lie." )
// viewing different file extensions
check ( "md" , "/user2/readme-test/src/branch/master/" , "README.md" , "markdown" , "The cake is a lie." )
check ( "txt" , "/user2/readme-test/src/branch/txt/" , "README.txt" , "plain-text" , "My spoon is too big." )
check ( "plain" , "/user2/readme-test/src/branch/plain/" , "README" , "plain-text" , "Birken my stocks gee howdy" )
check ( "i18n" , "/user2/readme-test/src/branch/i18n/" , "README.zh.md" , "markdown" , "蛋糕是一个谎言" )
// viewing different subdirectories
check ( "subdir" , "/user2/readme-test/src/branch/subdir/libcake" , "README.md" , "markdown" , "Four pints of sugar." )
check ( "docs-direct" , "/user2/readme-test/src/branch/special-subdir-docs/docs/" , "README.md" , "markdown" , "This is in docs/" )
check ( "docs" , "/user2/readme-test/src/branch/special-subdir-docs/" , "docs/README.md" , "markdown" , "This is in docs/" )
check ( ".gitea" , "/user2/readme-test/src/branch/special-subdir-.gitea/" , ".gitea/README.md" , "markdown" , "This is in .gitea/" )
check ( ".github" , "/user2/readme-test/src/branch/special-subdir-.github/" , ".github/README.md" , "markdown" , "This is in .github/" )
// symlinks
// symlinks are subtle:
// - they should be able to handle going a reasonable number of times up and down in the tree
// - they shouldn't get stuck on link cycles
// - they should determine the filetype based on the name of the link, not the target
check ( "symlink" , "/user2/readme-test/src/branch/symlink/" , "README.md" , "markdown" , "This is in some/other/path" )
check ( "symlink-multiple" , "/user2/readme-test/src/branch/symlink/some/" , "README.txt" , "plain-text" , "This is in some/other/path" )
check ( "symlink-up-and-down" , "/user2/readme-test/src/branch/symlink/up/back/down/down" , "README.md" , "markdown" , "It's a me, mario" )
// testing fallback rules
// READMEs are searched in this order:
// - [README.zh-cn.md, README.zh_cn.md, README.zh.md, README_zh.md, README.md, README.txt, README,
// docs/README.zh-cn.md, docs/README.zh_cn.md, docs/README.zh.md, docs/README_zh.md, docs/README.md, docs/README.txt, docs/README,
// .gitea/README.zh-cn.md, .gitea/README.zh_cn.md, .gitea/README.zh.md, .gitea/README_zh.md, .gitea/README.md, .gitea/README.txt, .gitea/README,
// .github/README.zh-cn.md, .github/README.zh_cn.md, .github/README.zh.md, .github/README_zh.md, .github/README.md, .github/README.txt, .github/README]
// and a broken/looped symlink counts as not existing at all and should be skipped.
// again, this doesn't cover all cases, but it covers a few
check ( "fallback/top" , "/user2/readme-test/src/branch/fallbacks/" , "README.en.md" , "markdown" , "This is README.en.md" )
check ( "fallback/2" , "/user2/readme-test/src/branch/fallbacks2/" , "README.md" , "markdown" , "This is README.md" )
check ( "fallback/3" , "/user2/readme-test/src/branch/fallbacks3/" , "README" , "plain-text" , "This is README" )
check ( "fallback/4" , "/user2/readme-test/src/branch/fallbacks4/" , "docs/README.en.md" , "markdown" , "This is docs/README.en.md" )
check ( "fallback/5" , "/user2/readme-test/src/branch/fallbacks5/" , "docs/README.md" , "markdown" , "This is docs/README.md" )
check ( "fallback/6" , "/user2/readme-test/src/branch/fallbacks6/" , "docs/README" , "plain-text" , "This is docs/README" )
check ( "fallback/7" , "/user2/readme-test/src/branch/fallbacks7/" , ".gitea/README.en.md" , "markdown" , "This is .gitea/README.en.md" )
check ( "fallback/8" , "/user2/readme-test/src/branch/fallbacks8/" , ".gitea/README.md" , "markdown" , "This is .gitea/README.md" )
check ( "fallback/9" , "/user2/readme-test/src/branch/fallbacks9/" , ".gitea/README" , "plain-text" , "This is .gitea/README" )
// this case tests that broken symlinks count as missing files, instead of rendering their contents
check ( "fallbacks-broken-symlinks" , "/user2/readme-test/src/branch/fallbacks-broken-symlinks/" , "docs/README" , "plain-text" , "This is docs/README" )
// some cases that should NOT render a README
// - /readme
// - /.github/docs/README.md
// - a symlink loop
missing := func ( name , url string ) {
t . Run ( "missing/" + name , func ( t * testing . T ) {
defer tests . PrintCurrentTest ( t ) ( )
req := NewRequest ( t , "GET" , url )
resp := session . MakeRequest ( t , req , http . StatusOK )
htmlDoc := NewHTMLParser ( t , resp . Body )
_ , exists := htmlDoc . doc . Find ( ".file-view" ) . Attr ( "class" )
fmt . Printf ( "%s" , resp . Body )
assert . False ( t , exists , "README should not have rendered" )
} )
}
missing ( "sp-ace" , "/user2/readme-test/src/branch/sp-ace/" )
missing ( "nested-special" , "/user2/readme-test/src/branch/special-subdir-nested/subproject" ) // the special subdirs should only trigger on the repo root
// missing("special-subdir-nested", "/user2/readme-test/src/branch/special-subdir-nested/") // This is currently FAILING, due to a bug introduced in https://github.com/go-gitea/gitea/pull/22177
missing ( "symlink-loop" , "/user2/readme-test/src/branch/symlink-loop/" )
}
2023-03-16 05:51:39 +08:00
func TestMarkDownReadmeImage ( t * testing . T ) {
2023-03-03 18:01:33 +08:00
defer tests . PrepareTestEnv ( t ) ( )
session := loginUser ( t , "user2" )
req := NewRequest ( t , "GET" , "/user2/repo1/src/branch/home-md-img-check" )
resp := session . MakeRequest ( t , req , http . StatusOK )
htmlDoc := NewHTMLParser ( t , resp . Body )
2023-03-16 05:51:39 +08:00
src , exists := htmlDoc . doc . Find ( ` .markdown img ` ) . Attr ( "src" )
assert . True ( t , exists , "Image not found in README" )
assert . Equal ( t , src , "/user2/repo1/media/branch/home-md-img-check/test-fake-img.jpg" )
2023-03-03 18:01:33 +08:00
req = NewRequest ( t , "GET" , "/user2/repo1/src/branch/home-md-img-check/README.md" )
resp = session . MakeRequest ( t , req , http . StatusOK )
htmlDoc = NewHTMLParser ( t , resp . Body )
2023-03-16 05:51:39 +08:00
src , exists = htmlDoc . doc . Find ( ` .markdown img ` ) . Attr ( "src" )
assert . True ( t , exists , "Image not found in markdown file" )
assert . Equal ( t , src , "/user2/repo1/media/branch/home-md-img-check/test-fake-img.jpg" )
}
func TestMarkDownReadmeImageSubfolder ( t * testing . T ) {
defer tests . PrepareTestEnv ( t ) ( )
session := loginUser ( t , "user2" )
// this branch has the README in the special docs/README.md location
req := NewRequest ( t , "GET" , "/user2/repo1/src/branch/sub-home-md-img-check" )
resp := session . MakeRequest ( t , req , http . StatusOK )
htmlDoc := NewHTMLParser ( t , resp . Body )
src , exists := htmlDoc . doc . Find ( ` .markdown img ` ) . Attr ( "src" )
assert . True ( t , exists , "Image not found in README" )
assert . Equal ( t , src , "/user2/repo1/media/branch/sub-home-md-img-check/docs/test-fake-img.jpg" )
req = NewRequest ( t , "GET" , "/user2/repo1/src/branch/sub-home-md-img-check/docs/README.md" )
resp = session . MakeRequest ( t , req , http . StatusOK )
htmlDoc = NewHTMLParser ( t , resp . Body )
src , exists = htmlDoc . doc . Find ( ` .markdown img ` ) . Attr ( "src" )
assert . True ( t , exists , "Image not found in markdown file" )
assert . Equal ( t , src , "/user2/repo1/media/branch/sub-home-md-img-check/docs/test-fake-img.jpg" )
2023-03-03 18:01:33 +08:00
}