| @@ -0,0 +1,93 @@ | |||
| // Copyright 2019 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 integrations | |||
| import ( | |||
| "fmt" | |||
| "net/http" | |||
| "path" | |||
| "testing" | |||
| "code.gitea.io/gitea/models" | |||
| api "code.gitea.io/sdk/gitea" | |||
| "github.com/stretchr/testify/assert" | |||
| ) | |||
| func TestPullCreate_CommitStatus(t *testing.T) { | |||
| prepareTestEnv(t) | |||
| session := loginUser(t, "user1") | |||
| testRepoFork(t, session, "user2", "repo1", "user1", "repo1") | |||
| testEditFileToNewBranch(t, session, "user1", "repo1", "master", "status1", "README.md", "status1") | |||
| url := path.Join("user1", "repo1", "compare", "master...status1") | |||
| req := NewRequestWithValues(t, "POST", url, | |||
| map[string]string{ | |||
| "_csrf": GetCSRF(t, session, url), | |||
| "title": "pull request from status1", | |||
| }, | |||
| ) | |||
| session.MakeRequest(t, req, http.StatusFound) | |||
| req = NewRequest(t, "GET", "/user1/repo1/pulls") | |||
| resp := session.MakeRequest(t, req, http.StatusOK) | |||
| doc := NewHTMLParser(t, resp.Body) | |||
| // Request repository commits page | |||
| req = NewRequest(t, "GET", "/user1/repo1/pulls/1/commits") | |||
| resp = session.MakeRequest(t, req, http.StatusOK) | |||
| doc = NewHTMLParser(t, resp.Body) | |||
| // Get first commit URL | |||
| commitURL, exists := doc.doc.Find("#commits-table tbody tr td.sha a").Last().Attr("href") | |||
| assert.True(t, exists) | |||
| assert.NotEmpty(t, commitURL) | |||
| commitID := path.Base(commitURL) | |||
| statusList := []models.CommitStatusState{ | |||
| models.CommitStatusPending, | |||
| models.CommitStatusError, | |||
| models.CommitStatusFailure, | |||
| models.CommitStatusWarning, | |||
| models.CommitStatusSuccess, | |||
| } | |||
| statesIcons := map[models.CommitStatusState]string{ | |||
| models.CommitStatusPending: "circle icon yellow", | |||
| models.CommitStatusSuccess: "check icon green", | |||
| models.CommitStatusError: "warning icon red", | |||
| models.CommitStatusFailure: "remove icon red", | |||
| models.CommitStatusWarning: "warning sign icon yellow", | |||
| } | |||
| // Update commit status, and check if icon is updated as well | |||
| for _, status := range statusList { | |||
| // Call API to add status for commit | |||
| token := getTokenForLoggedInUser(t, session) | |||
| req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/user1/repo1/statuses/%s?token=%s", commitID, token), | |||
| api.CreateStatusOption{ | |||
| State: api.StatusState(status), | |||
| TargetURL: "http://test.ci/", | |||
| Description: "", | |||
| Context: "testci", | |||
| }, | |||
| ) | |||
| session.MakeRequest(t, req, http.StatusCreated) | |||
| req = NewRequestf(t, "GET", "/user1/repo1/pulls/1/commits") | |||
| resp = session.MakeRequest(t, req, http.StatusOK) | |||
| doc = NewHTMLParser(t, resp.Body) | |||
| commitURL, exists = doc.doc.Find("#commits-table tbody tr td.sha a").Last().Attr("href") | |||
| assert.True(t, exists) | |||
| assert.NotEmpty(t, commitURL) | |||
| assert.EqualValues(t, commitID, path.Base(commitURL)) | |||
| cls, ok := doc.doc.Find("#commits-table tbody tr td.message i.commit-status").Last().Attr("class") | |||
| assert.True(t, ok) | |||
| assert.EqualValues(t, "commit-status "+statesIcons[status], cls) | |||
| } | |||
| } | |||
| @@ -292,6 +292,31 @@ func (pr *PullRequest) CanAutoMerge() bool { | |||
| return pr.Status == PullRequestStatusMergeable | |||
| } | |||
| // GetLastCommitStatus returns the last commit status for this pull request. | |||
| func (pr *PullRequest) GetLastCommitStatus() (status *CommitStatus, err error) { | |||
| if err = pr.GetHeadRepo(); err != nil { | |||
| return nil, err | |||
| } | |||
| headGitRepo, err := git.OpenRepository(pr.HeadRepo.RepoPath()) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| repo := pr.HeadRepo | |||
| lastCommitID, err := headGitRepo.GetBranchCommitID(pr.HeadBranch) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| var statusList []*CommitStatus | |||
| statusList, err = GetLatestCommitStatus(repo, lastCommitID, 0) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| return CalcCommitStatus(statusList), nil | |||
| } | |||
| // MergeStyle represents the approach to merge commits into base branch. | |||
| type MergeStyle string | |||
| @@ -214,6 +214,8 @@ func issues(ctx *context.Context, milestoneID int64, isPullOption util.OptionalB | |||
| } | |||
| } | |||
| var commitStatus = make(map[int64]*models.CommitStatus, len(issues)) | |||
| // Get posters. | |||
| for i := range issues { | |||
| // Check read status | |||
| @@ -223,8 +225,14 @@ func issues(ctx *context.Context, milestoneID int64, isPullOption util.OptionalB | |||
| ctx.ServerError("GetIsRead", err) | |||
| return | |||
| } | |||
| if isPullOption == util.OptionalBoolTrue { | |||
| commitStatus[issues[i].PullRequest.ID], _ = issues[i].PullRequest.GetLastCommitStatus() | |||
| } | |||
| } | |||
| ctx.Data["Issues"] = issues | |||
| ctx.Data["CommitStatus"] = commitStatus | |||
| // Get assignees. | |||
| ctx.Data["Assignees"], err = repo.GetAssignees() | |||
| @@ -319,8 +319,13 @@ func Issues(ctx *context.Context) { | |||
| return | |||
| } | |||
| var commitStatus = make(map[int64]*models.CommitStatus, len(issues)) | |||
| for _, issue := range issues { | |||
| issue.Repo = showReposMap[issue.RepoID] | |||
| if isPullList { | |||
| commitStatus[issue.PullRequest.ID], _ = issue.PullRequest.GetLastCommitStatus() | |||
| } | |||
| } | |||
| issueStats, err := models.GetUserIssueStats(models.UserIssueStatsOptions{ | |||
| @@ -344,6 +349,7 @@ func Issues(ctx *context.Context) { | |||
| } | |||
| ctx.Data["Issues"] = issues | |||
| ctx.Data["CommitStatus"] = commitStatus | |||
| ctx.Data["Repos"] = showRepos | |||
| ctx.Data["Counts"] = counts | |||
| ctx.Data["Page"] = paginater.New(total, setting.UI.IssuePagingNum, page, 5) | |||
| @@ -203,6 +203,12 @@ | |||
| <div class="ui {{if .IsRead}}black{{else}}green{{end}} label">#{{.Index}}</div> | |||
| <a class="title has-emoji" href="{{$.Link}}/{{.Index}}">{{.Title}}</a> | |||
| {{if .IsPull }} | |||
| {{if (index $.CommitStatus .ID)}} | |||
| {{template "repo/commit_status" (index $.CommitStatus .ID)}} | |||
| {{end}} | |||
| {{end}} | |||
| {{if .Ref}} | |||
| <a class="ui label" href="{{$.RepoLink}}/src/branch/{{.Ref}}">{{.Ref}}</a> | |||
| {{end}} | |||
| @@ -66,6 +66,12 @@ | |||
| <div class="ui label">{{if not $.RepoID}}{{.Repo.FullName}}{{end}}#{{.Index}}</div> | |||
| <a class="title has-emoji" href="{{AppSubUrl}}/{{.Repo.Owner.Name}}/{{.Repo.Name}}/issues/{{.Index}}">{{.Title}}</a> | |||
| {{if .IsPull }} | |||
| {{if (index $.CommitStatus .ID)}} | |||
| {{template "repo/commit_status" (index $.CommitStatus .ID)}} | |||
| {{end}} | |||
| {{end}} | |||
| {{with .Labels}} | |||
| {{/* If we have any labels, we should show them | |||
| with a 2.5 line height, this way they don't look | |||