diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index 03017ce6746df..047ec74837e24 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -1639,6 +1639,11 @@ issues.reopen_issue = Reopen
issues.reopen_comment_issue = Reopen with Comment
issues.create_comment = Comment
issues.comment.blocked_user = Cannot create or edit comment because you are blocked by the poster or repository owner.
+issues.not_closed = The issue is not closed.
+issues.reopen_not_allowed = No permission to reopen this issue.
+issues.reopen_not_allowed_merged = A pull request cannot be reopened after it has been merged.
+issues.comment.empty_content = The comment content cannot be empty.
+issues.already_closed = The issue is already closed.
issues.closed_at = `closed this issue %[2]s`
issues.reopened_at = `reopened this issue %[2]s`
issues.commit_ref_at = `referenced this issue from a commit %[2]s`
@@ -1959,7 +1964,8 @@ pulls.has_merged = Failed: The pull request has been merged. You cannot merge ag
pulls.push_rejected = Push Failed: The push was rejected. Review the Git Hooks for this repository.
pulls.push_rejected_summary = Full Rejection Message
pulls.push_rejected_no_message = Push Failed: The push was rejected but there was no remote message. Review the Git Hooks for this repository.
-pulls.open_unmerged_pull_exists = `You cannot perform a reopen operation because there is a pending pull request (#%d) with identical properties.`
+pulls.open_unmerged_pull_exists = You cannot perform a reopen operation because there is a pending pull request (#%d) with identical properties.
+pulls.head_branch_not_exist = The head branch does not exist, cannot reopen the pull request.
pulls.status_checking = Some checks are pending
pulls.status_checks_success = All checks were successful
pulls.status_checks_warning = Some checks reported warnings
diff --git a/routers/web/repo/issue_comment.go b/routers/web/repo/issue_comment.go
index 4e7f245296c17..cf88b6582a343 100644
--- a/routers/web/repo/issue_comment.go
+++ b/routers/web/repo/issue_comment.go
@@ -11,6 +11,7 @@ import (
"strconv"
"strings"
+ git_model "code.gitea.io/gitea/models/git"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/renderhelper"
user_model "code.gitea.io/gitea/models/user"
@@ -22,6 +23,7 @@ import (
repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
@@ -30,6 +32,142 @@ import (
pull_service "code.gitea.io/gitea/services/pull"
)
+func reopenPullWithComment(ctx *context.Context, issue *issues_model.Issue, content string, attachments []string) *issues_model.Comment {
+ pull := issue.PullRequest
+
+ // get head commit of branch in the head repo
+ if err := pull.LoadHeadRepo(ctx); err != nil {
+ ctx.ServerError("Unable to load head repo", err)
+ return nil
+ }
+
+ // check whether the ref of PR in base repo is consistent with the head commit of head branch in the head repo
+ // get head commit of PR
+ if pull.Flow == issues_model.PullRequestFlowGithub {
+ prHeadRef := pull.GetGitHeadRefName()
+ if err := pull.LoadBaseRepo(ctx); err != nil {
+ ctx.ServerError("Unable to load base repo", err)
+ return nil
+ }
+ prHeadCommitID, err := git.GetFullCommitID(ctx, pull.BaseRepo.RepoPath(), prHeadRef)
+ if err != nil {
+ ctx.ServerError("Get head commit Id of pr fail", err)
+ return nil
+ }
+
+ if ok := gitrepo.IsBranchExist(ctx, pull.HeadRepo, pull.BaseBranch); !ok {
+ // todo localize
+ ctx.JSONError("The origin branch is delete, cannot reopen.")
+ return nil
+ }
+ headBranchRef := git.RefNameFromBranch(pull.HeadBranch)
+ headBranchCommitID, err := git.GetFullCommitID(ctx, pull.HeadRepo.RepoPath(), headBranchRef.String())
+ if err != nil {
+ ctx.ServerError("Get head commit Id of head branch fail", err)
+ return nil
+ }
+
+ err = pull.LoadIssue(ctx)
+ if err != nil {
+ ctx.ServerError("load the issue of pull request error", err)
+ return nil
+ }
+
+ if prHeadCommitID != headBranchCommitID {
+ // force push to base repo
+ err := git.Push(ctx, pull.HeadRepo.RepoPath(), git.PushOptions{
+ Remote: pull.BaseRepo.RepoPath(),
+ Branch: pull.HeadBranch + ":" + prHeadRef,
+ Force: true,
+ Env: repo_module.InternalPushingEnvironment(pull.Issue.Poster, pull.BaseRepo),
+ })
+ if err != nil {
+ ctx.ServerError("force push error", err)
+ return nil
+ }
+ }
+ }
+
+ branchExist, err := git_model.IsBranchExist(ctx, pull.HeadRepo.ID, pull.HeadBranch)
+ if err != nil {
+ ctx.ServerError("IsBranchExist", err)
+ return nil
+ }
+ if !branchExist {
+ ctx.JSONError(ctx.Tr("repo.pulls.head_branch_not_exist"))
+ return nil
+ }
+
+ // check if an opened pull request exists with the same head branch and base branch
+ pr, err := issues_model.GetUnmergedPullRequest(ctx, pull.HeadRepoID, pull.BaseRepoID, pull.HeadBranch, pull.BaseBranch, pull.Flow)
+ if err != nil {
+ if !issues_model.IsErrPullRequestNotExist(err) {
+ ctx.JSONError(err.Error())
+ return nil
+ }
+ }
+ if pr != nil {
+ ctx.Flash.Info(ctx.Tr("repo.pulls.open_unmerged_pull_exists", pr.Index))
+ return nil
+ }
+
+ createdComment, err := issue_service.ReopenIssueWithComment(ctx, issue, ctx.Doer, "", content, attachments)
+ if err != nil {
+ if errors.Is(err, user_model.ErrBlockedUser) {
+ ctx.JSONError(ctx.Tr("repo.issues.comment.blocked_user"))
+ } else {
+ ctx.ServerError("ReopenIssue", err)
+ }
+ return nil
+ }
+
+ // check whether the ref of PR in base repo is consistent with the head commit of head branch in the head repo
+ // get head commit of PR
+ if pull.Flow == issues_model.PullRequestFlowGithub {
+ prHeadRef := pull.GetGitHeadRefName()
+ if err := pull.LoadBaseRepo(ctx); err != nil {
+ ctx.ServerError("Unable to load base repo", err)
+ return nil
+ }
+ prHeadCommitID, err := ctx.Repo.GitRepo.GetRefCommitID(prHeadRef)
+ if err != nil {
+ ctx.ServerError("Get head commit Id of pr fail", err)
+ return nil
+ }
+
+ headBranchCommitID, err := gitrepo.GetBranchCommitID(ctx, pull.HeadRepo, pull.HeadBranch)
+ if err != nil {
+ ctx.ServerError("Get head commit Id of head branch fail", err)
+ return nil
+ }
+
+ if err = pull.LoadIssue(ctx); err != nil {
+ ctx.ServerError("load the issue of pull request error", err)
+ return nil
+ }
+
+ // if the head commit ID of the PR is different from the head branch
+ if prHeadCommitID != headBranchCommitID {
+ // force push to base repo
+ err := git.Push(ctx, pull.HeadRepo.RepoPath(), git.PushOptions{
+ Remote: pull.BaseRepo.RepoPath(),
+ Branch: pull.HeadBranch + ":" + prHeadRef,
+ Force: true,
+ Env: repo_module.InternalPushingEnvironment(pull.Issue.Poster, pull.BaseRepo),
+ })
+ if err != nil {
+ ctx.ServerError("force push error", err)
+ return nil
+ }
+ }
+
+ // Regenerate patch and test conflict.
+ pull.HeadCommitID = ""
+ pull_service.StartPullRequestCheckImmediately(ctx, pull)
+ }
+ return createdComment
+}
+
// NewComment create a comment for issue
func NewComment(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.CreateCommentForm)
@@ -66,6 +204,11 @@ func NewComment(ctx *context.Context) {
return
}
+ if form.Content == "" {
+ ctx.JSONError(ctx.Tr("repo.issues.comment.empty_content"))
+ return
+ }
+
var attachments []string
if setting.Attachment.Enabled {
attachments = form.Files
@@ -76,132 +219,61 @@ func NewComment(ctx *context.Context) {
return
}
- var comment *issues_model.Comment
- defer func() {
- // Check if issue admin/poster changes the status of issue.
- if (ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) || (ctx.IsSigned && issue.IsPoster(ctx.Doer.ID))) &&
- (form.Status == "reopen" || form.Status == "close") &&
- !(issue.IsPull && issue.PullRequest.HasMerged) {
- // Duplication and conflict check should apply to reopen pull request.
- var pr *issues_model.PullRequest
-
- if form.Status == "reopen" && issue.IsPull {
- pull := issue.PullRequest
- var err error
- pr, err = issues_model.GetUnmergedPullRequest(ctx, pull.HeadRepoID, pull.BaseRepoID, pull.HeadBranch, pull.BaseBranch, pull.Flow)
- if err != nil {
- if !issues_model.IsErrPullRequestNotExist(err) {
- ctx.JSONError(ctx.Tr("repo.issues.dependency.pr_close_blocked"))
- return
- }
- }
-
- // Regenerate patch and test conflict.
- if pr == nil {
- issue.PullRequest.HeadCommitID = ""
- pull_service.StartPullRequestCheckImmediately(ctx, issue.PullRequest)
- }
+ var createdComment *issues_model.Comment
+ var err error
- // check whether the ref of PR in base repo is consistent with the head commit of head branch in the head repo
- // get head commit of PR
- if pull.Flow == issues_model.PullRequestFlowGithub {
- prHeadRef := pull.GetGitHeadRefName()
- if err := pull.LoadBaseRepo(ctx); err != nil {
- ctx.ServerError("Unable to load base repo", err)
- return
- }
- prHeadCommitID, err := git.GetFullCommitID(ctx, pull.BaseRepo.RepoPath(), prHeadRef)
- if err != nil {
- ctx.ServerError("Get head commit Id of pr fail", err)
- return
- }
-
- // get head commit of branch in the head repo
- if err := pull.LoadHeadRepo(ctx); err != nil {
- ctx.ServerError("Unable to load head repo", err)
- return
- }
- if ok := gitrepo.IsBranchExist(ctx, pull.HeadRepo, pull.BaseBranch); !ok {
- // todo localize
- ctx.JSONError("The origin branch is delete, cannot reopen.")
- return
- }
- headBranchRef := git.RefNameFromBranch(pull.HeadBranch)
- headBranchCommitID, err := git.GetFullCommitID(ctx, pull.HeadRepo.RepoPath(), headBranchRef.String())
- if err != nil {
- ctx.ServerError("Get head commit Id of head branch fail", err)
- return
- }
-
- err = pull.LoadIssue(ctx)
- if err != nil {
- ctx.ServerError("load the issue of pull request error", err)
- return
- }
-
- if prHeadCommitID != headBranchCommitID {
- // force push to base repo
- err := git.Push(ctx, pull.HeadRepo.RepoPath(), git.PushOptions{
- Remote: pull.BaseRepo.RepoPath(),
- Branch: pull.HeadBranch + ":" + prHeadRef,
- Force: true,
- Env: repo_module.InternalPushingEnvironment(pull.Issue.Poster, pull.BaseRepo),
- })
- if err != nil {
- ctx.ServerError("force push error", err)
- return
- }
- }
- }
- }
+ switch form.Status {
+ case "reopen":
+ if !issue.IsClosed {
+ ctx.JSONError(ctx.Tr("repo.issues.not_closed"))
+ return
+ }
+ if !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) &&
+ !issue.IsPoster(ctx.Doer.ID) &&
+ !ctx.Doer.IsAdmin {
+ ctx.JSONError(ctx.Tr("repo.issues.reopen_not_allowed"))
+ return
+ }
- if pr != nil {
- ctx.Flash.Info(ctx.Tr("repo.pulls.open_unmerged_pull_exists", pr.Index))
- } else {
- if form.Status == "close" && !issue.IsClosed {
- if err := issue_service.CloseIssue(ctx, issue, ctx.Doer, ""); err != nil {
- log.Error("CloseIssue: %v", err)
- if issues_model.IsErrDependenciesLeft(err) {
- if issue.IsPull {
- ctx.JSONError(ctx.Tr("repo.issues.dependency.pr_close_blocked"))
- } else {
- ctx.JSONError(ctx.Tr("repo.issues.dependency.issue_close_blocked"))
- }
- return
- }
- } else {
- if err := stopTimerIfAvailable(ctx, ctx.Doer, issue); err != nil {
- ctx.ServerError("stopTimerIfAvailable", err)
- return
- }
- log.Trace("Issue [%d] status changed to closed: %v", issue.ID, issue.IsClosed)
- }
- } else if form.Status == "reopen" && issue.IsClosed {
- if err := issue_service.ReopenIssue(ctx, issue, ctx.Doer, ""); err != nil {
- log.Error("ReopenIssue: %v", err)
- }
+ if issue.IsPull {
+ createdComment = reopenPullWithComment(ctx, issue, form.Content, attachments)
+ } else {
+ createdComment, err = issue_service.ReopenIssueWithComment(ctx, issue, ctx.Doer, "", form.Content, attachments)
+ if err != nil {
+ if errors.Is(err, user_model.ErrBlockedUser) {
+ ctx.JSONError(ctx.Tr("repo.issues.comment.blocked_user"))
+ } else {
+ ctx.ServerError("ReopenIssue", err)
}
+ return
}
}
+ if ctx.Written() {
+ return
+ }
+ case "close":
+ if issue.IsClosed {
+ ctx.JSONError(ctx.Tr("repo.issues.already_closed"))
+ return
+ }
- // Redirect to comment hashtag if there is any actual content.
- typeName := "issues"
- if issue.IsPull {
- typeName = "pulls"
+ if !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) &&
+ !issue.IsPoster(ctx.Doer.ID) &&
+ !ctx.Doer.IsAdmin {
+ ctx.JSONError(ctx.Tr("repo.issues.close_not_allowed"))
+ return
}
- if comment != nil {
- ctx.JSONRedirect(fmt.Sprintf("%s/%s/%d#%s", ctx.Repo.RepoLink, typeName, issue.Index, comment.HashTag()))
- } else {
- ctx.JSONRedirect(fmt.Sprintf("%s/%s/%d", ctx.Repo.RepoLink, typeName, issue.Index))
+
+ createdComment, err = issue_service.CloseIssueWithComment(ctx, issue, ctx.Doer, "", form.Content, attachments)
+ default:
+ if len(form.Content) == 0 && len(attachments) == 0 {
+ ctx.JSONError(ctx.Tr("repo.issues.comment.empty_content"))
+ return
}
- }()
- // Fix #321: Allow empty comments, as long as we have attachments.
- if len(form.Content) == 0 && len(attachments) == 0 {
- return
+ createdComment, err = issue_service.CreateIssueComment(ctx, ctx.Doer, ctx.Repo.Repository, issue, form.Content, attachments)
}
- comment, err := issue_service.CreateIssueComment(ctx, ctx.Doer, ctx.Repo.Repository, issue, form.Content, attachments)
if err != nil {
if errors.Is(err, user_model.ErrBlockedUser) {
ctx.JSONError(ctx.Tr("repo.issues.comment.blocked_user"))
@@ -211,7 +283,15 @@ func NewComment(ctx *context.Context) {
return
}
- log.Trace("Comment created: %d/%d/%d", ctx.Repo.Repository.ID, issue.ID, comment.ID)
+ // Redirect to comment hashtag if there is any actual content.
+ typeName := util.Iif(issue.IsPull, "pulls", "issues")
+
+ if createdComment != nil {
+ log.Trace("Comment created: %d/%d/%d", ctx.Repo.Repository.ID, issue.ID, createdComment.ID)
+ ctx.JSONRedirect(fmt.Sprintf("%s/%s/%d#%s", ctx.Repo.RepoLink, typeName, issue.Index, createdComment.HashTag()))
+ } else {
+ ctx.JSONRedirect(fmt.Sprintf("%s/%s/%d", ctx.Repo.RepoLink, typeName, issue.Index))
+ }
}
// UpdateCommentContent change comment of issue's content
diff --git a/services/issue/comments.go b/services/issue/comments.go
index 9442701029b57..bbeb75f27244e 100644
--- a/services/issue/comments.go
+++ b/services/issue/comments.go
@@ -15,6 +15,7 @@ import (
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/json"
+ "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/timeutil"
git_service "code.gitea.io/gitea/services/git"
notify_service "code.gitea.io/gitea/services/notify"
@@ -55,6 +56,22 @@ func CreateRefComment(ctx context.Context, doer *user_model.User, repo *repo_mod
return err
}
+func notifyCommentCreated(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, issue *issues_model.Issue, comment *issues_model.Comment) error {
+ mentions, err := issues_model.FindAndUpdateIssueMentions(ctx, issue, doer, comment.Content)
+ if err != nil {
+ return err
+ }
+
+ // reload issue to ensure it has the latest data, especially the number of comments
+ issue, err = issues_model.GetIssueByID(ctx, issue.ID)
+ if err != nil {
+ return err
+ }
+
+ notify_service.CreateIssueComment(ctx, doer, repo, issue, comment, mentions)
+ return nil
+}
+
// CreateIssueComment creates a plain issue comment.
func CreateIssueComment(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, issue *issues_model.Issue, content string, attachments []string) (*issues_model.Comment, error) {
if user_model.IsUserBlockedBy(ctx, doer, issue.PosterID, repo.OwnerID) {
@@ -75,19 +92,11 @@ func CreateIssueComment(ctx context.Context, doer *user_model.User, repo *repo_m
return nil, err
}
- mentions, err := issues_model.FindAndUpdateIssueMentions(ctx, issue, doer, comment.Content)
- if err != nil {
- return nil, err
+ if err := notifyCommentCreated(ctx, doer, repo, issue, comment); err != nil {
+ // If notification fails, we still return the comment but log the error.
+ log.Error("Failed to notify comment creation: %v", err)
}
- // reload issue to ensure it has the latest data, especially the number of comments
- issue, err = issues_model.GetIssueByID(ctx, issue.ID)
- if err != nil {
- return nil, err
- }
-
- notify_service.CreateIssueComment(ctx, doer, repo, issue, comment, mentions)
-
return comment, nil
}
diff --git a/services/issue/status.go b/services/issue/status.go
index fa59df93ba107..50a26547357ac 100644
--- a/services/issue/status.go
+++ b/services/issue/status.go
@@ -13,7 +13,7 @@ import (
notify_service "code.gitea.io/gitea/services/notify"
)
-// CloseIssue close an issue.
+// CloseIssue closes an issue
func CloseIssue(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, commitID string) error {
var comment *issues_model.Comment
if err := db.WithTx(ctx, func(ctx context.Context) error {
@@ -39,7 +39,53 @@ func CloseIssue(ctx context.Context, issue *issues_model.Issue, doer *user_model
return nil
}
-// ReopenIssue reopen an issue.
+// CloseIssueWithComment close an issue with comment
+func CloseIssueWithComment(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, commitID, commentContent string, attachments []string) (*issues_model.Comment, error) {
+ var refComment, createdComment *issues_model.Comment
+ if err := db.WithTx(ctx, func(ctx context.Context) error {
+ var err error
+ if commentContent != "" || len(attachments) > 0 {
+ createdComment, err = issues_model.CreateComment(ctx, &issues_model.CreateCommentOptions{
+ Type: issues_model.CommentTypeComment,
+ Doer: doer,
+ Repo: issue.Repo,
+ Issue: issue,
+ Content: commentContent,
+ Attachments: attachments,
+ })
+ if err != nil {
+ return err
+ }
+ }
+
+ refComment, err = issues_model.CloseIssue(ctx, issue, doer)
+ if err != nil {
+ if issues_model.IsErrDependenciesLeft(err) {
+ if _, err := issues_model.FinishIssueStopwatch(ctx, doer, issue); err != nil {
+ log.Error("Unable to stop stopwatch for issue[%d]#%d: %v", issue.ID, issue.Index, err)
+ }
+ }
+ return err
+ }
+
+ _, err = issues_model.FinishIssueStopwatch(ctx, doer, issue)
+ return err
+ }); err != nil {
+ return nil, err
+ }
+
+ if createdComment != nil {
+ if err := notifyCommentCreated(ctx, doer, issue.Repo, issue, createdComment); err != nil {
+ log.Error("Unable to notify comment created for issue[%d]#%d: %v", issue.ID, issue.Index, err)
+ }
+ }
+
+ notify_service.IssueChangeStatus(ctx, doer, commitID, issue, refComment, true)
+
+ return createdComment, nil
+}
+
+// ReopenIssue reopen an issue
// FIXME: If some issues dependent this one are closed, should we also reopen them?
func ReopenIssue(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, commitID string) error {
comment, err := issues_model.ReopenIssue(ctx, issue, doer)
@@ -51,3 +97,40 @@ func ReopenIssue(ctx context.Context, issue *issues_model.Issue, doer *user_mode
return nil
}
+
+// ReopenIssueWithComment reopen an issue with a comment.
+// FIXME: If some issues dependent this one are closed, should we also reopen them?
+func ReopenIssueWithComment(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, commitID, commentContent string, attachments []string) (*issues_model.Comment, error) {
+ var createdComment *issues_model.Comment
+ refComment, err := db.WithTx2(ctx, func(ctx context.Context) (*issues_model.Comment, error) {
+ var err error
+ if commentContent != "" || len(attachments) > 0 {
+ createdComment, err = issues_model.CreateComment(ctx, &issues_model.CreateCommentOptions{
+ Type: issues_model.CommentTypeComment,
+ Doer: doer,
+ Repo: issue.Repo,
+ Issue: issue,
+ Content: commentContent,
+ Attachments: attachments,
+ })
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ return issues_model.ReopenIssue(ctx, issue, doer)
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ if createdComment != nil {
+ if err := notifyCommentCreated(ctx, doer, issue.Repo, issue, createdComment); err != nil {
+ log.Error("Unable to notify comment created for issue[%d]#%d: %v", issue.ID, issue.Index, err)
+ }
+ }
+
+ notify_service.IssueChangeStatus(ctx, doer, commitID, issue, refComment, false)
+
+ return createdComment, nil
+}
diff --git a/tests/integration/issue_test.go b/tests/integration/issue_test.go
index 613353e55cbb9..09292b52b66e9 100644
--- a/tests/integration/issue_test.go
+++ b/tests/integration/issue_test.go
@@ -255,6 +255,26 @@ func TestIssueCommentClose(t *testing.T) {
htmlDoc := NewHTMLParser(t, resp.Body)
val := htmlDoc.doc.Find(".comment-list .comment .render-content p").First().Text()
assert.Equal(t, "Description", val)
+ val = strings.TrimSpace(htmlDoc.doc.Find(".issue-title-header .issue-state-label").Text())
+ assert.Equal(t, "Closed", val)
+}
+
+func TestIssueCommentReopen(t *testing.T) {
+ defer tests.PrepareTestEnv(t)()
+ session := loginUser(t, "user2")
+ issueURL := testNewIssue(t, session, "user2", "repo1", "Title", "Description")
+ testIssueAddComment(t, session, issueURL, "Test comment 1", "")
+ testIssueAddComment(t, session, issueURL, "Test comment 2", "close")
+ testIssueAddComment(t, session, issueURL, "Test comment 2", "reopen")
+
+ // Validate that issue content has not been updated
+ req := NewRequest(t, "GET", issueURL)
+ resp := session.MakeRequest(t, req, http.StatusOK)
+ htmlDoc := NewHTMLParser(t, resp.Body)
+ val := htmlDoc.doc.Find(".comment-list .comment .render-content p").First().Text()
+ assert.Equal(t, "Description", val)
+ val = strings.TrimSpace(htmlDoc.doc.Find(".issue-title-header .issue-state-label").Text())
+ assert.Equal(t, "Open", val)
}
func TestIssueCommentDelete(t *testing.T) {
diff --git a/tests/integration/pull_create_test.go b/tests/integration/pull_create_test.go
index 2eb5e94cf9d98..e546f3a7c5803 100644
--- a/tests/integration/pull_create_test.go
+++ b/tests/integration/pull_create_test.go
@@ -315,3 +315,49 @@ func TestCreatePullWhenBlocked(t *testing.T) {
MakeRequest(t, req, http.StatusNoContent)
})
}
+
+func TestPullRequestCommentClose(t *testing.T) {
+ onGiteaRun(t, func(t *testing.T, u *url.URL) {
+ session := loginUser(t, "user1")
+ testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
+ testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
+ resp := testPullCreate(t, session, "user1", "repo1", false, "master", "master", "This is a pull title")
+ pullURL := test.RedirectURL(resp)
+
+ testIssueAddComment(t, session, pullURL, "Test comment 1", "")
+ testIssueAddComment(t, session, pullURL, "Test comment 2", "")
+ testIssueAddComment(t, session, pullURL, "Test comment 3", "close")
+
+ // Validate that issue content has not been updated
+ req := NewRequest(t, "GET", pullURL)
+ resp = session.MakeRequest(t, req, http.StatusOK)
+ htmlDoc := NewHTMLParser(t, resp.Body)
+ val := strings.Split(strings.TrimSpace(htmlDoc.doc.Find("#issue-title-display > h1").First().Text()), "\n")[0]
+ assert.Equal(t, "This is a pull title", val)
+ val = strings.TrimSpace(htmlDoc.doc.Find(".issue-title-header .issue-state-label").Text())
+ assert.Equal(t, "Closed", val)
+ })
+}
+
+func TestPullRequestCommentReopen(t *testing.T) {
+ onGiteaRun(t, func(t *testing.T, u *url.URL) {
+ session := loginUser(t, "user1")
+ testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
+ testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
+ resp := testPullCreate(t, session, "user1", "repo1", false, "master", "master", "This is a pull title")
+ pullURL := test.RedirectURL(resp)
+
+ testIssueAddComment(t, session, pullURL, "Test comment 1", "")
+ testIssueAddComment(t, session, pullURL, "Test comment 2", "close")
+ testIssueAddComment(t, session, pullURL, "Test comment 2", "reopen")
+
+ // Validate that issue content has not been updated
+ req := NewRequest(t, "GET", pullURL)
+ resp = session.MakeRequest(t, req, http.StatusOK)
+ htmlDoc := NewHTMLParser(t, resp.Body)
+ val := strings.Split(strings.TrimSpace(htmlDoc.doc.Find("#issue-title-display > h1").First().Text()), "\n")[0]
+ assert.Equal(t, "This is a pull title", val)
+ val = strings.TrimSpace(htmlDoc.doc.Find(".issue-title-header .issue-state-label").Text())
+ assert.Equal(t, "Open", val)
+ })
+}