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.

issue_test.go 9.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. // Copyright 2017 The Gitea Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package integrations
  5. import (
  6. "fmt"
  7. "net/http"
  8. "path"
  9. "strconv"
  10. "strings"
  11. "testing"
  12. "code.gitea.io/gitea/models"
  13. "code.gitea.io/gitea/modules/setting"
  14. "code.gitea.io/gitea/modules/test"
  15. "github.com/PuerkitoBio/goquery"
  16. "github.com/stretchr/testify/assert"
  17. )
  18. func getIssuesSelection(t testing.TB, htmlDoc *HTMLDoc) *goquery.Selection {
  19. issueList := htmlDoc.doc.Find(".issue.list")
  20. assert.EqualValues(t, 1, issueList.Length())
  21. return issueList.Find("li").Find(".title")
  22. }
  23. func getIssue(t *testing.T, repoID int64, issueSelection *goquery.Selection) *models.Issue {
  24. href, exists := issueSelection.Attr("href")
  25. assert.True(t, exists)
  26. indexStr := href[strings.LastIndexByte(href, '/')+1:]
  27. index, err := strconv.Atoi(indexStr)
  28. assert.NoError(t, err, "Invalid issue href: %s", href)
  29. return models.AssertExistsAndLoadBean(t, &models.Issue{RepoID: repoID, Index: int64(index)}).(*models.Issue)
  30. }
  31. func assertMatch(t testing.TB, issue *models.Issue, keyword string) {
  32. matches := strings.Contains(strings.ToLower(issue.Title), keyword) ||
  33. strings.Contains(strings.ToLower(issue.Content), keyword)
  34. for _, comment := range issue.Comments {
  35. matches = matches || strings.Contains(
  36. strings.ToLower(comment.Content),
  37. keyword,
  38. )
  39. }
  40. assert.True(t, matches)
  41. }
  42. func TestNoLoginViewIssues(t *testing.T) {
  43. prepareTestEnv(t)
  44. req := NewRequest(t, "GET", "/user2/repo1/issues")
  45. MakeRequest(t, req, http.StatusOK)
  46. }
  47. func TestViewIssuesSortByType(t *testing.T) {
  48. prepareTestEnv(t)
  49. user := models.AssertExistsAndLoadBean(t, &models.User{ID: 1}).(*models.User)
  50. repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
  51. session := loginUser(t, user.Name)
  52. req := NewRequest(t, "GET", repo.RelLink()+"/issues?type=created_by")
  53. resp := session.MakeRequest(t, req, http.StatusOK)
  54. htmlDoc := NewHTMLParser(t, resp.Body)
  55. issuesSelection := getIssuesSelection(t, htmlDoc)
  56. expectedNumIssues := models.GetCount(t,
  57. &models.Issue{RepoID: repo.ID, PosterID: user.ID},
  58. models.Cond("is_closed=?", false),
  59. models.Cond("is_pull=?", false),
  60. )
  61. if expectedNumIssues > setting.UI.IssuePagingNum {
  62. expectedNumIssues = setting.UI.IssuePagingNum
  63. }
  64. assert.EqualValues(t, expectedNumIssues, issuesSelection.Length())
  65. issuesSelection.Each(func(_ int, selection *goquery.Selection) {
  66. issue := getIssue(t, repo.ID, selection)
  67. assert.EqualValues(t, user.ID, issue.PosterID)
  68. })
  69. }
  70. func TestViewIssuesKeyword(t *testing.T) {
  71. prepareTestEnv(t)
  72. repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
  73. const keyword = "first"
  74. req := NewRequestf(t, "GET", "%s/issues?q=%s", repo.RelLink(), keyword)
  75. resp := MakeRequest(t, req, http.StatusOK)
  76. htmlDoc := NewHTMLParser(t, resp.Body)
  77. issuesSelection := getIssuesSelection(t, htmlDoc)
  78. assert.EqualValues(t, 1, issuesSelection.Length())
  79. issuesSelection.Each(func(_ int, selection *goquery.Selection) {
  80. issue := getIssue(t, repo.ID, selection)
  81. assert.False(t, issue.IsClosed)
  82. assert.False(t, issue.IsPull)
  83. assertMatch(t, issue, keyword)
  84. })
  85. }
  86. func TestNoLoginViewIssue(t *testing.T) {
  87. prepareTestEnv(t)
  88. req := NewRequest(t, "GET", "/user2/repo1/issues/1")
  89. MakeRequest(t, req, http.StatusOK)
  90. }
  91. func testNewIssue(t *testing.T, session *TestSession, user, repo, title, content string) string {
  92. req := NewRequest(t, "GET", path.Join(user, repo, "issues", "new"))
  93. resp := session.MakeRequest(t, req, http.StatusOK)
  94. htmlDoc := NewHTMLParser(t, resp.Body)
  95. link, exists := htmlDoc.doc.Find("form.ui.form").Attr("action")
  96. assert.True(t, exists, "The template has changed")
  97. req = NewRequestWithValues(t, "POST", link, map[string]string{
  98. "_csrf": htmlDoc.GetCSRF(),
  99. "title": title,
  100. "content": content,
  101. })
  102. resp = session.MakeRequest(t, req, http.StatusFound)
  103. issueURL := test.RedirectURL(resp)
  104. req = NewRequest(t, "GET", issueURL)
  105. resp = session.MakeRequest(t, req, http.StatusOK)
  106. htmlDoc = NewHTMLParser(t, resp.Body)
  107. val := htmlDoc.doc.Find("#issue-title").Text()
  108. assert.Equal(t, title, val)
  109. val = htmlDoc.doc.Find(".comment-list .comments .comment .render-content p").First().Text()
  110. assert.Equal(t, content, val)
  111. return issueURL
  112. }
  113. func testIssueAddComment(t *testing.T, session *TestSession, issueURL, content, status string) int64 {
  114. req := NewRequest(t, "GET", issueURL)
  115. resp := session.MakeRequest(t, req, http.StatusOK)
  116. htmlDoc := NewHTMLParser(t, resp.Body)
  117. link, exists := htmlDoc.doc.Find("#comment-form").Attr("action")
  118. assert.True(t, exists, "The template has changed")
  119. commentCount := htmlDoc.doc.Find(".comment-list .comments .comment .render-content").Length()
  120. req = NewRequestWithValues(t, "POST", link, map[string]string{
  121. "_csrf": htmlDoc.GetCSRF(),
  122. "content": content,
  123. "status": status,
  124. })
  125. resp = session.MakeRequest(t, req, http.StatusFound)
  126. req = NewRequest(t, "GET", test.RedirectURL(resp))
  127. resp = session.MakeRequest(t, req, http.StatusOK)
  128. htmlDoc = NewHTMLParser(t, resp.Body)
  129. val := htmlDoc.doc.Find(".comment-list .comments .comment .render-content p").Eq(commentCount).Text()
  130. assert.Equal(t, content, val)
  131. idAttr, has := htmlDoc.doc.Find(".comment-list .comments .comment").Eq(commentCount).Attr("id")
  132. idStr := idAttr[strings.LastIndexByte(idAttr, '-')+1:]
  133. assert.True(t, has)
  134. id, err := strconv.Atoi(idStr)
  135. assert.NoError(t, err)
  136. return int64(id)
  137. }
  138. func TestNewIssue(t *testing.T) {
  139. prepareTestEnv(t)
  140. session := loginUser(t, "user2")
  141. testNewIssue(t, session, "user2", "repo1", "Title", "Description")
  142. }
  143. func TestIssueCommentClose(t *testing.T) {
  144. prepareTestEnv(t)
  145. session := loginUser(t, "user2")
  146. issueURL := testNewIssue(t, session, "user2", "repo1", "Title", "Description")
  147. testIssueAddComment(t, session, issueURL, "Test comment 1", "")
  148. testIssueAddComment(t, session, issueURL, "Test comment 2", "")
  149. testIssueAddComment(t, session, issueURL, "Test comment 3", "close")
  150. // Validate that issue content has not been updated
  151. req := NewRequest(t, "GET", issueURL)
  152. resp := session.MakeRequest(t, req, http.StatusOK)
  153. htmlDoc := NewHTMLParser(t, resp.Body)
  154. val := htmlDoc.doc.Find(".comment-list .comments .comment .render-content p").First().Text()
  155. assert.Equal(t, "Description", val)
  156. }
  157. func TestIssueCrossReference(t *testing.T) {
  158. prepareTestEnv(t)
  159. // Issue that will be referenced
  160. _, issueBase := testIssueWithBean(t, "user2", 1, "Title", "Description")
  161. // Ref from issue title
  162. issueRefURL, issueRef := testIssueWithBean(t, "user2", 1, fmt.Sprintf("Title ref #%d", issueBase.Index), "Description")
  163. models.AssertExistsAndLoadBean(t, &models.Comment{
  164. IssueID: issueBase.ID,
  165. RefRepoID: 1,
  166. RefIssueID: issueRef.ID,
  167. RefCommentID: 0,
  168. RefIsPull: false,
  169. RefAction: models.XRefActionNone})
  170. // Edit title, neuter ref
  171. testIssueChangeInfo(t, "user2", issueRefURL, "title", "Title no ref")
  172. models.AssertExistsAndLoadBean(t, &models.Comment{
  173. IssueID: issueBase.ID,
  174. RefRepoID: 1,
  175. RefIssueID: issueRef.ID,
  176. RefCommentID: 0,
  177. RefIsPull: false,
  178. RefAction: models.XRefActionNeutered})
  179. // Ref from issue content
  180. issueRefURL, issueRef = testIssueWithBean(t, "user2", 1, "TitleXRef", fmt.Sprintf("Description ref #%d", issueBase.Index))
  181. models.AssertExistsAndLoadBean(t, &models.Comment{
  182. IssueID: issueBase.ID,
  183. RefRepoID: 1,
  184. RefIssueID: issueRef.ID,
  185. RefCommentID: 0,
  186. RefIsPull: false,
  187. RefAction: models.XRefActionNone})
  188. // Edit content, neuter ref
  189. testIssueChangeInfo(t, "user2", issueRefURL, "content", "Description no ref")
  190. models.AssertExistsAndLoadBean(t, &models.Comment{
  191. IssueID: issueBase.ID,
  192. RefRepoID: 1,
  193. RefIssueID: issueRef.ID,
  194. RefCommentID: 0,
  195. RefIsPull: false,
  196. RefAction: models.XRefActionNeutered})
  197. // Ref from a comment
  198. session := loginUser(t, "user2")
  199. commentID := testIssueAddComment(t, session, issueRefURL, fmt.Sprintf("Adding ref from comment #%d", issueBase.Index), "")
  200. comment := &models.Comment{
  201. IssueID: issueBase.ID,
  202. RefRepoID: 1,
  203. RefIssueID: issueRef.ID,
  204. RefCommentID: commentID,
  205. RefIsPull: false,
  206. RefAction: models.XRefActionNone}
  207. models.AssertExistsAndLoadBean(t, comment)
  208. // Ref from a different repository
  209. issueRefURL, issueRef = testIssueWithBean(t, "user12", 10, "TitleXRef", fmt.Sprintf("Description ref user2/repo1#%d", issueBase.Index))
  210. models.AssertExistsAndLoadBean(t, &models.Comment{
  211. IssueID: issueBase.ID,
  212. RefRepoID: 10,
  213. RefIssueID: issueRef.ID,
  214. RefCommentID: 0,
  215. RefIsPull: false,
  216. RefAction: models.XRefActionNone})
  217. }
  218. func testIssueWithBean(t *testing.T, user string, repoID int64, title, content string) (string, *models.Issue) {
  219. session := loginUser(t, user)
  220. issueURL := testNewIssue(t, session, user, fmt.Sprintf("repo%d", repoID), title, content)
  221. indexStr := issueURL[strings.LastIndexByte(issueURL, '/')+1:]
  222. index, err := strconv.Atoi(indexStr)
  223. assert.NoError(t, err, "Invalid issue href: %s", issueURL)
  224. issue := &models.Issue{RepoID: repoID, Index: int64(index)}
  225. models.AssertExistsAndLoadBean(t, issue)
  226. return issueURL, issue
  227. }
  228. func testIssueChangeInfo(t *testing.T, user, issueURL, info string, value string) {
  229. session := loginUser(t, user)
  230. req := NewRequest(t, "GET", issueURL)
  231. resp := session.MakeRequest(t, req, http.StatusOK)
  232. htmlDoc := NewHTMLParser(t, resp.Body)
  233. req = NewRequestWithValues(t, "POST", path.Join(issueURL, info), map[string]string{
  234. "_csrf": htmlDoc.GetCSRF(),
  235. info: value,
  236. })
  237. _ = session.MakeRequest(t, req, http.StatusOK)
  238. }