test: add comprehensive tests for Issues 2.0 features
Create tests (11 new): - Flag parsing: --type, --parent (number/URL), --blocked-by, --blocking - Behavior: type resolution + mutation, type not found error, parent resolution + addSubIssue, blocked-by/blocking with swapped args verification, GHES unsupported error Edit tests (18 new): - Flag parsing: --type, --set-parent, --remove-parent, mutual exclusivity, --add-sub-issue, --remove-sub-issue, --add-blocked-by, --remove-blocked-by, --add-blocking - Behavior: type edit, set/remove parent, add/remove sub-issues, add/remove blocked-by, add-blocking with swapped args, batch edit type across multiple issues - Bug fix: copy SetParent value into Editable.Parent.Value View tests (5 new): - TTY: full view with all Issues 2.0 fields, regression test with no new fields - JSON: issueType export, parent/subIssues/subIssuesSummary export, blockedBy/blocking export Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
parent
67f63a1096
commit
4c12393b88
4 changed files with 1073 additions and 0 deletions
|
|
@ -1262,6 +1262,348 @@ func TestIssueCreate_projectsV2(t *testing.T) {
|
|||
|
||||
// TODO projectsV1Deprecation
|
||||
// Remove this test.
|
||||
func TestNewCmdCreate_issuesV2Flags(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
cli string
|
||||
wantsErr bool
|
||||
wantsType string
|
||||
wantsParent string
|
||||
wantsBlocking []string
|
||||
wantsBlockBy []string
|
||||
}{
|
||||
{
|
||||
name: "type flag",
|
||||
cli: `-t mytitle -b mybody --type Bug`,
|
||||
wantsType: "Bug",
|
||||
},
|
||||
{
|
||||
name: "parent flag with number",
|
||||
cli: `-t mytitle -b mybody --parent 100`,
|
||||
wantsParent: "100",
|
||||
},
|
||||
{
|
||||
name: "parent flag with URL",
|
||||
cli: `-t mytitle -b mybody --parent https://github.com/cli/go-gh/issues/42`,
|
||||
wantsParent: "https://github.com/cli/go-gh/issues/42",
|
||||
},
|
||||
{
|
||||
name: "blocked-by flag",
|
||||
cli: `-t mytitle -b mybody --blocked-by 200,201`,
|
||||
wantsBlockBy: []string{"200", "201"},
|
||||
},
|
||||
{
|
||||
name: "blocking flag",
|
||||
cli: `-t mytitle -b mybody --blocking 300`,
|
||||
wantsBlocking: []string{"300"},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ios, _, _, _ := iostreams.Test()
|
||||
|
||||
f := &cmdutil.Factory{
|
||||
IOStreams: ios,
|
||||
Config: func() (gh.Config, error) {
|
||||
return config.NewBlankConfig(), nil
|
||||
},
|
||||
}
|
||||
|
||||
var opts *CreateOptions
|
||||
cmd := NewCmdCreate(f, func(o *CreateOptions) error {
|
||||
opts = o
|
||||
return nil
|
||||
})
|
||||
|
||||
args, err := shlex.Split(tt.cli)
|
||||
require.NoError(t, err)
|
||||
cmd.SetArgs(args)
|
||||
cmd.SetOut(io.Discard)
|
||||
cmd.SetErr(io.Discard)
|
||||
_, err = cmd.ExecuteC()
|
||||
if tt.wantsErr {
|
||||
assert.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, tt.wantsType, opts.IssueType)
|
||||
assert.Equal(t, tt.wantsParent, opts.Parent)
|
||||
assert.Equal(t, tt.wantsBlocking, opts.Blocking)
|
||||
assert.Equal(t, tt.wantsBlockBy, opts.BlockedBy)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_createRun_issuesV2(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
opts CreateOptions
|
||||
httpStubs func(*testing.T, *httpmock.Registry)
|
||||
wantsStdout string
|
||||
wantsStderr string
|
||||
wantsErr string
|
||||
}{
|
||||
{
|
||||
name: "create with type",
|
||||
opts: CreateOptions{
|
||||
Detector: &fd.EnabledDetectorMock{},
|
||||
Title: "bug title",
|
||||
Body: "bug body",
|
||||
IssueType: "Bug",
|
||||
},
|
||||
httpStubs: func(t *testing.T, r *httpmock.Registry) {
|
||||
r.Register(
|
||||
httpmock.GraphQL(`query IssueRepositoryInfo\b`),
|
||||
httpmock.StringResponse(`
|
||||
{ "data": { "repository": {
|
||||
"id": "REPOID",
|
||||
"hasIssuesEnabled": true
|
||||
} } }`))
|
||||
r.Register(
|
||||
httpmock.GraphQL(`mutation IssueCreate\b`),
|
||||
httpmock.StringResponse(`
|
||||
{ "data": { "createIssue": { "issue": {
|
||||
"id": "ISSUE_ID_123",
|
||||
"URL": "https://github.com/OWNER/REPO/issues/123"
|
||||
} } } }`))
|
||||
r.Register(
|
||||
httpmock.GraphQL(`query RepositoryIssueTypes\b`),
|
||||
httpmock.StringResponse(`
|
||||
{ "data": { "repository": { "issueTypes": { "nodes": [
|
||||
{ "id": "IT_1", "name": "Bug", "description": "", "color": "d73a4a" },
|
||||
{ "id": "IT_2", "name": "Feature", "description": "", "color": "0075ca" },
|
||||
{ "id": "IT_3", "name": "Task", "description": "", "color": "e4e669" }
|
||||
] } } } }`))
|
||||
r.Register(
|
||||
httpmock.GraphQL(`mutation UpdateIssueIssueType\b`),
|
||||
httpmock.GraphQLMutation(`
|
||||
{ "data": { "updateIssueIssueType": { "issue": { "id": "ISSUE_ID_123" } } } }`,
|
||||
func(inputs map[string]interface{}) {
|
||||
assert.Equal(t, "ISSUE_ID_123", inputs["issueId"])
|
||||
assert.Equal(t, "IT_1", inputs["issueTypeId"])
|
||||
}))
|
||||
},
|
||||
wantsStdout: "https://github.com/OWNER/REPO/issues/123\n",
|
||||
wantsStderr: "\nCreating issue in OWNER/REPO\n\n",
|
||||
},
|
||||
{
|
||||
name: "create with type not found",
|
||||
opts: CreateOptions{
|
||||
Detector: &fd.EnabledDetectorMock{},
|
||||
Title: "bug title",
|
||||
Body: "bug body",
|
||||
IssueType: "Bugz",
|
||||
},
|
||||
httpStubs: func(t *testing.T, r *httpmock.Registry) {
|
||||
r.Register(
|
||||
httpmock.GraphQL(`query IssueRepositoryInfo\b`),
|
||||
httpmock.StringResponse(`
|
||||
{ "data": { "repository": {
|
||||
"id": "REPOID",
|
||||
"hasIssuesEnabled": true
|
||||
} } }`))
|
||||
r.Register(
|
||||
httpmock.GraphQL(`mutation IssueCreate\b`),
|
||||
httpmock.StringResponse(`
|
||||
{ "data": { "createIssue": { "issue": {
|
||||
"id": "ISSUE_ID_123",
|
||||
"URL": "https://github.com/OWNER/REPO/issues/123"
|
||||
} } } }`))
|
||||
r.Register(
|
||||
httpmock.GraphQL(`query RepositoryIssueTypes\b`),
|
||||
httpmock.StringResponse(`
|
||||
{ "data": { "repository": { "issueTypes": { "nodes": [
|
||||
{ "id": "IT_1", "name": "Bug", "description": "", "color": "d73a4a" },
|
||||
{ "id": "IT_2", "name": "Feature", "description": "", "color": "0075ca" },
|
||||
{ "id": "IT_3", "name": "Task", "description": "", "color": "e4e669" }
|
||||
] } } } }`))
|
||||
},
|
||||
wantsErr: `type "Bugz" not found; available types: Bug, Feature, Task`,
|
||||
},
|
||||
{
|
||||
name: "create with parent",
|
||||
opts: CreateOptions{
|
||||
Detector: &fd.EnabledDetectorMock{},
|
||||
Title: "child issue",
|
||||
Body: "child body",
|
||||
Parent: "100",
|
||||
},
|
||||
httpStubs: func(t *testing.T, r *httpmock.Registry) {
|
||||
r.Register(
|
||||
httpmock.GraphQL(`query IssueRepositoryInfo\b`),
|
||||
httpmock.StringResponse(`
|
||||
{ "data": { "repository": {
|
||||
"id": "REPOID",
|
||||
"hasIssuesEnabled": true
|
||||
} } }`))
|
||||
r.Register(
|
||||
httpmock.GraphQL(`mutation IssueCreate\b`),
|
||||
httpmock.StringResponse(`
|
||||
{ "data": { "createIssue": { "issue": {
|
||||
"id": "ISSUE_ID_123",
|
||||
"URL": "https://github.com/OWNER/REPO/issues/123"
|
||||
} } } }`))
|
||||
r.Register(
|
||||
httpmock.GraphQL(`query IssueNodeID\b`),
|
||||
httpmock.StringResponse(`
|
||||
{ "data": { "repository": { "issue": { "id": "PARENT_ID_100" } } } }`))
|
||||
r.Register(
|
||||
httpmock.GraphQL(`mutation AddSubIssue\b`),
|
||||
httpmock.GraphQLMutation(`
|
||||
{ "data": { "addSubIssue": { "issue": { "id": "PARENT_ID_100" } } } }`,
|
||||
func(inputs map[string]interface{}) {
|
||||
assert.Equal(t, "PARENT_ID_100", inputs["issueId"])
|
||||
assert.Equal(t, "ISSUE_ID_123", inputs["subIssueId"])
|
||||
assert.Equal(t, false, inputs["replaceParent"])
|
||||
}))
|
||||
},
|
||||
wantsStdout: "https://github.com/OWNER/REPO/issues/123\n",
|
||||
wantsStderr: "\nCreating issue in OWNER/REPO\n\n",
|
||||
},
|
||||
{
|
||||
name: "create with blocked-by and blocking",
|
||||
opts: CreateOptions{
|
||||
Detector: &fd.EnabledDetectorMock{},
|
||||
Title: "blocked issue",
|
||||
Body: "blocked body",
|
||||
BlockedBy: []string{"200"},
|
||||
Blocking: []string{"300"},
|
||||
},
|
||||
httpStubs: func(t *testing.T, r *httpmock.Registry) {
|
||||
r.Register(
|
||||
httpmock.GraphQL(`query IssueRepositoryInfo\b`),
|
||||
httpmock.StringResponse(`
|
||||
{ "data": { "repository": {
|
||||
"id": "REPOID",
|
||||
"hasIssuesEnabled": true
|
||||
} } }`))
|
||||
r.Register(
|
||||
httpmock.GraphQL(`mutation IssueCreate\b`),
|
||||
httpmock.StringResponse(`
|
||||
{ "data": { "createIssue": { "issue": {
|
||||
"id": "ISSUE_ID_123",
|
||||
"URL": "https://github.com/OWNER/REPO/issues/123"
|
||||
} } } }`))
|
||||
// IssueNodeID for --blocked-by 200
|
||||
r.Register(
|
||||
httpmock.GraphQL(`query IssueNodeID\b`),
|
||||
httpmock.StringResponse(`
|
||||
{ "data": { "repository": { "issue": { "id": "BLOCKER_ID_200" } } } }`))
|
||||
// AddBlockedBy for --blocked-by: new issue is blocked by #200
|
||||
r.Register(
|
||||
httpmock.GraphQL(`mutation AddBlockedBy\b`),
|
||||
httpmock.GraphQLMutation(`
|
||||
{ "data": { "addBlockedBy": { "blockedIssue": { "id": "ISSUE_ID_123" } } } }`,
|
||||
func(inputs map[string]interface{}) {
|
||||
assert.Equal(t, "ISSUE_ID_123", inputs["issueId"])
|
||||
assert.Equal(t, "BLOCKER_ID_200", inputs["blockingIssueId"])
|
||||
}))
|
||||
// IssueNodeID for --blocking 300
|
||||
r.Register(
|
||||
httpmock.GraphQL(`query IssueNodeID\b`),
|
||||
httpmock.StringResponse(`
|
||||
{ "data": { "repository": { "issue": { "id": "BLOCKED_ID_300" } } } }`))
|
||||
// AddBlockedBy for --blocking: #300 is blocked by new issue (swapped args)
|
||||
r.Register(
|
||||
httpmock.GraphQL(`mutation AddBlockedBy\b`),
|
||||
httpmock.GraphQLMutation(`
|
||||
{ "data": { "addBlockedBy": { "blockedIssue": { "id": "BLOCKED_ID_300" } } } }`,
|
||||
func(inputs map[string]interface{}) {
|
||||
assert.Equal(t, "BLOCKED_ID_300", inputs["issueId"])
|
||||
assert.Equal(t, "ISSUE_ID_123", inputs["blockingIssueId"])
|
||||
}))
|
||||
},
|
||||
wantsStdout: "https://github.com/OWNER/REPO/issues/123\n",
|
||||
wantsStderr: "\nCreating issue in OWNER/REPO\n\n",
|
||||
},
|
||||
{
|
||||
name: "blocked-by unsupported on GHES",
|
||||
opts: CreateOptions{
|
||||
Detector: &fd.DisabledDetectorMock{},
|
||||
Title: "blocked issue",
|
||||
Body: "blocked body",
|
||||
BlockedBy: []string{"200"},
|
||||
},
|
||||
httpStubs: func(t *testing.T, r *httpmock.Registry) {
|
||||
r.Register(
|
||||
httpmock.GraphQL(`query IssueRepositoryInfo\b`),
|
||||
httpmock.StringResponse(`
|
||||
{ "data": { "repository": {
|
||||
"id": "REPOID",
|
||||
"hasIssuesEnabled": true
|
||||
} } }`))
|
||||
r.Register(
|
||||
httpmock.GraphQL(`mutation IssueCreate\b`),
|
||||
httpmock.StringResponse(`
|
||||
{ "data": { "createIssue": { "issue": {
|
||||
"id": "ISSUE_ID_123",
|
||||
"URL": "https://github.com/OWNER/REPO/issues/123"
|
||||
} } } }`))
|
||||
},
|
||||
wantsErr: "issue relationships are not supported on this GitHub Enterprise Server version",
|
||||
},
|
||||
{
|
||||
name: "blocking unsupported on GHES",
|
||||
opts: CreateOptions{
|
||||
Detector: &fd.DisabledDetectorMock{},
|
||||
Title: "blocking issue",
|
||||
Body: "blocking body",
|
||||
Blocking: []string{"300"},
|
||||
},
|
||||
httpStubs: func(t *testing.T, r *httpmock.Registry) {
|
||||
r.Register(
|
||||
httpmock.GraphQL(`query IssueRepositoryInfo\b`),
|
||||
httpmock.StringResponse(`
|
||||
{ "data": { "repository": {
|
||||
"id": "REPOID",
|
||||
"hasIssuesEnabled": true
|
||||
} } }`))
|
||||
r.Register(
|
||||
httpmock.GraphQL(`mutation IssueCreate\b`),
|
||||
httpmock.StringResponse(`
|
||||
{ "data": { "createIssue": { "issue": {
|
||||
"id": "ISSUE_ID_123",
|
||||
"URL": "https://github.com/OWNER/REPO/issues/123"
|
||||
} } } }`))
|
||||
},
|
||||
wantsErr: "issue relationships are not supported on this GitHub Enterprise Server version",
|
||||
},
|
||||
}
|
||||
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(t, httpReg)
|
||||
}
|
||||
|
||||
ios, _, stdout, stderr := iostreams.Test()
|
||||
ios.SetStdoutTTY(true)
|
||||
opts := &tt.opts
|
||||
opts.IO = ios
|
||||
opts.HttpClient = func() (*http.Client, error) {
|
||||
return &http.Client{Transport: httpReg}, nil
|
||||
}
|
||||
opts.BaseRepo = func() (ghrepo.Interface, error) {
|
||||
return ghrepo.New("OWNER", "REPO"), nil
|
||||
}
|
||||
opts.Browser = &browser.Stub{}
|
||||
|
||||
err := createRun(opts)
|
||||
if tt.wantsErr == "" {
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
assert.EqualError(t, err, tt.wantsErr)
|
||||
return
|
||||
}
|
||||
|
||||
assert.Equal(t, tt.wantsStdout, stdout.String())
|
||||
assert.Equal(t, tt.wantsStderr, stderr.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProjectsV1Deprecation(t *testing.T) {
|
||||
|
||||
t.Run("non-interactive submission", func(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -176,6 +176,8 @@ func NewCmdEdit(f *cmdutil.Factory, runF func(*EditOptions) error) *cobra.Comman
|
|||
opts.Editable.Parent.Edited = true
|
||||
if opts.RemoveParent {
|
||||
opts.Editable.Parent.Value = ""
|
||||
} else {
|
||||
opts.Editable.Parent.Value = opts.SetParent
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/MakeNowJust/heredoc"
|
||||
"github.com/cli/cli/v2/api"
|
||||
fd "github.com/cli/cli/v2/internal/featuredetection"
|
||||
"github.com/cli/cli/v2/internal/gh"
|
||||
"github.com/cli/cli/v2/internal/ghrepo"
|
||||
"github.com/cli/cli/v2/internal/run"
|
||||
prShared "github.com/cli/cli/v2/pkg/cmd/pr/shared"
|
||||
|
|
@ -281,6 +282,92 @@ func TestNewCmdEdit(t *testing.T) {
|
|||
input: "23 34",
|
||||
wantsErr: true,
|
||||
},
|
||||
{
|
||||
name: "type flag",
|
||||
input: "23 --type Bug",
|
||||
output: EditOptions{
|
||||
IssueNumbers: []int{23},
|
||||
Editable: prShared.Editable{
|
||||
IssueType: prShared.EditableString{
|
||||
Value: "Bug",
|
||||
Edited: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "set-parent flag",
|
||||
input: "23 --set-parent 100",
|
||||
output: EditOptions{
|
||||
IssueNumbers: []int{23},
|
||||
SetParent: "100",
|
||||
Editable: prShared.Editable{
|
||||
Parent: prShared.EditableString{
|
||||
Value: "100",
|
||||
Edited: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "remove-parent flag",
|
||||
input: "23 --remove-parent",
|
||||
output: EditOptions{
|
||||
IssueNumbers: []int{23},
|
||||
RemoveParent: true,
|
||||
Editable: prShared.Editable{
|
||||
Parent: prShared.EditableString{
|
||||
Value: "",
|
||||
Edited: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "both set-parent and remove-parent flags",
|
||||
input: "23 --set-parent 100 --remove-parent",
|
||||
wantsErr: true,
|
||||
},
|
||||
{
|
||||
name: "add-sub-issue flag",
|
||||
input: "23 --add-sub-issue 123,124",
|
||||
output: EditOptions{
|
||||
IssueNumbers: []int{23},
|
||||
AddSubIssues: []string{"123", "124"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "remove-sub-issue flag",
|
||||
input: "23 --remove-sub-issue 50",
|
||||
output: EditOptions{
|
||||
IssueNumbers: []int{23},
|
||||
RemoveSubIssues: []string{"50"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "add-blocked-by flag",
|
||||
input: "23 --add-blocked-by 200",
|
||||
output: EditOptions{
|
||||
IssueNumbers: []int{23},
|
||||
AddBlockedBy: []string{"200"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "remove-blocked-by flag",
|
||||
input: "23 --remove-blocked-by 201",
|
||||
output: EditOptions{
|
||||
IssueNumbers: []int{23},
|
||||
RemoveBlockedBy: []string{"201"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "add-blocking flag",
|
||||
input: "23 --add-blocking 300,301",
|
||||
output: EditOptions{
|
||||
IssueNumbers: []int{23},
|
||||
AddBlocking: []string{"300", "301"},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
|
@ -322,6 +409,13 @@ func TestNewCmdEdit(t *testing.T) {
|
|||
assert.Equal(t, tt.output.IssueNumbers, gotOpts.IssueNumbers)
|
||||
assert.Equal(t, tt.output.Interactive, gotOpts.Interactive)
|
||||
assert.Equal(t, tt.output.Editable, gotOpts.Editable)
|
||||
assert.Equal(t, tt.output.SetParent, gotOpts.SetParent)
|
||||
assert.Equal(t, tt.output.RemoveParent, gotOpts.RemoveParent)
|
||||
assert.Equal(t, tt.output.AddSubIssues, gotOpts.AddSubIssues)
|
||||
assert.Equal(t, tt.output.RemoveSubIssues, gotOpts.RemoveSubIssues)
|
||||
assert.Equal(t, tt.output.AddBlockedBy, gotOpts.AddBlockedBy)
|
||||
assert.Equal(t, tt.output.RemoveBlockedBy, gotOpts.RemoveBlockedBy)
|
||||
assert.Equal(t, tt.output.AddBlocking, gotOpts.AddBlocking)
|
||||
if tt.expectedBaseRepo != nil {
|
||||
baseRepo, err := gotOpts.BaseRepo()
|
||||
require.NoError(t, err)
|
||||
|
|
@ -720,6 +814,345 @@ func Test_editRun(t *testing.T) {
|
|||
},
|
||||
stdout: "https://github.com/OWNER/REPO/issue/123\n",
|
||||
},
|
||||
{
|
||||
name: "edit type",
|
||||
input: &EditOptions{
|
||||
Detector: &fd.EnabledDetectorMock{},
|
||||
IssueNumbers: []int{123},
|
||||
Interactive: false,
|
||||
Editable: prShared.Editable{
|
||||
IssueType: prShared.EditableString{
|
||||
Value: "Bug",
|
||||
Edited: true,
|
||||
},
|
||||
},
|
||||
FetchOptions: func(_ *api.Client, _ ghrepo.Interface, _ *prShared.Editable, _ gh.ProjectsV1Support) error {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
httpStubs: func(t *testing.T, reg *httpmock.Registry) {
|
||||
mockIssueGet(t, reg)
|
||||
reg.Register(
|
||||
httpmock.GraphQL(`query RepositoryIssueTypes\b`),
|
||||
httpmock.StringResponse(`
|
||||
{ "data": { "repository": { "issueTypes": { "nodes": [
|
||||
{ "id": "BUG_TYPE_ID", "name": "Bug", "description": "", "color": "" },
|
||||
{ "id": "FEATURE_TYPE_ID", "name": "Feature", "description": "", "color": "" }
|
||||
] } } } }
|
||||
`),
|
||||
)
|
||||
reg.Register(
|
||||
httpmock.GraphQL(`mutation UpdateIssueIssueType\b`),
|
||||
httpmock.GraphQLMutation(`
|
||||
{ "data": { "updateIssueIssueType": { "issue": { "id": "123" } } } }`,
|
||||
func(inputs map[string]interface{}) {
|
||||
assert.Equal(t, "123", inputs["issueId"])
|
||||
assert.Equal(t, "BUG_TYPE_ID", inputs["issueTypeId"])
|
||||
}),
|
||||
)
|
||||
},
|
||||
stdout: "https://github.com/OWNER/REPO/issue/123\n",
|
||||
},
|
||||
{
|
||||
name: "edit set parent",
|
||||
input: &EditOptions{
|
||||
Detector: &fd.EnabledDetectorMock{},
|
||||
IssueNumbers: []int{123},
|
||||
Interactive: false,
|
||||
Editable: prShared.Editable{
|
||||
Parent: prShared.EditableString{
|
||||
Value: "100",
|
||||
Edited: true,
|
||||
},
|
||||
},
|
||||
FetchOptions: func(_ *api.Client, _ ghrepo.Interface, _ *prShared.Editable, _ gh.ProjectsV1Support) error {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
httpStubs: func(t *testing.T, reg *httpmock.Registry) {
|
||||
mockIssueGet(t, reg)
|
||||
reg.Register(
|
||||
httpmock.GraphQL(`query IssueNodeID\b`),
|
||||
httpmock.StringResponse(`
|
||||
{ "data": { "repository": { "issue": { "id": "PARENT_100_ID" } } } }
|
||||
`),
|
||||
)
|
||||
reg.Register(
|
||||
httpmock.GraphQL(`mutation AddSubIssue\b`),
|
||||
httpmock.GraphQLMutation(`
|
||||
{ "data": { "addSubIssue": { "issue": { "id": "PARENT_100_ID" } } } }`,
|
||||
func(inputs map[string]interface{}) {
|
||||
assert.Equal(t, "PARENT_100_ID", inputs["issueId"])
|
||||
assert.Equal(t, "123", inputs["subIssueId"])
|
||||
assert.Equal(t, true, inputs["replaceParent"])
|
||||
}),
|
||||
)
|
||||
},
|
||||
stdout: "https://github.com/OWNER/REPO/issue/123\n",
|
||||
},
|
||||
{
|
||||
name: "edit remove parent",
|
||||
input: &EditOptions{
|
||||
Detector: &fd.EnabledDetectorMock{},
|
||||
IssueNumbers: []int{123},
|
||||
Interactive: false,
|
||||
RemoveParent: true,
|
||||
Editable: prShared.Editable{
|
||||
Parent: prShared.EditableString{
|
||||
Value: "",
|
||||
Edited: true,
|
||||
},
|
||||
},
|
||||
FetchOptions: func(_ *api.Client, _ ghrepo.Interface, _ *prShared.Editable, _ gh.ProjectsV1Support) error {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
httpStubs: func(t *testing.T, reg *httpmock.Registry) {
|
||||
reg.Register(
|
||||
httpmock.GraphQL(`query IssueByNumber\b`),
|
||||
httpmock.StringResponse(`
|
||||
{ "data": { "repository": { "hasIssuesEnabled": true, "issue": {
|
||||
"id": "123",
|
||||
"number": 123,
|
||||
"url": "https://github.com/OWNER/REPO/issue/123",
|
||||
"parent": {
|
||||
"number": 100,
|
||||
"title": "Parent Issue",
|
||||
"url": "https://github.com/OWNER/REPO/issues/100",
|
||||
"state": "OPEN",
|
||||
"repository": { "nameWithOwner": "OWNER/REPO" }
|
||||
}
|
||||
} } } }
|
||||
`),
|
||||
)
|
||||
reg.Register(
|
||||
httpmock.GraphQL(`query IssueNodeID\b`),
|
||||
httpmock.StringResponse(`
|
||||
{ "data": { "repository": { "issue": { "id": "PARENT_100_ID" } } } }
|
||||
`),
|
||||
)
|
||||
reg.Register(
|
||||
httpmock.GraphQL(`mutation RemoveSubIssue\b`),
|
||||
httpmock.GraphQLMutation(`
|
||||
{ "data": { "removeSubIssue": { "issue": { "id": "PARENT_100_ID" } } } }`,
|
||||
func(inputs map[string]interface{}) {
|
||||
assert.Equal(t, "PARENT_100_ID", inputs["issueId"])
|
||||
assert.Equal(t, "123", inputs["subIssueId"])
|
||||
}),
|
||||
)
|
||||
},
|
||||
stdout: "https://github.com/OWNER/REPO/issue/123\n",
|
||||
},
|
||||
{
|
||||
name: "edit add sub-issues",
|
||||
input: &EditOptions{
|
||||
Detector: &fd.EnabledDetectorMock{},
|
||||
IssueNumbers: []int{100},
|
||||
Interactive: false,
|
||||
AddSubIssues: []string{"123", "124"},
|
||||
FetchOptions: func(_ *api.Client, _ ghrepo.Interface, _ *prShared.Editable, _ gh.ProjectsV1Support) error {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
httpStubs: func(t *testing.T, reg *httpmock.Registry) {
|
||||
mockIssueNumberGet(t, reg, 100)
|
||||
reg.Register(
|
||||
httpmock.GraphQL(`query IssueNodeID\b`),
|
||||
httpmock.StringResponse(`
|
||||
{ "data": { "repository": { "issue": { "id": "SUB_123_ID" } } } }
|
||||
`),
|
||||
)
|
||||
reg.Register(
|
||||
httpmock.GraphQL(`mutation AddSubIssue\b`),
|
||||
httpmock.GraphQLMutation(`
|
||||
{ "data": { "addSubIssue": { "issue": { "id": "100" } } } }`,
|
||||
func(inputs map[string]interface{}) {
|
||||
assert.Equal(t, "100", inputs["issueId"])
|
||||
assert.Equal(t, "SUB_123_ID", inputs["subIssueId"])
|
||||
assert.Equal(t, false, inputs["replaceParent"])
|
||||
}),
|
||||
)
|
||||
reg.Register(
|
||||
httpmock.GraphQL(`query IssueNodeID\b`),
|
||||
httpmock.StringResponse(`
|
||||
{ "data": { "repository": { "issue": { "id": "SUB_124_ID" } } } }
|
||||
`),
|
||||
)
|
||||
reg.Register(
|
||||
httpmock.GraphQL(`mutation AddSubIssue\b`),
|
||||
httpmock.GraphQLMutation(`
|
||||
{ "data": { "addSubIssue": { "issue": { "id": "100" } } } }`,
|
||||
func(inputs map[string]interface{}) {
|
||||
assert.Equal(t, "100", inputs["issueId"])
|
||||
assert.Equal(t, "SUB_124_ID", inputs["subIssueId"])
|
||||
}),
|
||||
)
|
||||
},
|
||||
stdout: "https://github.com/OWNER/REPO/issue/100\n",
|
||||
},
|
||||
{
|
||||
name: "edit remove sub-issue",
|
||||
input: &EditOptions{
|
||||
Detector: &fd.EnabledDetectorMock{},
|
||||
IssueNumbers: []int{100},
|
||||
Interactive: false,
|
||||
RemoveSubIssues: []string{"123"},
|
||||
FetchOptions: func(_ *api.Client, _ ghrepo.Interface, _ *prShared.Editable, _ gh.ProjectsV1Support) error {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
httpStubs: func(t *testing.T, reg *httpmock.Registry) {
|
||||
mockIssueNumberGet(t, reg, 100)
|
||||
reg.Register(
|
||||
httpmock.GraphQL(`query IssueNodeID\b`),
|
||||
httpmock.StringResponse(`
|
||||
{ "data": { "repository": { "issue": { "id": "SUB_123_ID" } } } }
|
||||
`),
|
||||
)
|
||||
reg.Register(
|
||||
httpmock.GraphQL(`mutation RemoveSubIssue\b`),
|
||||
httpmock.GraphQLMutation(`
|
||||
{ "data": { "removeSubIssue": { "issue": { "id": "100" } } } }`,
|
||||
func(inputs map[string]interface{}) {
|
||||
assert.Equal(t, "100", inputs["issueId"])
|
||||
assert.Equal(t, "SUB_123_ID", inputs["subIssueId"])
|
||||
}),
|
||||
)
|
||||
},
|
||||
stdout: "https://github.com/OWNER/REPO/issue/100\n",
|
||||
},
|
||||
{
|
||||
name: "edit add and remove blocked-by",
|
||||
input: &EditOptions{
|
||||
Detector: &fd.EnabledDetectorMock{},
|
||||
IssueNumbers: []int{123},
|
||||
Interactive: false,
|
||||
AddBlockedBy: []string{"200"},
|
||||
RemoveBlockedBy: []string{"201"},
|
||||
FetchOptions: func(_ *api.Client, _ ghrepo.Interface, _ *prShared.Editable, _ gh.ProjectsV1Support) error {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
httpStubs: func(t *testing.T, reg *httpmock.Registry) {
|
||||
mockIssueGet(t, reg)
|
||||
reg.Register(
|
||||
httpmock.GraphQL(`query IssueNodeID\b`),
|
||||
httpmock.StringResponse(`
|
||||
{ "data": { "repository": { "issue": { "id": "BLOCKING_200_ID" } } } }
|
||||
`),
|
||||
)
|
||||
reg.Register(
|
||||
httpmock.GraphQL(`mutation AddBlockedBy\b`),
|
||||
httpmock.GraphQLMutation(`
|
||||
{ "data": { "addBlockedBy": { "blockedIssue": { "id": "123" } } } }`,
|
||||
func(inputs map[string]interface{}) {
|
||||
assert.Equal(t, "123", inputs["issueId"])
|
||||
assert.Equal(t, "BLOCKING_200_ID", inputs["blockingIssueId"])
|
||||
}),
|
||||
)
|
||||
reg.Register(
|
||||
httpmock.GraphQL(`query IssueNodeID\b`),
|
||||
httpmock.StringResponse(`
|
||||
{ "data": { "repository": { "issue": { "id": "BLOCKING_201_ID" } } } }
|
||||
`),
|
||||
)
|
||||
reg.Register(
|
||||
httpmock.GraphQL(`mutation RemoveBlockedBy\b`),
|
||||
httpmock.GraphQLMutation(`
|
||||
{ "data": { "removeBlockedBy": { "blockedIssue": { "id": "123" } } } }`,
|
||||
func(inputs map[string]interface{}) {
|
||||
assert.Equal(t, "123", inputs["issueId"])
|
||||
assert.Equal(t, "BLOCKING_201_ID", inputs["blockingIssueId"])
|
||||
}),
|
||||
)
|
||||
},
|
||||
stdout: "https://github.com/OWNER/REPO/issue/123\n",
|
||||
},
|
||||
{
|
||||
name: "edit add blocking swaps args",
|
||||
input: &EditOptions{
|
||||
Detector: &fd.EnabledDetectorMock{},
|
||||
IssueNumbers: []int{123},
|
||||
Interactive: false,
|
||||
AddBlocking: []string{"300"},
|
||||
FetchOptions: func(_ *api.Client, _ ghrepo.Interface, _ *prShared.Editable, _ gh.ProjectsV1Support) error {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
httpStubs: func(t *testing.T, reg *httpmock.Registry) {
|
||||
mockIssueGet(t, reg)
|
||||
reg.Register(
|
||||
httpmock.GraphQL(`query IssueNodeID\b`),
|
||||
httpmock.StringResponse(`
|
||||
{ "data": { "repository": { "issue": { "id": "BLOCKED_300_ID" } } } }
|
||||
`),
|
||||
)
|
||||
reg.Register(
|
||||
httpmock.GraphQL(`mutation AddBlockedBy\b`),
|
||||
httpmock.GraphQLMutation(`
|
||||
{ "data": { "addBlockedBy": { "blockedIssue": { "id": "BLOCKED_300_ID" } } } }`,
|
||||
func(inputs map[string]interface{}) {
|
||||
// --add-blocking swaps: OTHER issue is blocked BY this issue
|
||||
assert.Equal(t, "BLOCKED_300_ID", inputs["issueId"])
|
||||
assert.Equal(t, "123", inputs["blockingIssueId"])
|
||||
}),
|
||||
)
|
||||
},
|
||||
stdout: "https://github.com/OWNER/REPO/issue/123\n",
|
||||
},
|
||||
{
|
||||
name: "batch edit type",
|
||||
input: &EditOptions{
|
||||
Detector: &fd.EnabledDetectorMock{},
|
||||
IssueNumbers: []int{123, 456},
|
||||
Interactive: false,
|
||||
Editable: prShared.Editable{
|
||||
IssueType: prShared.EditableString{
|
||||
Value: "Bug",
|
||||
Edited: true,
|
||||
},
|
||||
},
|
||||
FetchOptions: func(_ *api.Client, _ ghrepo.Interface, _ *prShared.Editable, _ gh.ProjectsV1Support) error {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
httpStubs: func(t *testing.T, reg *httpmock.Registry) {
|
||||
mockIssueNumberGet(t, reg, 123)
|
||||
mockIssueNumberGet(t, reg, 456)
|
||||
reg.Register(
|
||||
httpmock.GraphQL(`query RepositoryIssueTypes\b`),
|
||||
httpmock.StringResponse(`
|
||||
{ "data": { "repository": { "issueTypes": { "nodes": [
|
||||
{ "id": "BUG_TYPE_ID", "name": "Bug", "description": "", "color": "" }
|
||||
] } } } }
|
||||
`),
|
||||
)
|
||||
reg.Register(
|
||||
httpmock.GraphQL(`query RepositoryIssueTypes\b`),
|
||||
httpmock.StringResponse(`
|
||||
{ "data": { "repository": { "issueTypes": { "nodes": [
|
||||
{ "id": "BUG_TYPE_ID", "name": "Bug", "description": "", "color": "" }
|
||||
] } } } }
|
||||
`),
|
||||
)
|
||||
reg.Register(
|
||||
httpmock.GraphQL(`mutation UpdateIssueIssueType\b`),
|
||||
httpmock.GraphQLMutation(`
|
||||
{ "data": { "updateIssueIssueType": { "issue": { "id": "123" } } } }`,
|
||||
func(inputs map[string]interface{}) {}),
|
||||
)
|
||||
reg.Register(
|
||||
httpmock.GraphQL(`mutation UpdateIssueIssueType\b`),
|
||||
httpmock.GraphQLMutation(`
|
||||
{ "data": { "updateIssueIssueType": { "issue": { "id": "456" } } } }`,
|
||||
func(inputs map[string]interface{}) {}),
|
||||
)
|
||||
},
|
||||
stdout: heredoc.Doc(`
|
||||
https://github.com/OWNER/REPO/issue/123
|
||||
https://github.com/OWNER/REPO/issue/456
|
||||
`),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package view
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
|
@ -21,6 +22,7 @@ import (
|
|||
"github.com/cli/cli/v2/test"
|
||||
"github.com/google/shlex"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestJSONFields(t *testing.T) {
|
||||
|
|
@ -641,3 +643,297 @@ func mockV2ProjectItems(t *testing.T, r *httpmock.Registry) {
|
|||
} } } } }
|
||||
`))
|
||||
}
|
||||
|
||||
// issueResponseAllIssues2Fields returns a GraphQL response for an issue with all Issues 2.0 fields populated.
|
||||
func issueResponseAllIssues2Fields() string {
|
||||
return `{ "data": { "repository": { "hasIssuesEnabled": true, "issue": {
|
||||
"id": "ISSUE_123",
|
||||
"number": 123,
|
||||
"title": "Implement OAuth flow",
|
||||
"state": "OPEN",
|
||||
"stateReason": "",
|
||||
"body": "The OAuth flow needs work.",
|
||||
"author": {"login": "user1"},
|
||||
"createdAt": "2024-01-01T00:00:00Z",
|
||||
"comments": {"nodes":[], "totalCount": 0},
|
||||
"assignees": {"nodes": [], "totalCount": 0},
|
||||
"labels": {"nodes": [], "totalCount": 0},
|
||||
"milestone": null,
|
||||
"reactionGroups": [],
|
||||
"projectCards": {"nodes": [], "totalCount": 0},
|
||||
"projectItems": {"nodes": [], "totalCount": 0},
|
||||
"url": "https://github.com/OWNER/REPO/issues/123",
|
||||
"issueType": {"id":"IT_1","name":"Bug","description":"Something is not working","color":"d73a4a"},
|
||||
"parent": {"number":100,"title":"Epic: Authentication overhaul","url":"https://github.com/OWNER/REPO/issues/100","state":"OPEN","repository":{"nameWithOwner":"OWNER/REPO"}},
|
||||
"subIssues": {
|
||||
"nodes": [
|
||||
{"number":101,"title":"Design auth module","url":"https://github.com/OWNER/REPO/issues/101","state":"CLOSED","repository":{"nameWithOwner":"OWNER/REPO"}},
|
||||
{"number":102,"title":"Token refresh logic","url":"https://github.com/OWNER/REPO/issues/102","state":"OPEN","repository":{"nameWithOwner":"OWNER/REPO"}}
|
||||
],
|
||||
"totalCount": 2
|
||||
},
|
||||
"subIssuesSummary": {"total":2,"completed":1,"percentCompleted":50.0},
|
||||
"blockedBy": {
|
||||
"nodes": [{"number":200,"title":"API rate limiting","url":"https://github.com/OWNER/REPO/issues/200","state":"OPEN","repository":{"nameWithOwner":"OWNER/REPO"}}]
|
||||
},
|
||||
"blocking": {
|
||||
"nodes": [{"number":300,"title":"Release v2.0","url":"https://github.com/OWNER/REPO/issues/300","state":"OPEN","repository":{"nameWithOwner":"OWNER/REPO"}}]
|
||||
}
|
||||
} } } }`
|
||||
}
|
||||
|
||||
// issueResponseNoIssues2Fields returns a GraphQL response for an issue with no Issues 2.0 fields.
|
||||
func issueResponseNoIssues2Fields() string {
|
||||
return `{ "data": { "repository": { "hasIssuesEnabled": true, "issue": {
|
||||
"id": "ISSUE_456",
|
||||
"number": 456,
|
||||
"title": "Fix login page",
|
||||
"state": "OPEN",
|
||||
"stateReason": "",
|
||||
"body": "The login page is broken.",
|
||||
"author": {"login": "user2"},
|
||||
"createdAt": "2024-01-01T00:00:00Z",
|
||||
"comments": {"nodes":[], "totalCount": 2},
|
||||
"assignees": {"nodes": [], "totalCount": 0},
|
||||
"labels": {"nodes": [], "totalCount": 0},
|
||||
"milestone": null,
|
||||
"reactionGroups": [],
|
||||
"projectCards": {"nodes": [], "totalCount": 0},
|
||||
"projectItems": {"nodes": [], "totalCount": 0},
|
||||
"url": "https://github.com/OWNER/REPO/issues/456"
|
||||
} } } }`
|
||||
}
|
||||
|
||||
func TestIssueView_tty_Issues2AllFields(t *testing.T) {
|
||||
ios, _, stdout, stderr := iostreams.Test()
|
||||
ios.SetStdoutTTY(true)
|
||||
ios.SetStdinTTY(true)
|
||||
ios.SetStderrTTY(true)
|
||||
|
||||
httpReg := &httpmock.Registry{}
|
||||
defer httpReg.Verify(t)
|
||||
|
||||
httpReg.Register(
|
||||
httpmock.GraphQL(`query IssueByNumber\b`),
|
||||
httpmock.StringResponse(issueResponseAllIssues2Fields()),
|
||||
)
|
||||
mockEmptyV2ProjectItems(t, httpReg)
|
||||
|
||||
opts := ViewOptions{
|
||||
IO: ios,
|
||||
Now: func() time.Time {
|
||||
t, _ := time.Parse(time.RFC822, "03 Nov 24 15:04 UTC")
|
||||
return t
|
||||
},
|
||||
HttpClient: func() (*http.Client, error) {
|
||||
return &http.Client{Transport: httpReg}, nil
|
||||
},
|
||||
BaseRepo: func() (ghrepo.Interface, error) {
|
||||
return ghrepo.New("OWNER", "REPO"), nil
|
||||
},
|
||||
IssueNumber: 123,
|
||||
}
|
||||
|
||||
err := viewRun(&opts)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "", stderr.String())
|
||||
|
||||
out := stdout.String()
|
||||
|
||||
// Title
|
||||
assert.Contains(t, out, "Implement OAuth flow")
|
||||
assert.Contains(t, out, "OWNER/REPO#123")
|
||||
|
||||
// State line includes issue type prefix
|
||||
assert.Contains(t, out, "Bug · Open")
|
||||
|
||||
// Type metadata row
|
||||
assert.Contains(t, out, "Type:")
|
||||
assert.Contains(t, out, "Bug")
|
||||
|
||||
// Parent metadata row
|
||||
assert.Contains(t, out, "Parent:")
|
||||
assert.Contains(t, out, "OWNER/REPO#100 Epic: Authentication overhaul")
|
||||
|
||||
// Blocked by metadata row
|
||||
assert.Contains(t, out, "Blocked by:")
|
||||
assert.Contains(t, out, "OWNER/REPO#200 API rate limiting")
|
||||
|
||||
// Blocking metadata row
|
||||
assert.Contains(t, out, "Blocking:")
|
||||
assert.Contains(t, out, "OWNER/REPO#300 Release v2.0")
|
||||
|
||||
// Sub-issues section
|
||||
assert.Contains(t, out, "Sub-issues")
|
||||
assert.Contains(t, out, "1/2 (50%)")
|
||||
assert.Contains(t, out, "OWNER/REPO#101")
|
||||
assert.Contains(t, out, "Design auth module")
|
||||
assert.Contains(t, out, "OWNER/REPO#102")
|
||||
assert.Contains(t, out, "Token refresh logic")
|
||||
|
||||
// Body
|
||||
assert.Contains(t, out, "The OAuth flow needs work.")
|
||||
|
||||
// Footer
|
||||
assert.Contains(t, out, "View this issue on GitHub: https://github.com/OWNER/REPO/issues/123")
|
||||
}
|
||||
|
||||
func TestIssueView_tty_Issues2NoFields(t *testing.T) {
|
||||
ios, _, stdout, stderr := iostreams.Test()
|
||||
ios.SetStdoutTTY(true)
|
||||
ios.SetStdinTTY(true)
|
||||
ios.SetStderrTTY(true)
|
||||
|
||||
httpReg := &httpmock.Registry{}
|
||||
defer httpReg.Verify(t)
|
||||
|
||||
httpReg.Register(
|
||||
httpmock.GraphQL(`query IssueByNumber\b`),
|
||||
httpmock.StringResponse(issueResponseNoIssues2Fields()),
|
||||
)
|
||||
mockEmptyV2ProjectItems(t, httpReg)
|
||||
|
||||
opts := ViewOptions{
|
||||
IO: ios,
|
||||
Now: func() time.Time {
|
||||
t, _ := time.Parse(time.RFC822, "03 Nov 24 15:04 UTC")
|
||||
return t
|
||||
},
|
||||
HttpClient: func() (*http.Client, error) {
|
||||
return &http.Client{Transport: httpReg}, nil
|
||||
},
|
||||
BaseRepo: func() (ghrepo.Interface, error) {
|
||||
return ghrepo.New("OWNER", "REPO"), nil
|
||||
},
|
||||
IssueNumber: 456,
|
||||
}
|
||||
|
||||
err := viewRun(&opts)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "", stderr.String())
|
||||
|
||||
out := stdout.String()
|
||||
|
||||
// Standard fields are still present
|
||||
assert.Contains(t, out, "Fix login page")
|
||||
assert.Contains(t, out, "OWNER/REPO#456")
|
||||
assert.Contains(t, out, "Open")
|
||||
assert.Contains(t, out, "The login page is broken.")
|
||||
assert.Contains(t, out, "View this issue on GitHub: https://github.com/OWNER/REPO/issues/456")
|
||||
|
||||
// Issues 2.0 sections must NOT appear
|
||||
assert.NotContains(t, out, "Type:")
|
||||
assert.NotContains(t, out, "Parent:")
|
||||
assert.NotContains(t, out, "Blocked by:")
|
||||
assert.NotContains(t, out, "Blocking:")
|
||||
assert.NotContains(t, out, "Sub-issues")
|
||||
}
|
||||
|
||||
func TestIssueView_json_IssueType(t *testing.T) {
|
||||
httpReg := &httpmock.Registry{}
|
||||
defer httpReg.Verify(t)
|
||||
|
||||
httpReg.Register(
|
||||
httpmock.GraphQL(`query IssueByNumber\b`),
|
||||
httpmock.StringResponse(issueResponseAllIssues2Fields()),
|
||||
)
|
||||
|
||||
output, err := runCommand(httpReg, false, `123 --json issueType`)
|
||||
require.NoError(t, err)
|
||||
|
||||
var data map[string]interface{}
|
||||
require.NoError(t, json.Unmarshal(output.OutBuf.Bytes(), &data))
|
||||
|
||||
issueType, ok := data["issueType"].(map[string]interface{})
|
||||
require.True(t, ok, "issueType should be an object")
|
||||
assert.Equal(t, "IT_1", issueType["id"])
|
||||
assert.Equal(t, "Bug", issueType["name"])
|
||||
assert.Equal(t, "Something is not working", issueType["description"])
|
||||
assert.Equal(t, "d73a4a", issueType["color"])
|
||||
}
|
||||
|
||||
func TestIssueView_json_ParentSubIssues(t *testing.T) {
|
||||
httpReg := &httpmock.Registry{}
|
||||
defer httpReg.Verify(t)
|
||||
|
||||
httpReg.Register(
|
||||
httpmock.GraphQL(`query IssueByNumber\b`),
|
||||
httpmock.StringResponse(issueResponseAllIssues2Fields()),
|
||||
)
|
||||
|
||||
output, err := runCommand(httpReg, false, `123 --json parent,subIssues,subIssuesSummary`)
|
||||
require.NoError(t, err)
|
||||
|
||||
var data map[string]interface{}
|
||||
require.NoError(t, json.Unmarshal(output.OutBuf.Bytes(), &data))
|
||||
|
||||
// Parent
|
||||
parent, ok := data["parent"].(map[string]interface{})
|
||||
require.True(t, ok, "parent should be an object")
|
||||
assert.Equal(t, float64(100), parent["number"])
|
||||
assert.Equal(t, "Epic: Authentication overhaul", parent["title"])
|
||||
assert.Equal(t, "https://github.com/OWNER/REPO/issues/100", parent["url"])
|
||||
assert.Equal(t, "OPEN", parent["state"])
|
||||
|
||||
// Sub-issues
|
||||
subIssues, ok := data["subIssues"].([]interface{})
|
||||
require.True(t, ok, "subIssues should be an array")
|
||||
require.Len(t, subIssues, 2)
|
||||
|
||||
sub0 := subIssues[0].(map[string]interface{})
|
||||
assert.Equal(t, float64(101), sub0["number"])
|
||||
assert.Equal(t, "Design auth module", sub0["title"])
|
||||
assert.Equal(t, "CLOSED", sub0["state"])
|
||||
|
||||
sub1 := subIssues[1].(map[string]interface{})
|
||||
assert.Equal(t, float64(102), sub1["number"])
|
||||
assert.Equal(t, "Token refresh logic", sub1["title"])
|
||||
assert.Equal(t, "OPEN", sub1["state"])
|
||||
|
||||
// Sub-issues summary
|
||||
summary, ok := data["subIssuesSummary"].(map[string]interface{})
|
||||
require.True(t, ok, "subIssuesSummary should be an object")
|
||||
assert.Equal(t, float64(2), summary["total"])
|
||||
assert.Equal(t, float64(1), summary["completed"])
|
||||
assert.Equal(t, float64(50), summary["percentCompleted"])
|
||||
}
|
||||
|
||||
func TestIssueView_json_BlockedByBlocking(t *testing.T) {
|
||||
httpReg := &httpmock.Registry{}
|
||||
defer httpReg.Verify(t)
|
||||
|
||||
httpReg.Register(
|
||||
httpmock.GraphQL(`query IssueByNumber\b`),
|
||||
httpmock.StringResponse(issueResponseAllIssues2Fields()),
|
||||
)
|
||||
|
||||
output, err := runCommand(httpReg, false, `123 --json blockedBy,blocking`)
|
||||
require.NoError(t, err)
|
||||
|
||||
var data map[string]interface{}
|
||||
require.NoError(t, json.Unmarshal(output.OutBuf.Bytes(), &data))
|
||||
|
||||
// Blocked by
|
||||
blockedBy, ok := data["blockedBy"].([]interface{})
|
||||
require.True(t, ok, "blockedBy should be an array")
|
||||
require.Len(t, blockedBy, 1)
|
||||
|
||||
blocked0 := blockedBy[0].(map[string]interface{})
|
||||
assert.Equal(t, float64(200), blocked0["number"])
|
||||
assert.Equal(t, "API rate limiting", blocked0["title"])
|
||||
assert.Equal(t, "https://github.com/OWNER/REPO/issues/200", blocked0["url"])
|
||||
assert.Equal(t, "OPEN", blocked0["state"])
|
||||
|
||||
// Blocking
|
||||
blocking, ok := data["blocking"].([]interface{})
|
||||
require.True(t, ok, "blocking should be an array")
|
||||
require.Len(t, blocking, 1)
|
||||
|
||||
blocking0 := blocking[0].(map[string]interface{})
|
||||
assert.Equal(t, float64(300), blocking0["number"])
|
||||
assert.Equal(t, "Release v2.0", blocking0["title"])
|
||||
assert.Equal(t, "https://github.com/OWNER/REPO/issues/300", blocking0["url"])
|
||||
assert.Equal(t, "OPEN", blocking0["state"])
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue