cli/pkg/cmd/issue/list/list_test.go
2021-08-25 12:41:30 +02:00

523 lines
14 KiB
Go

package list
import (
"bytes"
"io/ioutil"
"net/http"
"regexp"
"testing"
"github.com/MakeNowJust/heredoc"
"github.com/cli/cli/v2/internal/config"
"github.com/cli/cli/v2/internal/ghrepo"
"github.com/cli/cli/v2/internal/run"
prShared "github.com/cli/cli/v2/pkg/cmd/pr/shared"
"github.com/cli/cli/v2/pkg/cmdutil"
"github.com/cli/cli/v2/pkg/httpmock"
"github.com/cli/cli/v2/pkg/iostreams"
"github.com/cli/cli/v2/test"
"github.com/google/shlex"
"github.com/stretchr/testify/assert"
)
func runCommand(rt http.RoundTripper, isTTY bool, cli string) (*test.CmdOut, error) {
io, _, stdout, stderr := iostreams.Test()
io.SetStdoutTTY(isTTY)
io.SetStdinTTY(isTTY)
io.SetStderrTTY(isTTY)
factory := &cmdutil.Factory{
IOStreams: io,
HttpClient: func() (*http.Client, error) {
return &http.Client{Transport: rt}, nil
},
Config: func() (config.Config, error) {
return config.NewBlankConfig(), nil
},
BaseRepo: func() (ghrepo.Interface, error) {
return ghrepo.New("OWNER", "REPO"), nil
},
}
cmd := NewCmdList(factory, nil)
argv, err := shlex.Split(cli)
if err != nil {
return nil, err
}
cmd.SetArgs(argv)
cmd.SetIn(&bytes.Buffer{})
cmd.SetOut(ioutil.Discard)
cmd.SetErr(ioutil.Discard)
_, err = cmd.ExecuteC()
return &test.CmdOut{
OutBuf: stdout,
ErrBuf: stderr,
}, err
}
func TestIssueList_nontty(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)
http.Register(
httpmock.GraphQL(`query IssueList\b`),
httpmock.FileResponse("./fixtures/issueList.json"))
output, err := runCommand(http, false, "")
if err != nil {
t.Errorf("error running command `issue list`: %v", err)
}
assert.Equal(t, "", output.Stderr())
//nolint:staticcheck // prefer exact matchers over ExpectLines
test.ExpectLines(t, output.String(),
`1[\t]+number won[\t]+label[\t]+\d+`,
`2[\t]+number too[\t]+label[\t]+\d+`,
`4[\t]+number fore[\t]+label[\t]+\d+`)
}
func TestIssueList_tty(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)
http.Register(
httpmock.GraphQL(`query IssueList\b`),
httpmock.FileResponse("./fixtures/issueList.json"))
output, err := runCommand(http, true, "")
if err != nil {
t.Errorf("error running command `issue list`: %v", err)
}
out := output.String()
timeRE := regexp.MustCompile(`\d+ years`)
out = timeRE.ReplaceAllString(out, "X years")
assert.Equal(t, heredoc.Doc(`
Showing 3 of 3 open issues in OWNER/REPO
#1 number won label about X years ago
#2 number too label about X years ago
#4 number fore label about X years ago
`), out)
assert.Equal(t, ``, output.Stderr())
}
func TestIssueList_tty_withFlags(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)
http.Register(
httpmock.GraphQL(`query IssueList\b`),
httpmock.GraphQLQuery(`
{ "data": { "repository": {
"hasIssuesEnabled": true,
"issues": { "nodes": [] }
} } }`, func(_ string, params map[string]interface{}) {
assert.Equal(t, "probablyCher", params["assignee"].(string))
assert.Equal(t, "foo", params["author"].(string))
assert.Equal(t, "me", params["mention"].(string))
assert.Equal(t, "12345", params["milestone"].(string))
assert.Equal(t, []interface{}{"OPEN"}, params["states"].([]interface{}))
}))
http.Register(
httpmock.GraphQL(`query RepositoryMilestoneList\b`),
httpmock.StringResponse(`
{ "data": { "repository": { "milestones": {
"nodes": [{ "title":"1.x", "id": "MDk6TWlsZXN0b25lMTIzNDU=" }],
"pageInfo": { "hasNextPage": false }
} } } }
`))
output, err := runCommand(http, true, "-a probablyCher -s open -A foo --mention me --milestone 1.x")
if err != nil {
t.Errorf("error running command `issue list`: %v", err)
}
assert.Equal(t, "", output.Stderr())
assert.Equal(t, `
No issues match your search in OWNER/REPO
`, output.String())
}
func TestIssueList_withInvalidLimitFlag(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)
_, err := runCommand(http, true, "--limit=0")
if err == nil || err.Error() != "invalid limit: 0" {
t.Errorf("error running command `issue list`: %v", err)
}
}
func TestIssueList_disabledIssues(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)
http.Register(
httpmock.GraphQL(`query IssueList\b`),
httpmock.StringResponse(`
{ "data": { "repository": {
"hasIssuesEnabled": false
} } }`),
)
_, err := runCommand(http, true, "")
if err == nil || err.Error() != "the 'OWNER/REPO' repository has disabled issues" {
t.Errorf("error running command `issue list`: %v", err)
}
}
func TestIssueList_web(t *testing.T) {
io, _, stdout, stderr := iostreams.Test()
io.SetStdoutTTY(true)
io.SetStderrTTY(true)
browser := &cmdutil.TestBrowser{}
reg := &httpmock.Registry{}
defer reg.Verify(t)
_, cmdTeardown := run.Stub()
defer cmdTeardown(t)
err := listRun(&ListOptions{
IO: io,
Browser: browser,
HttpClient: func() (*http.Client, error) {
return &http.Client{Transport: reg}, nil
},
BaseRepo: func() (ghrepo.Interface, error) {
return ghrepo.New("OWNER", "REPO"), nil
},
WebMode: true,
State: "all",
Assignee: "peter",
Author: "john",
Labels: []string{"bug", "docs"},
Mention: "frank",
Milestone: "v1.1",
LimitResults: 10,
})
if err != nil {
t.Errorf("error running command `issue list` with `--web` flag: %v", err)
}
assert.Equal(t, "", stdout.String())
assert.Equal(t, "Opening github.com/OWNER/REPO/issues in your browser.\n", stderr.String())
browser.Verify(t, "https://github.com/OWNER/REPO/issues?q=is%3Aissue+assignee%3Apeter+label%3Abug+label%3Adocs+author%3Ajohn+mentions%3Afrank+milestone%3Av1.1")
}
func Test_issueList(t *testing.T) {
type args struct {
repo ghrepo.Interface
filters prShared.FilterOptions
limit int
}
tests := []struct {
name string
args args
httpStubs func(*httpmock.Registry)
wantErr bool
}{
{
name: "default",
args: args{
limit: 30,
repo: ghrepo.New("OWNER", "REPO"),
filters: prShared.FilterOptions{
Entity: "issue",
State: "open",
},
},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.GraphQL(`query IssueList\b`),
httpmock.GraphQLQuery(`
{ "data": { "repository": {
"hasIssuesEnabled": true,
"issues": { "nodes": [] }
} } }`, func(_ string, params map[string]interface{}) {
assert.Equal(t, map[string]interface{}{
"owner": "OWNER",
"repo": "REPO",
"limit": float64(30),
"states": []interface{}{"OPEN"},
}, params)
}))
},
},
{
name: "milestone by number",
args: args{
limit: 30,
repo: ghrepo.New("OWNER", "REPO"),
filters: prShared.FilterOptions{
Entity: "issue",
State: "open",
Milestone: "13",
},
},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.GraphQL(`query RepositoryMilestoneByNumber\b`),
httpmock.StringResponse(`
{ "data": { "repository": { "milestone": {
"id": "MDk6TWlsZXN0b25lMTIzNDU="
} } } }
`))
reg.Register(
httpmock.GraphQL(`query IssueList\b`),
httpmock.GraphQLQuery(`
{ "data": { "repository": {
"hasIssuesEnabled": true,
"issues": { "nodes": [] }
} } }`, func(_ string, params map[string]interface{}) {
assert.Equal(t, map[string]interface{}{
"owner": "OWNER",
"repo": "REPO",
"limit": float64(30),
"states": []interface{}{"OPEN"},
"milestone": "12345",
}, params)
}))
},
},
{
name: "milestone by number with search",
args: args{
limit: 30,
repo: ghrepo.New("OWNER", "REPO"),
filters: prShared.FilterOptions{
Entity: "issue",
State: "open",
Milestone: "13",
Search: "auth bug",
},
},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.GraphQL(`query RepositoryMilestoneByNumber\b`),
httpmock.StringResponse(`
{ "data": { "repository": { "milestone": {
"title": "Big 1.0",
"id": "MDk6TWlsZXN0b25lMTIzNDU="
} } } }
`))
reg.Register(
httpmock.GraphQL(`query IssueSearch\b`),
httpmock.GraphQLQuery(`
{ "data": {
"repository": { "hasIssuesEnabled": true },
"search": {
"issueCount": 0,
"nodes": []
}
} }`, func(_ string, params map[string]interface{}) {
assert.Equal(t, map[string]interface{}{
"owner": "OWNER",
"repo": "REPO",
"limit": float64(30),
"query": "repo:OWNER/REPO is:issue is:open milestone:\"Big 1.0\" auth bug",
"type": "ISSUE",
}, params)
}))
},
},
{
name: "milestone by title with search",
args: args{
limit: 30,
repo: ghrepo.New("OWNER", "REPO"),
filters: prShared.FilterOptions{
Entity: "issue",
State: "open",
Milestone: "Big 1.0",
Search: "auth bug",
},
},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.GraphQL(`query IssueSearch\b`),
httpmock.GraphQLQuery(`
{ "data": {
"repository": { "hasIssuesEnabled": true },
"search": {
"issueCount": 0,
"nodes": []
}
} }`, func(_ string, params map[string]interface{}) {
assert.Equal(t, map[string]interface{}{
"owner": "OWNER",
"repo": "REPO",
"limit": float64(30),
"query": "repo:OWNER/REPO is:issue is:open milestone:\"Big 1.0\" auth bug",
"type": "ISSUE",
}, params)
}))
},
},
{
name: "milestone by title",
args: args{
limit: 30,
repo: ghrepo.New("OWNER", "REPO"),
filters: prShared.FilterOptions{
Entity: "issue",
State: "open",
Milestone: "1.x",
},
},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.GraphQL(`query RepositoryMilestoneList\b`),
httpmock.StringResponse(`
{ "data": { "repository": { "milestones": {
"nodes": [{ "title":"1.x", "id": "MDk6TWlsZXN0b25lMTIzNDU=" }],
"pageInfo": { "hasNextPage": false }
} } } }
`))
reg.Register(
httpmock.GraphQL(`query IssueList\b`),
httpmock.GraphQLQuery(`
{ "data": { "repository": {
"hasIssuesEnabled": true,
"issues": { "nodes": [] }
} } }`, func(_ string, params map[string]interface{}) {
assert.Equal(t, map[string]interface{}{
"owner": "OWNER",
"repo": "REPO",
"limit": float64(30),
"states": []interface{}{"OPEN"},
"milestone": "12345",
}, params)
}))
},
},
{
name: "@me syntax",
args: args{
limit: 30,
repo: ghrepo.New("OWNER", "REPO"),
filters: prShared.FilterOptions{
Entity: "issue",
State: "open",
Author: "@me",
Assignee: "@me",
Mention: "@me",
},
},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.GraphQL(`query UserCurrent\b`),
httpmock.StringResponse(`{"data": {"viewer": {"login": "monalisa"} } }`))
reg.Register(
httpmock.GraphQL(`query IssueList\b`),
httpmock.GraphQLQuery(`
{ "data": { "repository": {
"hasIssuesEnabled": true,
"issues": { "nodes": [] }
} } }`, func(_ string, params map[string]interface{}) {
assert.Equal(t, map[string]interface{}{
"owner": "OWNER",
"repo": "REPO",
"limit": float64(30),
"states": []interface{}{"OPEN"},
"assignee": "monalisa",
"author": "monalisa",
"mention": "monalisa",
}, params)
}))
},
},
{
name: "@me with search",
args: args{
limit: 30,
repo: ghrepo.New("OWNER", "REPO"),
filters: prShared.FilterOptions{
Entity: "issue",
State: "open",
Author: "@me",
Assignee: "@me",
Mention: "@me",
Search: "auth bug",
},
},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.GraphQL(`query IssueSearch\b`),
httpmock.GraphQLQuery(`
{ "data": {
"repository": { "hasIssuesEnabled": true },
"search": {
"issueCount": 0,
"nodes": []
}
} }`, func(_ string, params map[string]interface{}) {
assert.Equal(t, map[string]interface{}{
"owner": "OWNER",
"repo": "REPO",
"limit": float64(30),
"query": "repo:OWNER/REPO is:issue is:open assignee:@me author:@me mentions:@me auth bug",
"type": "ISSUE",
}, params)
}))
},
},
{
name: "with labels",
args: args{
limit: 30,
repo: ghrepo.New("OWNER", "REPO"),
filters: prShared.FilterOptions{
Entity: "issue",
State: "open",
Labels: []string{"hello", "one world"},
},
},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.GraphQL(`query IssueSearch\b`),
httpmock.GraphQLQuery(`
{ "data": {
"repository": { "hasIssuesEnabled": true },
"search": {
"issueCount": 0,
"nodes": []
}
} }`, func(_ string, params map[string]interface{}) {
assert.Equal(t, map[string]interface{}{
"owner": "OWNER",
"repo": "REPO",
"limit": float64(30),
"query": `repo:OWNER/REPO is:issue is:open label:hello label:"one world"`,
"type": "ISSUE",
}, params)
}))
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
httpreg := &httpmock.Registry{}
defer httpreg.Verify(t)
if tt.httpStubs != nil {
tt.httpStubs(httpreg)
}
client := &http.Client{Transport: httpreg}
_, err := issueList(client, tt.args.repo, tt.args.filters, tt.args.limit)
if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
})
}
}