mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-12-29 15:13:53 +03:00
Consolidate boilerplate in integration tests (#1979)
This commit is contained in:
parent
a3868ef536
commit
ce9b86082c
16 changed files with 147 additions and 166 deletions
|
@ -23,7 +23,7 @@ func TestAPITeam(t *testing.T) {
|
||||||
team := models.AssertExistsAndLoadBean(t, &models.Team{ID: teamUser.TeamID}).(*models.Team)
|
team := models.AssertExistsAndLoadBean(t, &models.Team{ID: teamUser.TeamID}).(*models.Team)
|
||||||
user := models.AssertExistsAndLoadBean(t, &models.User{ID: teamUser.UID}).(*models.User)
|
user := models.AssertExistsAndLoadBean(t, &models.User{ID: teamUser.UID}).(*models.User)
|
||||||
|
|
||||||
session := loginUser(t, user.Name, "password")
|
session := loginUser(t, user.Name)
|
||||||
url := fmt.Sprintf("/api/v1/teams/%d", teamUser.TeamID)
|
url := fmt.Sprintf("/api/v1/teams/%d", teamUser.TeamID)
|
||||||
req := NewRequest(t, "GET", url)
|
req := NewRequest(t, "GET", url)
|
||||||
resp := session.MakeRequest(t, req)
|
resp := session.MakeRequest(t, req)
|
||||||
|
|
|
@ -5,10 +5,8 @@
|
||||||
package integrations
|
package integrations
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
|
@ -21,21 +19,19 @@ func TestChangeDefaultBranch(t *testing.T) {
|
||||||
repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
|
repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
|
||||||
owner := models.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
|
owner := models.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
|
||||||
|
|
||||||
session := loginUser(t, owner.Name, "password")
|
session := loginUser(t, owner.Name)
|
||||||
branchesURL := fmt.Sprintf("/%s/%s/settings/branches", owner.Name, repo.Name)
|
branchesURL := fmt.Sprintf("/%s/%s/settings/branches", owner.Name, repo.Name)
|
||||||
|
|
||||||
req := NewRequest(t, "GET", branchesURL)
|
req := NewRequest(t, "GET", branchesURL)
|
||||||
resp := session.MakeRequest(t, req)
|
resp := session.MakeRequest(t, req)
|
||||||
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
||||||
doc, err := NewHtmlParser(resp.Body)
|
doc := NewHtmlParser(t, resp.Body)
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
req = NewRequestBody(t, "POST", branchesURL,
|
req = NewRequestWithValues(t, "POST", branchesURL, map[string]string{
|
||||||
bytes.NewBufferString(url.Values{
|
"_csrf": doc.GetCSRF(),
|
||||||
"_csrf": []string{doc.GetInputValueByName("_csrf")},
|
"action": "default_branch",
|
||||||
"action": []string{"default_branch"},
|
"branch": "DefaultBranch",
|
||||||
"branch": []string{"DefaultBranch"},
|
})
|
||||||
}.Encode()))
|
|
||||||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||||
resp = session.MakeRequest(t, req)
|
resp = session.MakeRequest(t, req)
|
||||||
assert.EqualValues(t, http.StatusFound, resp.HeaderCode)
|
assert.EqualValues(t, http.StatusFound, resp.HeaderCode)
|
||||||
|
@ -43,15 +39,13 @@ func TestChangeDefaultBranch(t *testing.T) {
|
||||||
req = NewRequest(t, "GET", branchesURL)
|
req = NewRequest(t, "GET", branchesURL)
|
||||||
resp = session.MakeRequest(t, req)
|
resp = session.MakeRequest(t, req)
|
||||||
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
||||||
doc, err = NewHtmlParser(resp.Body)
|
doc = NewHtmlParser(t, resp.Body)
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
req = NewRequestBody(t, "POST", branchesURL,
|
req = NewRequestWithValues(t, "POST", branchesURL, map[string]string{
|
||||||
bytes.NewBufferString(url.Values{
|
"_csrf": doc.GetInputValueByName("_csrf"),
|
||||||
"_csrf": []string{doc.GetInputValueByName("_csrf")},
|
"action": "default_branch",
|
||||||
"action": []string{"default_branch"},
|
"branch": "does_not_exist",
|
||||||
"branch": []string{"does_not_exist"},
|
})
|
||||||
}.Encode()))
|
|
||||||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||||
resp = session.MakeRequest(t, req)
|
resp = session.MakeRequest(t, req)
|
||||||
assert.EqualValues(t, http.StatusNotFound, resp.HeaderCode)
|
assert.EqualValues(t, http.StatusNotFound, resp.HeaderCode)
|
||||||
|
|
|
@ -5,9 +5,7 @@
|
||||||
package integrations
|
package integrations
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
|
@ -18,18 +16,16 @@ import (
|
||||||
func TestDeleteUser(t *testing.T) {
|
func TestDeleteUser(t *testing.T) {
|
||||||
prepareTestEnv(t)
|
prepareTestEnv(t)
|
||||||
|
|
||||||
session := loginUser(t, "user1", "password")
|
session := loginUser(t, "user1")
|
||||||
|
|
||||||
req := NewRequest(t, "GET", "/admin/users/8")
|
req := NewRequest(t, "GET", "/admin/users/8")
|
||||||
resp := session.MakeRequest(t, req)
|
resp := session.MakeRequest(t, req)
|
||||||
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
||||||
|
|
||||||
doc, err := NewHtmlParser(resp.Body)
|
doc := NewHtmlParser(t, resp.Body)
|
||||||
assert.NoError(t, err)
|
req = NewRequestWithValues(t, "POST", "/admin/users/8/delete", map[string]string{
|
||||||
req = NewRequestBody(t, "POST", "/admin/users/8/delete",
|
"_csrf": doc.GetCSRF(),
|
||||||
bytes.NewBufferString(url.Values{
|
})
|
||||||
"_csrf": []string{doc.GetInputValueByName("_csrf")},
|
|
||||||
}.Encode()))
|
|
||||||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||||
resp = session.MakeRequest(t, req)
|
resp = session.MakeRequest(t, req)
|
||||||
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
||||||
|
|
|
@ -5,9 +5,7 @@
|
||||||
package integrations
|
package integrations
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
"path"
|
"path"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -17,28 +15,25 @@ import (
|
||||||
func TestCreateFile(t *testing.T) {
|
func TestCreateFile(t *testing.T) {
|
||||||
prepareTestEnv(t)
|
prepareTestEnv(t)
|
||||||
|
|
||||||
session := loginUser(t, "user2", "password")
|
session := loginUser(t, "user2")
|
||||||
|
|
||||||
// Request editor page
|
// Request editor page
|
||||||
req := NewRequest(t, "GET", "/user2/repo1/_new/master/")
|
req := NewRequest(t, "GET", "/user2/repo1/_new/master/")
|
||||||
resp := session.MakeRequest(t, req)
|
resp := session.MakeRequest(t, req)
|
||||||
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
||||||
|
|
||||||
doc, err := NewHtmlParser(resp.Body)
|
doc := NewHtmlParser(t, resp.Body)
|
||||||
assert.NoError(t, err)
|
|
||||||
lastCommit := doc.GetInputValueByName("last_commit")
|
lastCommit := doc.GetInputValueByName("last_commit")
|
||||||
assert.NotEmpty(t, lastCommit)
|
assert.NotEmpty(t, lastCommit)
|
||||||
|
|
||||||
// Save new file to master branch
|
// Save new file to master branch
|
||||||
req = NewRequestBody(t, "POST", "/user2/repo1/_new/master/",
|
req = NewRequestWithValues(t, "POST", "/user2/repo1/_new/master/", map[string]string{
|
||||||
bytes.NewBufferString(url.Values{
|
"_csrf": doc.GetCSRF(),
|
||||||
"_csrf": []string{doc.GetInputValueByName("_csrf")},
|
"last_commit": lastCommit,
|
||||||
"last_commit": []string{lastCommit},
|
"tree_path": "test.txt",
|
||||||
"tree_path": []string{"test.txt"},
|
"content": "Content",
|
||||||
"content": []string{"Content"},
|
"commit_choice": "direct",
|
||||||
"commit_choice": []string{"direct"},
|
})
|
||||||
}.Encode()),
|
|
||||||
)
|
|
||||||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||||
resp = session.MakeRequest(t, req)
|
resp = session.MakeRequest(t, req)
|
||||||
assert.EqualValues(t, http.StatusFound, resp.HeaderCode)
|
assert.EqualValues(t, http.StatusFound, resp.HeaderCode)
|
||||||
|
@ -47,25 +42,21 @@ func TestCreateFile(t *testing.T) {
|
||||||
func TestCreateFileOnProtectedBranch(t *testing.T) {
|
func TestCreateFileOnProtectedBranch(t *testing.T) {
|
||||||
prepareTestEnv(t)
|
prepareTestEnv(t)
|
||||||
|
|
||||||
session := loginUser(t, "user2", "password")
|
session := loginUser(t, "user2")
|
||||||
|
|
||||||
// Open repository branch settings
|
// Open repository branch settings
|
||||||
req := NewRequest(t, "GET", "/user2/repo1/settings/branches")
|
req := NewRequest(t, "GET", "/user2/repo1/settings/branches")
|
||||||
resp := session.MakeRequest(t, req)
|
resp := session.MakeRequest(t, req)
|
||||||
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
||||||
|
|
||||||
doc, err := NewHtmlParser(resp.Body)
|
doc := NewHtmlParser(t, resp.Body)
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
// Change master branch to protected
|
// Change master branch to protected
|
||||||
req = NewRequestBody(t, "POST", "/user2/repo1/settings/branches?action=protected_branch",
|
req = NewRequestWithValues(t, "POST", "/user2/repo1/settings/branches?action=protected_branch", map[string]string{
|
||||||
bytes.NewBufferString(url.Values{
|
"_csrf": doc.GetCSRF(),
|
||||||
"_csrf": []string{doc.GetInputValueByName("_csrf")},
|
"branchName": "master",
|
||||||
"branchName": []string{"master"},
|
"canPush": "true",
|
||||||
"canPush": []string{"true"},
|
})
|
||||||
}.Encode()),
|
|
||||||
)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||||
resp = session.MakeRequest(t, req)
|
resp = session.MakeRequest(t, req)
|
||||||
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
||||||
|
@ -79,21 +70,19 @@ func TestCreateFileOnProtectedBranch(t *testing.T) {
|
||||||
resp = session.MakeRequest(t, req)
|
resp = session.MakeRequest(t, req)
|
||||||
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
||||||
|
|
||||||
doc, err = NewHtmlParser(resp.Body)
|
doc = NewHtmlParser(t, resp.Body)
|
||||||
assert.NoError(t, err)
|
|
||||||
lastCommit := doc.GetInputValueByName("last_commit")
|
lastCommit := doc.GetInputValueByName("last_commit")
|
||||||
assert.NotEmpty(t, lastCommit)
|
assert.NotEmpty(t, lastCommit)
|
||||||
|
|
||||||
// Save new file to master branch
|
// Save new file to master branch
|
||||||
req = NewRequestBody(t, "POST", "/user2/repo1/_new/master/",
|
req = NewRequestWithValues(t, "POST", "/user2/repo1/_new/master/", map[string]string{
|
||||||
bytes.NewBufferString(url.Values{
|
"_csrf": doc.GetCSRF(),
|
||||||
"_csrf": []string{doc.GetInputValueByName("_csrf")},
|
"last_commit": lastCommit,
|
||||||
"last_commit": []string{lastCommit},
|
"tree_path": "test.txt",
|
||||||
"tree_path": []string{"test.txt"},
|
"content": "Content",
|
||||||
"content": []string{"Content"},
|
"commit_choice": "direct",
|
||||||
"commit_choice": []string{"direct"},
|
})
|
||||||
}.Encode()),
|
|
||||||
)
|
|
||||||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||||
resp = session.MakeRequest(t, req)
|
resp = session.MakeRequest(t, req)
|
||||||
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
||||||
|
@ -110,20 +99,19 @@ func testEditFile(t *testing.T, session *TestSession, user, repo, branch, filePa
|
||||||
resp := session.MakeRequest(t, req)
|
resp := session.MakeRequest(t, req)
|
||||||
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
||||||
|
|
||||||
htmlDoc, err := NewHtmlParser(resp.Body)
|
htmlDoc := NewHtmlParser(t, resp.Body)
|
||||||
assert.NoError(t, err)
|
|
||||||
lastCommit := htmlDoc.GetInputValueByName("last_commit")
|
lastCommit := htmlDoc.GetInputValueByName("last_commit")
|
||||||
assert.NotEmpty(t, lastCommit)
|
assert.NotEmpty(t, lastCommit)
|
||||||
|
|
||||||
// Submit the edits
|
// Submit the edits
|
||||||
req = NewRequestBody(t, "POST", path.Join(user, repo, "_edit", branch, filePath),
|
req = NewRequestWithValues(t, "POST", path.Join(user, repo, "_edit", branch, filePath),
|
||||||
bytes.NewBufferString(url.Values{
|
map[string]string{
|
||||||
"_csrf": []string{htmlDoc.GetInputValueByName("_csrf")},
|
"_csrf": htmlDoc.GetCSRF(),
|
||||||
"last_commit": []string{lastCommit},
|
"last_commit": lastCommit,
|
||||||
"tree_path": []string{filePath},
|
"tree_path": filePath,
|
||||||
"content": []string{newContent},
|
"content": newContent,
|
||||||
"commit_choice": []string{"direct"},
|
"commit_choice": "direct",
|
||||||
}.Encode()),
|
},
|
||||||
)
|
)
|
||||||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||||
resp = session.MakeRequest(t, req)
|
resp = session.MakeRequest(t, req)
|
||||||
|
@ -140,6 +128,6 @@ func testEditFile(t *testing.T, session *TestSession, user, repo, branch, filePa
|
||||||
|
|
||||||
func TestEditFile(t *testing.T) {
|
func TestEditFile(t *testing.T) {
|
||||||
prepareTestEnv(t)
|
prepareTestEnv(t)
|
||||||
session := loginUser(t, "user2", "password")
|
session := loginUser(t, "user2")
|
||||||
testEditFile(t, session, "user2", "repo1", "master", "README.md")
|
testEditFile(t, session, "user2", "repo1", "master", "README.md")
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,21 +6,20 @@ package integrations
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
"github.com/PuerkitoBio/goquery"
|
"github.com/PuerkitoBio/goquery"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
type HtmlDoc struct {
|
type HtmlDoc struct {
|
||||||
doc *goquery.Document
|
doc *goquery.Document
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHtmlParser(content []byte) (*HtmlDoc, error) {
|
func NewHtmlParser(t *testing.T, content []byte) *HtmlDoc {
|
||||||
doc, err := goquery.NewDocumentFromReader(bytes.NewReader(content))
|
doc, err := goquery.NewDocumentFromReader(bytes.NewReader(content))
|
||||||
if err != nil {
|
assert.NoError(t, err)
|
||||||
return nil, err
|
return &HtmlDoc{doc: doc}
|
||||||
}
|
|
||||||
|
|
||||||
return &HtmlDoc{doc: doc}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (doc *HtmlDoc) GetInputValueById(id string) string {
|
func (doc *HtmlDoc) GetInputValueById(id string) string {
|
||||||
|
@ -32,3 +31,7 @@ func (doc *HtmlDoc) GetInputValueByName(name string) string {
|
||||||
text, _ := doc.doc.Find("input[name=\"" + name + "\"]").Attr("value")
|
text, _ := doc.doc.Find("input[name=\"" + name + "\"]").Attr("value")
|
||||||
return text
|
return text
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (doc *HtmlDoc) GetCSRF() string {
|
||||||
|
return doc.GetInputValueByName("_csrf")
|
||||||
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ package integrations
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
|
@ -155,21 +156,23 @@ func (s *TestSession) MakeRequest(t *testing.T, req *http.Request) *TestResponse
|
||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
|
|
||||||
func loginUser(t *testing.T, userName, password string) *TestSession {
|
const userPassword = "password"
|
||||||
|
|
||||||
|
func loginUser(t *testing.T, userName string) *TestSession {
|
||||||
|
return loginUserWithPassword(t, userName, userPassword)
|
||||||
|
}
|
||||||
|
|
||||||
|
func loginUserWithPassword(t *testing.T, userName, password string) *TestSession {
|
||||||
req := NewRequest(t, "GET", "/user/login")
|
req := NewRequest(t, "GET", "/user/login")
|
||||||
resp := MakeRequest(req)
|
resp := MakeRequest(req)
|
||||||
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
||||||
|
|
||||||
doc, err := NewHtmlParser(resp.Body)
|
doc := NewHtmlParser(t, resp.Body)
|
||||||
assert.NoError(t, err)
|
req = NewRequestWithValues(t, "POST", "/user/login", map[string]string{
|
||||||
|
"_csrf": doc.GetCSRF(),
|
||||||
req = NewRequestBody(t, "POST", "/user/login",
|
"user_name": userName,
|
||||||
bytes.NewBufferString(url.Values{
|
"password": password,
|
||||||
"_csrf": []string{doc.GetInputValueByName("_csrf")},
|
})
|
||||||
"user_name": []string{userName},
|
|
||||||
"password": []string{password},
|
|
||||||
}.Encode()),
|
|
||||||
)
|
|
||||||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||||
resp = MakeRequest(req)
|
resp = MakeRequest(req)
|
||||||
assert.EqualValues(t, http.StatusFound, resp.HeaderCode)
|
assert.EqualValues(t, http.StatusFound, resp.HeaderCode)
|
||||||
|
@ -211,14 +214,28 @@ type TestResponse struct {
|
||||||
Headers http.Header
|
Headers http.Header
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRequest(t *testing.T, method, url string) *http.Request {
|
func NewRequest(t *testing.T, method, urlStr string) *http.Request {
|
||||||
return NewRequestBody(t, method, url, nil)
|
return NewRequestWithBody(t, method, urlStr, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRequestBody(t *testing.T, method, url string, body io.Reader) *http.Request {
|
func NewRequestWithValues(t *testing.T, method, urlStr string, values map[string]string) *http.Request {
|
||||||
request, err := http.NewRequest(method, url, body)
|
urlValues := url.Values{}
|
||||||
|
for key, value := range values {
|
||||||
|
urlValues[key] = []string{value}
|
||||||
|
}
|
||||||
|
return NewRequestWithBody(t, method, urlStr, bytes.NewBufferString(urlValues.Encode()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRequestWithJSON(t *testing.T, method, urlStr string, v interface{}) *http.Request {
|
||||||
|
jsonBytes, err := json.Marshal(v)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
request.RequestURI = url
|
return NewRequestWithBody(t, method, urlStr, bytes.NewBuffer(jsonBytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRequestWithBody(t *testing.T, method, urlStr string, body io.Reader) *http.Request {
|
||||||
|
request, err := http.NewRequest(method, urlStr, body)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
request.RequestURI = urlStr
|
||||||
return request
|
return request
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,13 +45,12 @@ func TestNoLoginViewIssuesSortByType(t *testing.T) {
|
||||||
repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
|
repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
|
||||||
repo.Owner = models.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
|
repo.Owner = models.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
|
||||||
|
|
||||||
session := loginUser(t, user.Name, "password")
|
session := loginUser(t, user.Name)
|
||||||
req := NewRequest(t, "GET", repo.RelLink()+"/issues?type=created_by")
|
req := NewRequest(t, "GET", repo.RelLink()+"/issues?type=created_by")
|
||||||
resp := session.MakeRequest(t, req)
|
resp := session.MakeRequest(t, req)
|
||||||
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
||||||
|
|
||||||
htmlDoc, err := NewHtmlParser(resp.Body)
|
htmlDoc := NewHtmlParser(t, resp.Body)
|
||||||
assert.NoError(t, err)
|
|
||||||
issuesSelection := getIssuesSelection(htmlDoc)
|
issuesSelection := getIssuesSelection(htmlDoc)
|
||||||
expectedNumIssues := models.GetCount(t,
|
expectedNumIssues := models.GetCount(t,
|
||||||
&models.Issue{RepoID: repo.ID, PosterID: user.ID},
|
&models.Issue{RepoID: repo.ID, PosterID: user.ID},
|
||||||
|
|
|
@ -14,12 +14,11 @@ import (
|
||||||
func TestPullCompare(t *testing.T) {
|
func TestPullCompare(t *testing.T) {
|
||||||
prepareTestEnv(t)
|
prepareTestEnv(t)
|
||||||
|
|
||||||
session := loginUser(t, "user2", "password")
|
session := loginUser(t, "user2")
|
||||||
req := NewRequest(t, "GET", "/user2/repo1/pulls")
|
req := NewRequest(t, "GET", "/user2/repo1/pulls")
|
||||||
resp := session.MakeRequest(t, req)
|
resp := session.MakeRequest(t, req)
|
||||||
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
||||||
htmlDoc, err := NewHtmlParser(resp.Body)
|
htmlDoc := NewHtmlParser(t, resp.Body)
|
||||||
assert.NoError(t, err)
|
|
||||||
link, exists := htmlDoc.doc.Find(".navbar").Find(".ui.green.button").Attr("href")
|
link, exists := htmlDoc.doc.Find(".navbar").Find(".ui.green.button").Attr("href")
|
||||||
assert.True(t, exists, "The template has changed")
|
assert.True(t, exists, "The template has changed")
|
||||||
|
|
||||||
|
|
|
@ -5,9 +5,7 @@
|
||||||
package integrations
|
package integrations
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
"path"
|
"path"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -20,8 +18,7 @@ func testPullCreate(t *testing.T, session *TestSession, user, repo, branch strin
|
||||||
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
||||||
|
|
||||||
// Click the little green button to create a pull
|
// Click the little green button to create a pull
|
||||||
htmlDoc, err := NewHtmlParser(resp.Body)
|
htmlDoc := NewHtmlParser(t, resp.Body)
|
||||||
assert.NoError(t, err)
|
|
||||||
link, exists := htmlDoc.doc.Find("button.ui.green.small.button").Parent().Attr("href")
|
link, exists := htmlDoc.doc.Find("button.ui.green.small.button").Parent().Attr("href")
|
||||||
assert.True(t, exists, "The template has changed")
|
assert.True(t, exists, "The template has changed")
|
||||||
|
|
||||||
|
@ -30,16 +27,13 @@ func testPullCreate(t *testing.T, session *TestSession, user, repo, branch strin
|
||||||
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
||||||
|
|
||||||
// Submit the form for creating the pull
|
// Submit the form for creating the pull
|
||||||
htmlDoc, err = NewHtmlParser(resp.Body)
|
htmlDoc = NewHtmlParser(t, resp.Body)
|
||||||
assert.NoError(t, err)
|
|
||||||
link, exists = htmlDoc.doc.Find("form.ui.form").Attr("action")
|
link, exists = htmlDoc.doc.Find("form.ui.form").Attr("action")
|
||||||
assert.True(t, exists, "The template has changed")
|
assert.True(t, exists, "The template has changed")
|
||||||
req = NewRequestBody(t, "POST", link,
|
req = NewRequestWithValues(t, "POST", link, map[string]string{
|
||||||
bytes.NewBufferString(url.Values{
|
"_csrf": htmlDoc.GetCSRF(),
|
||||||
"_csrf": []string{htmlDoc.GetInputValueByName("_csrf")},
|
"title": "This is a pull title",
|
||||||
"title": []string{"This is a pull title"},
|
})
|
||||||
}.Encode()),
|
|
||||||
)
|
|
||||||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||||
resp = session.MakeRequest(t, req)
|
resp = session.MakeRequest(t, req)
|
||||||
assert.EqualValues(t, http.StatusFound, resp.HeaderCode)
|
assert.EqualValues(t, http.StatusFound, resp.HeaderCode)
|
||||||
|
@ -51,7 +45,7 @@ func testPullCreate(t *testing.T, session *TestSession, user, repo, branch strin
|
||||||
|
|
||||||
func TestPullCreate(t *testing.T) {
|
func TestPullCreate(t *testing.T) {
|
||||||
prepareTestEnv(t)
|
prepareTestEnv(t)
|
||||||
session := loginUser(t, "user1", "password")
|
session := loginUser(t, "user1")
|
||||||
testRepoFork(t, session)
|
testRepoFork(t, session)
|
||||||
testEditFile(t, session, "user1", "repo1", "master", "README.md")
|
testEditFile(t, session, "user1", "repo1", "master", "README.md")
|
||||||
testPullCreate(t, session, "user1", "repo1", "master")
|
testPullCreate(t, session, "user1", "repo1", "master")
|
||||||
|
|
|
@ -5,9 +5,7 @@
|
||||||
package integrations
|
package integrations
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -21,15 +19,12 @@ func testPullMerge(t *testing.T, session *TestSession, user, repo, pullnum strin
|
||||||
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
||||||
|
|
||||||
// Click the little green button to craete a pull
|
// Click the little green button to craete a pull
|
||||||
htmlDoc, err := NewHtmlParser(resp.Body)
|
htmlDoc := NewHtmlParser(t, resp.Body)
|
||||||
assert.NoError(t, err)
|
|
||||||
link, exists := htmlDoc.doc.Find("form.ui.form>button.ui.green.button").Parent().Attr("action")
|
link, exists := htmlDoc.doc.Find("form.ui.form>button.ui.green.button").Parent().Attr("action")
|
||||||
assert.True(t, exists, "The template has changed")
|
assert.True(t, exists, "The template has changed")
|
||||||
req = NewRequestBody(t, "POST", link,
|
req = NewRequestWithValues(t, "POST", link, map[string]string{
|
||||||
bytes.NewBufferString(url.Values{
|
"_csrf": htmlDoc.GetCSRF(),
|
||||||
"_csrf": []string{htmlDoc.GetInputValueByName("_csrf")},
|
})
|
||||||
}.Encode()),
|
|
||||||
)
|
|
||||||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||||
resp = session.MakeRequest(t, req)
|
resp = session.MakeRequest(t, req)
|
||||||
assert.EqualValues(t, http.StatusFound, resp.HeaderCode)
|
assert.EqualValues(t, http.StatusFound, resp.HeaderCode)
|
||||||
|
@ -39,7 +34,7 @@ func testPullMerge(t *testing.T, session *TestSession, user, repo, pullnum strin
|
||||||
|
|
||||||
func TestPullMerge(t *testing.T) {
|
func TestPullMerge(t *testing.T) {
|
||||||
prepareTestEnv(t)
|
prepareTestEnv(t)
|
||||||
session := loginUser(t, "user1", "password")
|
session := loginUser(t, "user1")
|
||||||
testRepoFork(t, session)
|
testRepoFork(t, session)
|
||||||
testEditFile(t, session, "user1", "repo1", "master", "README.md")
|
testEditFile(t, session, "user1", "repo1", "master", "README.md")
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ import (
|
||||||
func TestViewReleases(t *testing.T) {
|
func TestViewReleases(t *testing.T) {
|
||||||
prepareTestEnv(t)
|
prepareTestEnv(t)
|
||||||
|
|
||||||
session := loginUser(t, "user2", "password")
|
session := loginUser(t, "user2")
|
||||||
req := NewRequest(t, "GET", "/user2/repo1/releases")
|
req := NewRequest(t, "GET", "/user2/repo1/releases")
|
||||||
resp := session.MakeRequest(t, req)
|
resp := session.MakeRequest(t, req)
|
||||||
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
||||||
|
|
|
@ -5,26 +5,26 @@
|
||||||
package integrations
|
package integrations
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"path"
|
"path"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
api "code.gitea.io/sdk/gitea"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRepoCommits(t *testing.T) {
|
func TestRepoCommits(t *testing.T) {
|
||||||
prepareTestEnv(t)
|
prepareTestEnv(t)
|
||||||
|
|
||||||
session := loginUser(t, "user2", "password")
|
session := loginUser(t, "user2")
|
||||||
|
|
||||||
// Request repository commits page
|
// Request repository commits page
|
||||||
req := NewRequest(t, "GET", "/user2/repo1/commits/master")
|
req := NewRequest(t, "GET", "/user2/repo1/commits/master")
|
||||||
resp := session.MakeRequest(t, req)
|
resp := session.MakeRequest(t, req)
|
||||||
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
||||||
|
|
||||||
doc, err := NewHtmlParser(resp.Body)
|
doc := NewHtmlParser(t, resp.Body)
|
||||||
assert.NoError(t, err)
|
|
||||||
commitURL, exists := doc.doc.Find("#commits-table tbody tr td.sha a").Attr("href")
|
commitURL, exists := doc.doc.Find("#commits-table tbody tr td.sha a").Attr("href")
|
||||||
assert.True(t, exists)
|
assert.True(t, exists)
|
||||||
assert.NotEmpty(t, commitURL)
|
assert.NotEmpty(t, commitURL)
|
||||||
|
@ -33,23 +33,28 @@ func TestRepoCommits(t *testing.T) {
|
||||||
func doTestRepoCommitWithStatus(t *testing.T, state string, classes ...string) {
|
func doTestRepoCommitWithStatus(t *testing.T, state string, classes ...string) {
|
||||||
prepareTestEnv(t)
|
prepareTestEnv(t)
|
||||||
|
|
||||||
session := loginUser(t, "user2", "password")
|
session := loginUser(t, "user2")
|
||||||
|
|
||||||
// Request repository commits page
|
// Request repository commits page
|
||||||
req := NewRequest(t, "GET", "/user2/repo1/commits/master")
|
req := NewRequest(t, "GET", "/user2/repo1/commits/master")
|
||||||
resp := session.MakeRequest(t, req)
|
resp := session.MakeRequest(t, req)
|
||||||
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
||||||
|
|
||||||
doc, err := NewHtmlParser(resp.Body)
|
doc := NewHtmlParser(t, resp.Body)
|
||||||
assert.NoError(t, err)
|
|
||||||
// Get first commit URL
|
// Get first commit URL
|
||||||
commitURL, exists := doc.doc.Find("#commits-table tbody tr td.sha a").Attr("href")
|
commitURL, exists := doc.doc.Find("#commits-table tbody tr td.sha a").Attr("href")
|
||||||
assert.True(t, exists)
|
assert.True(t, exists)
|
||||||
assert.NotEmpty(t, commitURL)
|
assert.NotEmpty(t, commitURL)
|
||||||
|
|
||||||
// Call API to add status for commit
|
// Call API to add status for commit
|
||||||
req = NewRequestBody(t, "POST", "/api/v1/repos/user2/repo1/statuses/"+path.Base(commitURL),
|
req = NewRequestWithJSON(t, "POST", "/api/v1/repos/user2/repo1/statuses/"+path.Base(commitURL),
|
||||||
bytes.NewBufferString("{\"state\":\""+state+"\", \"target_url\": \"http://test.ci/\", \"description\": \"\", \"context\": \"testci\"}"))
|
api.CreateStatusOption{
|
||||||
|
State: api.StatusState(state),
|
||||||
|
TargetURL: "http://test.ci/",
|
||||||
|
Description: "",
|
||||||
|
Context: "testci",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
req.Header.Add("Content-Type", "application/json")
|
req.Header.Add("Content-Type", "application/json")
|
||||||
resp = session.MakeRequest(t, req)
|
resp = session.MakeRequest(t, req)
|
||||||
|
@ -59,8 +64,7 @@ func doTestRepoCommitWithStatus(t *testing.T, state string, classes ...string) {
|
||||||
resp = session.MakeRequest(t, req)
|
resp = session.MakeRequest(t, req)
|
||||||
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
||||||
|
|
||||||
doc, err = NewHtmlParser(resp.Body)
|
doc = NewHtmlParser(t, resp.Body)
|
||||||
assert.NoError(t, err)
|
|
||||||
// Check if commit status is displayed in message column
|
// Check if commit status is displayed in message column
|
||||||
sel := doc.doc.Find("#commits-table tbody tr td.message i.commit-status")
|
sel := doc.doc.Find("#commits-table tbody tr td.message i.commit-status")
|
||||||
assert.Equal(t, sel.Length(), 1)
|
assert.Equal(t, sel.Length(), 1)
|
||||||
|
|
|
@ -5,9 +5,7 @@
|
||||||
package integrations
|
package integrations
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
@ -25,8 +23,7 @@ func testRepoFork(t *testing.T, session *TestSession) *TestResponse {
|
||||||
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
||||||
|
|
||||||
// Step2: click the fork button
|
// Step2: click the fork button
|
||||||
htmlDoc, err := NewHtmlParser(resp.Body)
|
htmlDoc := NewHtmlParser(t, resp.Body)
|
||||||
assert.NoError(t, err)
|
|
||||||
link, exists := htmlDoc.doc.Find("a.ui.button[href^=\"/repo/fork/\"]").Attr("href")
|
link, exists := htmlDoc.doc.Find("a.ui.button[href^=\"/repo/fork/\"]").Attr("href")
|
||||||
assert.True(t, exists, "The template has changed")
|
assert.True(t, exists, "The template has changed")
|
||||||
req = NewRequest(t, "GET", link)
|
req = NewRequest(t, "GET", link)
|
||||||
|
@ -34,17 +31,14 @@ func testRepoFork(t *testing.T, session *TestSession) *TestResponse {
|
||||||
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
||||||
|
|
||||||
// Step3: fill the form of the forking
|
// Step3: fill the form of the forking
|
||||||
htmlDoc, err = NewHtmlParser(resp.Body)
|
htmlDoc = NewHtmlParser(t, resp.Body)
|
||||||
assert.NoError(t, err)
|
|
||||||
link, exists = htmlDoc.doc.Find("form.ui.form[action^=\"/repo/fork/\"]").Attr("action")
|
link, exists = htmlDoc.doc.Find("form.ui.form[action^=\"/repo/fork/\"]").Attr("action")
|
||||||
assert.True(t, exists, "The template has changed")
|
assert.True(t, exists, "The template has changed")
|
||||||
req = NewRequestBody(t, "POST", link,
|
req = NewRequestWithValues(t, "POST", link, map[string]string{
|
||||||
bytes.NewBufferString(url.Values{
|
"_csrf": htmlDoc.GetCSRF(),
|
||||||
"_csrf": []string{htmlDoc.GetInputValueByName("_csrf")},
|
"uid": "1",
|
||||||
"uid": []string{"1"},
|
"repo_name": "repo1",
|
||||||
"repo_name": []string{"repo1"},
|
})
|
||||||
}.Encode()),
|
|
||||||
)
|
|
||||||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||||
resp = session.MakeRequest(t, req)
|
resp = session.MakeRequest(t, req)
|
||||||
assert.EqualValues(t, http.StatusFound, resp.HeaderCode)
|
assert.EqualValues(t, http.StatusFound, resp.HeaderCode)
|
||||||
|
@ -59,6 +53,6 @@ func testRepoFork(t *testing.T, session *TestSession) *TestResponse {
|
||||||
|
|
||||||
func TestRepoFork(t *testing.T) {
|
func TestRepoFork(t *testing.T) {
|
||||||
prepareTestEnv(t)
|
prepareTestEnv(t)
|
||||||
session := loginUser(t, "user1", "password")
|
session := loginUser(t, "user1")
|
||||||
testRepoFork(t, session)
|
testRepoFork(t, session)
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ func TestViewRepo(t *testing.T) {
|
||||||
resp = MakeRequest(req)
|
resp = MakeRequest(req)
|
||||||
assert.EqualValues(t, http.StatusNotFound, resp.HeaderCode)
|
assert.EqualValues(t, http.StatusNotFound, resp.HeaderCode)
|
||||||
|
|
||||||
session := loginUser(t, "user1", "password")
|
session := loginUser(t, "user1")
|
||||||
resp = session.MakeRequest(t, req)
|
resp = session.MakeRequest(t, req)
|
||||||
assert.EqualValues(t, http.StatusNotFound, resp.HeaderCode)
|
assert.EqualValues(t, http.StatusNotFound, resp.HeaderCode)
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ func TestViewRepo2(t *testing.T) {
|
||||||
prepareTestEnv(t)
|
prepareTestEnv(t)
|
||||||
|
|
||||||
req := NewRequest(t, "GET", "/user3/repo3")
|
req := NewRequest(t, "GET", "/user3/repo3")
|
||||||
session := loginUser(t, "user2", "password")
|
session := loginUser(t, "user2")
|
||||||
resp := session.MakeRequest(t, req)
|
resp := session.MakeRequest(t, req)
|
||||||
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ func TestViewRepo3(t *testing.T) {
|
||||||
prepareTestEnv(t)
|
prepareTestEnv(t)
|
||||||
|
|
||||||
req := NewRequest(t, "GET", "/user3/repo3")
|
req := NewRequest(t, "GET", "/user3/repo3")
|
||||||
session := loginUser(t, "user3", "password")
|
session := loginUser(t, "user3")
|
||||||
resp := session.MakeRequest(t, req)
|
resp := session.MakeRequest(t, req)
|
||||||
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
assert.EqualValues(t, http.StatusOK, resp.HeaderCode)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,9 +5,7 @@
|
||||||
package integrations
|
package integrations
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
@ -20,14 +18,12 @@ func TestSignup(t *testing.T) {
|
||||||
|
|
||||||
setting.Service.EnableCaptcha = false
|
setting.Service.EnableCaptcha = false
|
||||||
|
|
||||||
req := NewRequestBody(t, "POST", "/user/sign_up",
|
req := NewRequestWithValues(t, "POST", "/user/sign_up", map[string]string{
|
||||||
bytes.NewBufferString(url.Values{
|
"user_name": "exampleUser",
|
||||||
"user_name": []string{"exampleUser"},
|
"email": "exampleUser@example.com",
|
||||||
"email": []string{"exampleUser@example.com"},
|
"password": "examplePassword",
|
||||||
"password": []string{"examplePassword"},
|
"retype": "examplePassword",
|
||||||
"retype": []string{"examplePassword"},
|
})
|
||||||
}.Encode()),
|
|
||||||
)
|
|
||||||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||||
resp := MakeRequest(req)
|
resp := MakeRequest(req)
|
||||||
assert.EqualValues(t, http.StatusFound, resp.HeaderCode)
|
assert.EqualValues(t, http.StatusFound, resp.HeaderCode)
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# NOTE: all users should have a password of "password"
|
||||||
|
|
||||||
- # NOTE: this user (id=1) is the admin
|
- # NOTE: this user (id=1) is the admin
|
||||||
id: 1
|
id: 1
|
||||||
lower_name: user1
|
lower_name: user1
|
||||||
|
|
Loading…
Reference in a new issue