mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-12-27 13:39:19 -05:00
49eb168677
Sometimes you need to work on a feature which depends on another (unmerged) feature. In this case, you may create a PR based on that feature instead of the main branch. Currently, such PRs will be closed without the possibility to reopen in case the parent feature is merged and its branch is deleted. Automatic target branch change make life a lot easier in such cases. Github and Bitbucket behave in such way. Example: $PR_1$: main <- feature1 $PR_2$: feature1 <- feature2 Currently, merging $PR_1$ and deleting its branch leads to $PR_2$ being closed without the possibility to reopen. This is both annoying and loses the review history when you open a new PR. With this change, $PR_2$ will change its target branch to main ($PR_2$: main <- feature2) after $PR_1$ has been merged and its branch has been deleted. This behavior is enabled by default but can be disabled. For security reasons, this target branch change will not be executed when merging PRs targeting another repo. Fixes #27062 Fixes #18408 --------- Co-authored-by: Denys Konovalov <kontakt@denyskon.de> Co-authored-by: delvh <dev.lh@web.de>
176 lines
6.3 KiB
Go
176 lines
6.3 KiB
Go
// Copyright 2017 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package integration
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"path"
|
|
"strings"
|
|
"testing"
|
|
|
|
"code.gitea.io/gitea/modules/test"
|
|
"code.gitea.io/gitea/tests"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func testPullCreate(t *testing.T, session *TestSession, user, repo string, toSelf bool, targetBranch, sourceBranch, title string) *httptest.ResponseRecorder {
|
|
req := NewRequest(t, "GET", path.Join(user, repo))
|
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
|
|
|
// Click the PR button to create a pull
|
|
htmlDoc := NewHTMLParser(t, resp.Body)
|
|
link, exists := htmlDoc.doc.Find("#new-pull-request").Attr("href")
|
|
assert.True(t, exists, "The template has changed")
|
|
|
|
targetUser := strings.Split(link, "/")[1]
|
|
if toSelf && targetUser != user {
|
|
link = strings.Replace(link, targetUser, user, 1)
|
|
}
|
|
|
|
if targetBranch != "master" {
|
|
link = strings.Replace(link, "master...", targetBranch+"...", 1)
|
|
}
|
|
if sourceBranch != "master" {
|
|
if targetUser == user {
|
|
link = strings.Replace(link, "...master", "..."+sourceBranch, 1)
|
|
} else {
|
|
link = strings.Replace(link, ":master", ":"+sourceBranch, 1)
|
|
}
|
|
}
|
|
|
|
req = NewRequest(t, "GET", link)
|
|
resp = session.MakeRequest(t, req, http.StatusOK)
|
|
|
|
// Submit the form for creating the pull
|
|
htmlDoc = NewHTMLParser(t, resp.Body)
|
|
link, exists = htmlDoc.doc.Find("form.ui.form").Attr("action")
|
|
assert.True(t, exists, "The template has changed")
|
|
req = NewRequestWithValues(t, "POST", link, map[string]string{
|
|
"_csrf": htmlDoc.GetCSRF(),
|
|
"title": title,
|
|
})
|
|
resp = session.MakeRequest(t, req, http.StatusOK)
|
|
return resp
|
|
}
|
|
|
|
func TestPullCreate(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")
|
|
|
|
// check the redirected URL
|
|
url := test.RedirectURL(resp)
|
|
assert.Regexp(t, "^/user2/repo1/pulls/[0-9]*$", url)
|
|
|
|
// check .diff can be accessed and matches performed change
|
|
req := NewRequest(t, "GET", url+".diff")
|
|
resp = session.MakeRequest(t, req, http.StatusOK)
|
|
assert.Regexp(t, `\+Hello, World \(Edited\)`, resp.Body)
|
|
assert.Regexp(t, "^diff", resp.Body)
|
|
assert.NotRegexp(t, "diff.*diff", resp.Body) // not two diffs, just one
|
|
|
|
// check .patch can be accessed and matches performed change
|
|
req = NewRequest(t, "GET", url+".patch")
|
|
resp = session.MakeRequest(t, req, http.StatusOK)
|
|
assert.Regexp(t, `\+Hello, World \(Edited\)`, resp.Body)
|
|
assert.Regexp(t, "diff", resp.Body)
|
|
assert.Regexp(t, `Subject: \[PATCH\] Update README.md`, resp.Body)
|
|
assert.NotRegexp(t, "diff.*diff", resp.Body) // not two diffs, just one
|
|
})
|
|
}
|
|
|
|
func TestPullCreate_TitleEscape(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", "<i>XSS PR</i>")
|
|
|
|
// check the redirected URL
|
|
url := test.RedirectURL(resp)
|
|
assert.Regexp(t, "^/user2/repo1/pulls/[0-9]*$", url)
|
|
|
|
// Edit title
|
|
req := NewRequest(t, "GET", url)
|
|
resp = session.MakeRequest(t, req, http.StatusOK)
|
|
htmlDoc := NewHTMLParser(t, resp.Body)
|
|
editTestTitleURL, exists := htmlDoc.doc.Find("#save-edit-title").First().Attr("data-update-url")
|
|
assert.True(t, exists, "The template has changed")
|
|
|
|
req = NewRequestWithValues(t, "POST", editTestTitleURL, map[string]string{
|
|
"_csrf": htmlDoc.GetCSRF(),
|
|
"title": "<u>XSS PR</u>",
|
|
})
|
|
session.MakeRequest(t, req, http.StatusOK)
|
|
|
|
req = NewRequest(t, "GET", url)
|
|
resp = session.MakeRequest(t, req, http.StatusOK)
|
|
htmlDoc = NewHTMLParser(t, resp.Body)
|
|
titleHTML, err := htmlDoc.doc.Find(".comment-list .timeline-item.event .text b").First().Html()
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "<strike><i>XSS PR</i></strike>", titleHTML)
|
|
titleHTML, err = htmlDoc.doc.Find(".comment-list .timeline-item.event .text b").Next().Html()
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "<u>XSS PR</u>", titleHTML)
|
|
})
|
|
}
|
|
|
|
func testUIDeleteBranch(t *testing.T, session *TestSession, ownerName, repoName, branchName string) {
|
|
relURL := "/" + path.Join(ownerName, repoName, "branches")
|
|
req := NewRequest(t, "GET", relURL)
|
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
|
htmlDoc := NewHTMLParser(t, resp.Body)
|
|
|
|
req = NewRequestWithValues(t, "POST", relURL+"/delete", map[string]string{
|
|
"_csrf": htmlDoc.GetCSRF(),
|
|
"name": branchName,
|
|
})
|
|
session.MakeRequest(t, req, http.StatusOK)
|
|
}
|
|
|
|
func testDeleteRepository(t *testing.T, session *TestSession, ownerName, repoName string) {
|
|
relURL := "/" + path.Join(ownerName, repoName, "settings")
|
|
req := NewRequest(t, "GET", relURL)
|
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
|
htmlDoc := NewHTMLParser(t, resp.Body)
|
|
|
|
req = NewRequestWithValues(t, "POST", relURL+"?action=delete", map[string]string{
|
|
"_csrf": htmlDoc.GetCSRF(),
|
|
"repo_name": repoName,
|
|
})
|
|
session.MakeRequest(t, req, http.StatusSeeOther)
|
|
}
|
|
|
|
func TestPullBranchDelete(t *testing.T) {
|
|
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
|
defer tests.PrepareTestEnv(t)()
|
|
|
|
session := loginUser(t, "user1")
|
|
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
|
|
testCreateBranch(t, session, "user1", "repo1", "branch/master", "master1", http.StatusSeeOther)
|
|
testEditFile(t, session, "user1", "repo1", "master1", "README.md", "Hello, World (Edited)\n")
|
|
resp := testPullCreate(t, session, "user1", "repo1", false, "master", "master1", "This is a pull title")
|
|
|
|
// check the redirected URL
|
|
url := test.RedirectURL(resp)
|
|
assert.Regexp(t, "^/user2/repo1/pulls/[0-9]*$", url)
|
|
req := NewRequest(t, "GET", url)
|
|
session.MakeRequest(t, req, http.StatusOK)
|
|
|
|
// delete head branch and confirm pull page is ok
|
|
testUIDeleteBranch(t, session, "user1", "repo1", "master1")
|
|
req = NewRequest(t, "GET", url)
|
|
session.MakeRequest(t, req, http.StatusOK)
|
|
|
|
// delete head repository and confirm pull page is ok
|
|
testDeleteRepository(t, session, "user1", "repo1")
|
|
req = NewRequest(t, "GET", url)
|
|
session.MakeRequest(t, req, http.StatusOK)
|
|
})
|
|
}
|