forgejo/tests/integration/editor_test.go
Gusted a0f47b8de7
[BUG] Escape editor.add_tmpl translation
- Previously translations were escaped, but now translations are
accepted as-is and will be rendered as HTML. Use `TrString` to escape
the translation value.
- Adds integration test.
- Regression of 65248945c9.
- Resolves #3260
2024-04-16 15:50:49 +02:00

516 lines
16 KiB
Go

// Copyright 2017 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package integration
import (
"bytes"
"fmt"
"io"
"mime/multipart"
"net/http"
"net/http/httptest"
"net/url"
"path"
"testing"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/translation"
gitea_context "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestCreateFile(t *testing.T) {
onGiteaRun(t, func(t *testing.T, u *url.URL) {
session := loginUser(t, "user2")
// Request editor page
req := NewRequest(t, "GET", "/user2/repo1/_new/master/")
resp := session.MakeRequest(t, req, http.StatusOK)
doc := NewHTMLParser(t, resp.Body)
lastCommit := doc.GetInputValueByName("last_commit")
assert.NotEmpty(t, lastCommit)
// Save new file to master branch
req = NewRequestWithValues(t, "POST", "/user2/repo1/_new/master/", map[string]string{
"_csrf": doc.GetCSRF(),
"last_commit": lastCommit,
"tree_path": "test.txt",
"content": "Content",
"commit_choice": "direct",
"commit_mail_id": "3",
})
session.MakeRequest(t, req, http.StatusSeeOther)
})
}
func TestCreateFileOnProtectedBranch(t *testing.T) {
onGiteaRun(t, func(t *testing.T, u *url.URL) {
session := loginUser(t, "user2")
csrf := GetCSRF(t, session, "/user2/repo1/settings/branches")
// Change master branch to protected
req := NewRequestWithValues(t, "POST", "/user2/repo1/settings/branches/edit", map[string]string{
"_csrf": csrf,
"rule_name": "master",
"enable_push": "true",
})
session.MakeRequest(t, req, http.StatusSeeOther)
// Check if master branch has been locked successfully
flashCookie := session.GetCookie(gitea_context.CookieNameFlash)
assert.NotNil(t, flashCookie)
assert.EqualValues(t, "success%3DBranch%2Bprotection%2Bfor%2Brule%2B%2522master%2522%2Bhas%2Bbeen%2Bupdated.", flashCookie.Value)
// Request editor page
req = NewRequest(t, "GET", "/user2/repo1/_new/master/")
resp := session.MakeRequest(t, req, http.StatusOK)
doc := NewHTMLParser(t, resp.Body)
lastCommit := doc.GetInputValueByName("last_commit")
assert.NotEmpty(t, lastCommit)
// Save new file to master branch
req = NewRequestWithValues(t, "POST", "/user2/repo1/_new/master/", map[string]string{
"_csrf": doc.GetCSRF(),
"last_commit": lastCommit,
"tree_path": "test.txt",
"content": "Content",
"commit_choice": "direct",
"commit_mail_id": "3",
})
resp = session.MakeRequest(t, req, http.StatusOK)
// Check body for error message
assert.Contains(t, resp.Body.String(), "Cannot commit to protected branch "master".")
// remove the protected branch
csrf = GetCSRF(t, session, "/user2/repo1/settings/branches")
// Change master branch to protected
req = NewRequestWithValues(t, "POST", "/user2/repo1/settings/branches/1/delete", map[string]string{
"_csrf": csrf,
})
resp = session.MakeRequest(t, req, http.StatusOK)
res := make(map[string]string)
assert.NoError(t, json.NewDecoder(resp.Body).Decode(&res))
assert.EqualValues(t, "/user2/repo1/settings/branches", res["redirect"])
// Check if master branch has been locked successfully
flashCookie = session.GetCookie(gitea_context.CookieNameFlash)
assert.NotNil(t, flashCookie)
assert.EqualValues(t, "error%3DRemoving%2Bbranch%2Bprotection%2Brule%2B%25221%2522%2Bfailed.", flashCookie.Value)
})
}
func testEditFile(t *testing.T, session *TestSession, user, repo, branch, filePath, newContent string) *httptest.ResponseRecorder {
// Get to the 'edit this file' page
req := NewRequest(t, "GET", path.Join(user, repo, "_edit", branch, filePath))
resp := session.MakeRequest(t, req, http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
lastCommit := htmlDoc.GetInputValueByName("last_commit")
assert.NotEmpty(t, lastCommit)
// Submit the edits
req = NewRequestWithValues(t, "POST", path.Join(user, repo, "_edit", branch, filePath),
map[string]string{
"_csrf": htmlDoc.GetCSRF(),
"last_commit": lastCommit,
"tree_path": filePath,
"content": newContent,
"commit_choice": "direct",
"commit_mail_id": "-1",
},
)
session.MakeRequest(t, req, http.StatusSeeOther)
// Verify the change
req = NewRequest(t, "GET", path.Join(user, repo, "raw/branch", branch, filePath))
resp = session.MakeRequest(t, req, http.StatusOK)
assert.EqualValues(t, newContent, resp.Body.String())
return resp
}
func testEditFileToNewBranch(t *testing.T, session *TestSession, user, repo, branch, targetBranch, filePath, newContent string) *httptest.ResponseRecorder {
// Get to the 'edit this file' page
req := NewRequest(t, "GET", path.Join(user, repo, "_edit", branch, filePath))
resp := session.MakeRequest(t, req, http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
lastCommit := htmlDoc.GetInputValueByName("last_commit")
assert.NotEmpty(t, lastCommit)
// Submit the edits
req = NewRequestWithValues(t, "POST", path.Join(user, repo, "_edit", branch, filePath),
map[string]string{
"_csrf": htmlDoc.GetCSRF(),
"last_commit": lastCommit,
"tree_path": filePath,
"content": newContent,
"commit_choice": "commit-to-new-branch",
"new_branch_name": targetBranch,
"commit_mail_id": "-1",
},
)
session.MakeRequest(t, req, http.StatusSeeOther)
// Verify the change
req = NewRequest(t, "GET", path.Join(user, repo, "raw/branch", targetBranch, filePath))
resp = session.MakeRequest(t, req, http.StatusOK)
assert.EqualValues(t, newContent, resp.Body.String())
return resp
}
func TestEditFile(t *testing.T) {
onGiteaRun(t, func(t *testing.T, u *url.URL) {
session := loginUser(t, "user2")
testEditFile(t, session, "user2", "repo1", "master", "README.md", "Hello, World (Edited)\n")
})
}
func TestEditFileToNewBranch(t *testing.T) {
onGiteaRun(t, func(t *testing.T, u *url.URL) {
session := loginUser(t, "user2")
testEditFileToNewBranch(t, session, "user2", "repo1", "master", "feature/test", "README.md", "Hello, World (Edited)\n")
})
}
func TestEditorAddTranslation(t *testing.T) {
defer tests.PrepareTestEnv(t)()
session := loginUser(t, "user2")
req := NewRequest(t, "GET", "/user2/repo1/_new/master")
resp := session.MakeRequest(t, req, http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
placeholder, ok := htmlDoc.Find("input[name='commit_summary']").Attr("placeholder")
assert.True(t, ok)
assert.EqualValues(t, `Add "<filename>"`, placeholder)
}
func TestCommitMail(t *testing.T) {
onGiteaRun(t, func(t *testing.T, _ *url.URL) {
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
// Require that the user has KeepEmailPrivate enabled, because it needs
// to be tested that even with this setting enabled, it will use the
// provided mail and not revert to the placeholder one.
assert.True(t, user.KeepEmailPrivate)
inactivatedMail := unittest.AssertExistsAndLoadBean(t, &user_model.EmailAddress{ID: 35, UID: user.ID})
assert.False(t, inactivatedMail.IsActivated)
otherEmail := unittest.AssertExistsAndLoadBean(t, &user_model.EmailAddress{ID: 1, IsActivated: true})
assert.NotEqualValues(t, otherEmail.UID, user.ID)
primaryEmail := unittest.AssertExistsAndLoadBean(t, &user_model.EmailAddress{ID: 3, UID: user.ID, IsActivated: true})
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
gitRepo, _ := git.OpenRepository(git.DefaultContext, repo1.RepoPath())
defer gitRepo.Close()
session := loginUser(t, user.Name)
lastCommitAndCSRF := func(t *testing.T, link string, skipLastCommit bool) (string, string) {
t.Helper()
req := NewRequest(t, "GET", link)
resp := session.MakeRequest(t, req, http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
lastCommit := htmlDoc.GetInputValueByName("last_commit")
if !skipLastCommit {
assert.NotEmpty(t, lastCommit)
}
return lastCommit, htmlDoc.GetCSRF()
}
type caseOpts struct {
link string
fileName string
base map[string]string
skipLastCommit bool
}
// Base2 should have different content, so we can test two 'correct' operations
// without the second becoming a noop because no content was changed. If needed,
// link2 can point to a new file that's used with base2.
assertCase := func(t *testing.T, case1, case2 caseOpts) {
t.Helper()
t.Run("Not activated", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
lastCommit, csrf := lastCommitAndCSRF(t, case1.link, case1.skipLastCommit)
baseCopy := case1.base
baseCopy["_csrf"] = csrf
baseCopy["last_commit"] = lastCommit
baseCopy["commit_mail_id"] = fmt.Sprintf("%d", inactivatedMail.ID)
req := NewRequestWithValues(t, "POST", case1.link, baseCopy)
resp := session.MakeRequest(t, req, http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
assert.Contains(t,
htmlDoc.doc.Find(".ui.negative.message").Text(),
translation.NewLocale("en-US").Tr("repo.editor.invalid_commit_mail"),
)
})
t.Run("Not belong to user", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
lastCommit, csrf := lastCommitAndCSRF(t, case1.link, case1.skipLastCommit)
baseCopy := case1.base
baseCopy["_csrf"] = csrf
baseCopy["last_commit"] = lastCommit
baseCopy["commit_mail_id"] = fmt.Sprintf("%d", otherEmail.ID)
req := NewRequestWithValues(t, "POST", case1.link, baseCopy)
resp := session.MakeRequest(t, req, http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
assert.Contains(t,
htmlDoc.doc.Find(".ui.negative.message").Text(),
translation.NewLocale("en-US").Tr("repo.editor.invalid_commit_mail"),
)
})
t.Run("Placeholder mail", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
lastCommit, csrf := lastCommitAndCSRF(t, case1.link, case1.skipLastCommit)
baseCopy := case1.base
baseCopy["_csrf"] = csrf
baseCopy["last_commit"] = lastCommit
baseCopy["commit_mail_id"] = "-1"
req := NewRequestWithValues(t, "POST", case1.link, baseCopy)
session.MakeRequest(t, req, http.StatusSeeOther)
if !case2.skipLastCommit {
newlastCommit, _ := lastCommitAndCSRF(t, case1.link, false)
assert.NotEqualValues(t, newlastCommit, lastCommit)
}
commit, err := gitRepo.GetCommitByPath(case1.fileName)
assert.NoError(t, err)
assert.EqualValues(t, "user2", commit.Author.Name)
assert.EqualValues(t, "user2@noreply.example.org", commit.Author.Email)
assert.EqualValues(t, "user2", commit.Committer.Name)
assert.EqualValues(t, "user2@noreply.example.org", commit.Committer.Email)
})
t.Run("Normal", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
lastCommit, csrf := lastCommitAndCSRF(t, case2.link, case2.skipLastCommit)
baseCopy := case2.base
baseCopy["_csrf"] = csrf
baseCopy["last_commit"] = lastCommit
baseCopy["commit_mail_id"] = fmt.Sprintf("%d", primaryEmail.ID)
req := NewRequestWithValues(t, "POST", case2.link, baseCopy)
session.MakeRequest(t, req, http.StatusSeeOther)
if !case2.skipLastCommit {
newlastCommit, _ := lastCommitAndCSRF(t, case2.link, false)
assert.NotEqualValues(t, newlastCommit, lastCommit)
}
commit, err := gitRepo.GetCommitByPath(case2.fileName)
assert.NoError(t, err)
assert.EqualValues(t, "user2", commit.Author.Name)
assert.EqualValues(t, primaryEmail.Email, commit.Author.Email)
assert.EqualValues(t, "user2", commit.Committer.Name)
assert.EqualValues(t, primaryEmail.Email, commit.Committer.Email)
})
}
t.Run("New", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
assertCase(t, caseOpts{
fileName: "new_file",
link: "user2/repo1/_new/master",
base: map[string]string{
"tree_path": "new_file",
"content": "new_content",
"commit_choice": "direct",
},
}, caseOpts{
fileName: "new_file_2",
link: "user2/repo1/_new/master",
base: map[string]string{
"tree_path": "new_file_2",
"content": "new_content",
"commit_choice": "direct",
},
},
)
})
t.Run("Edit", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
assertCase(t, caseOpts{
fileName: "README.md",
link: "user2/repo1/_edit/master/README.md",
base: map[string]string{
"tree_path": "README.md",
"content": "Edit content",
"commit_choice": "direct",
},
}, caseOpts{
fileName: "README.md",
link: "user2/repo1/_edit/master/README.md",
base: map[string]string{
"tree_path": "README.md",
"content": "Other content",
"commit_choice": "direct",
},
},
)
})
t.Run("Delete", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
assertCase(t, caseOpts{
fileName: "new_file",
link: "user2/repo1/_delete/master/new_file",
base: map[string]string{
"commit_choice": "direct",
},
}, caseOpts{
fileName: "new_file_2",
link: "user2/repo1/_delete/master/new_file_2",
base: map[string]string{
"commit_choice": "direct",
},
},
)
})
t.Run("Upload", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
// Upload two separate times, so we have two different 'uploads' that can
// be used indepently of each other.
uploadFile := func(t *testing.T, name, content string) string {
t.Helper()
body := &bytes.Buffer{}
mpForm := multipart.NewWriter(body)
err := mpForm.WriteField("_csrf", GetCSRF(t, session, "/user2/repo1/_upload/master"))
require.NoError(t, err)
file, err := mpForm.CreateFormFile("file", name)
require.NoError(t, err)
io.Copy(file, bytes.NewBufferString(content))
require.NoError(t, mpForm.Close())
req := NewRequestWithBody(t, "POST", "/user2/repo1/upload-file", body)
req.Header.Add("Content-Type", mpForm.FormDataContentType())
resp := session.MakeRequest(t, req, http.StatusOK)
respMap := map[string]string{}
DecodeJSON(t, resp, &respMap)
return respMap["uuid"]
}
file1UUID := uploadFile(t, "upload_file_1", "Uploaded a file!")
file2UUID := uploadFile(t, "upload_file_2", "Uploaded another file!")
assertCase(t, caseOpts{
fileName: "upload_file_1",
link: "user2/repo1/_upload/master",
skipLastCommit: true,
base: map[string]string{
"commit_choice": "direct",
"files": file1UUID,
},
}, caseOpts{
fileName: "upload_file_2",
link: "user2/repo1/_upload/master",
skipLastCommit: true,
base: map[string]string{
"commit_choice": "direct",
"files": file2UUID,
},
},
)
})
t.Run("Apply patch", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
assertCase(t, caseOpts{
fileName: "diff-file-1.txt",
link: "user2/repo1/_diffpatch/master",
base: map[string]string{
"tree_path": "patch",
"commit_choice": "direct",
"content": `diff --git a/diff-file-1.txt b/diff-file-1.txt
new file mode 100644
index 0000000000..50fcd26d6c
--- /dev/null
+++ b/diff-file-1.txt
@@ -0,0 +1 @@
+File 1
`,
},
}, caseOpts{
fileName: "diff-file-2.txt",
link: "user2/repo1/_diffpatch/master",
base: map[string]string{
"tree_path": "patch",
"commit_choice": "direct",
"content": `diff --git a/diff-file-2.txt b/diff-file-2.txt
new file mode 100644
index 0000000000..4475433e27
--- /dev/null
+++ b/diff-file-2.txt
@@ -0,0 +1 @@
+File 2
`,
},
})
})
t.Run("Cherry pick", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
commitID1, err := gitRepo.GetCommitByPath("diff-file-1.txt")
assert.NoError(t, err)
commitID2, err := gitRepo.GetCommitByPath("diff-file-2.txt")
assert.NoError(t, err)
assertCase(t, caseOpts{
fileName: "diff-file-1.txt",
link: "user2/repo1/_cherrypick/" + commitID1.ID.String() + "/master",
base: map[string]string{
"commit_choice": "direct",
"revert": "true",
},
}, caseOpts{
fileName: "diff-file-2.txt",
link: "user2/repo1/_cherrypick/" + commitID2.ID.String() + "/master",
base: map[string]string{
"commit_choice": "direct",
"revert": "true",
},
})
})
})
}