X Tutup
Skip to content

Commit fee7adf

Browse files
committed
Add issue create -F <file> flag and tests
1 parent 13c3c65 commit fee7adf

File tree

6 files changed

+273
-38
lines changed

6 files changed

+273
-38
lines changed

pkg/cmd/issue/create/create.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ func NewCmdCreate(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Co
4646
Config: f.Config,
4747
}
4848

49+
var bodyFile string
50+
4951
cmd := &cobra.Command{
5052
Use: "create",
5153
Short: "Create a new issue",
@@ -61,19 +63,27 @@ func NewCmdCreate(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Co
6163
RunE: func(cmd *cobra.Command, args []string) error {
6264
// support `-R, --repo` override
6365
opts.BaseRepo = f.BaseRepo
66+
opts.HasRepoOverride = cmd.Flags().Changed("repo")
6467

6568
titleProvided := cmd.Flags().Changed("title")
6669
bodyProvided := cmd.Flags().Changed("body")
67-
opts.HasRepoOverride = cmd.Flags().Changed("repo")
70+
if bodyFile != "" {
71+
b, err := cmdutil.ReadFile(bodyFile, opts.IO.In)
72+
if err != nil {
73+
return err
74+
}
75+
opts.Body = string(b)
76+
bodyProvided = true
77+
}
6878

6979
if !opts.IO.CanPrompt() && opts.RecoverFile != "" {
70-
return &cmdutil.FlagError{Err: errors.New("--recover only supported when running interactively")}
80+
return &cmdutil.FlagError{Err: errors.New("`--recover` only supported when running interactively")}
7181
}
7282

7383
opts.Interactive = !(titleProvided && bodyProvided)
7484

7585
if opts.Interactive && !opts.IO.CanPrompt() {
76-
return &cmdutil.FlagError{Err: errors.New("must provide --title and --body when not running interactively")}
86+
return &cmdutil.FlagError{Err: errors.New("must provide title and body when not running interactively")}
7787
}
7888

7989
if runF != nil {
@@ -85,6 +95,7 @@ func NewCmdCreate(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Co
8595

8696
cmd.Flags().StringVarP(&opts.Title, "title", "t", "", "Supply a title. Will prompt for one otherwise.")
8797
cmd.Flags().StringVarP(&opts.Body, "body", "b", "", "Supply a body. Will prompt for one otherwise.")
98+
cmd.Flags().StringVarP(&bodyFile, "body-file", "F", "", "Read body text from `file`")
8899
cmd.Flags().BoolVarP(&opts.WebMode, "web", "w", false, "Open the browser to create an issue")
89100
cmd.Flags().StringSliceVarP(&opts.Assignees, "assignee", "a", nil, "Assign people by their `login`. Use \"@me\" to self-assign.")
90101
cmd.Flags().StringSliceVarP(&opts.Labels, "label", "l", nil, "Add labels by `name`")

pkg/cmd/issue/create/create_test.go

Lines changed: 111 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"io/ioutil"
88
"net/http"
99
"os"
10+
"path/filepath"
1011
"strings"
1112
"testing"
1213

@@ -22,8 +23,118 @@ import (
2223
"github.com/cli/cli/test"
2324
"github.com/google/shlex"
2425
"github.com/stretchr/testify/assert"
26+
"github.com/stretchr/testify/require"
2527
)
2628

29+
func TestNewCmdCreate(t *testing.T) {
30+
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
31+
err := ioutil.WriteFile(tmpFile, []byte("a body from file"), 0600)
32+
require.NoError(t, err)
33+
34+
tests := []struct {
35+
name string
36+
tty bool
37+
stdin string
38+
cli string
39+
wantsErr bool
40+
wantsOpts CreateOptions
41+
}{
42+
{
43+
name: "empty non-tty",
44+
tty: false,
45+
cli: "",
46+
wantsErr: true,
47+
},
48+
{
49+
name: "only title non-tty",
50+
tty: false,
51+
cli: "-t mytitle",
52+
wantsErr: true,
53+
},
54+
{
55+
name: "empty tty",
56+
tty: true,
57+
cli: "",
58+
wantsErr: false,
59+
wantsOpts: CreateOptions{
60+
Title: "",
61+
Body: "",
62+
RecoverFile: "",
63+
WebMode: false,
64+
Interactive: true,
65+
},
66+
},
67+
{
68+
name: "body from stdin",
69+
tty: false,
70+
stdin: "this is on standard input",
71+
cli: "-t mytitle -F -",
72+
wantsErr: false,
73+
wantsOpts: CreateOptions{
74+
Title: "mytitle",
75+
Body: "this is on standard input",
76+
RecoverFile: "",
77+
WebMode: false,
78+
Interactive: false,
79+
},
80+
},
81+
{
82+
name: "body from file",
83+
tty: false,
84+
cli: fmt.Sprintf("-t mytitle -F '%s'", tmpFile),
85+
wantsErr: false,
86+
wantsOpts: CreateOptions{
87+
Title: "mytitle",
88+
Body: "a body from file",
89+
RecoverFile: "",
90+
WebMode: false,
91+
Interactive: false,
92+
},
93+
},
94+
}
95+
for _, tt := range tests {
96+
t.Run(tt.name, func(t *testing.T) {
97+
io, stdin, stdout, stderr := iostreams.Test()
98+
if tt.stdin != "" {
99+
_, _ = stdin.WriteString(tt.stdin)
100+
} else if tt.tty {
101+
io.SetStdinTTY(true)
102+
io.SetStdoutTTY(true)
103+
}
104+
105+
f := &cmdutil.Factory{
106+
IOStreams: io,
107+
}
108+
109+
var opts *CreateOptions
110+
cmd := NewCmdCreate(f, func(o *CreateOptions) error {
111+
opts = o
112+
return nil
113+
})
114+
115+
args, err := shlex.Split(tt.cli)
116+
require.NoError(t, err)
117+
cmd.SetArgs(args)
118+
_, err = cmd.ExecuteC()
119+
if tt.wantsErr {
120+
assert.Error(t, err)
121+
return
122+
} else {
123+
require.NoError(t, err)
124+
}
125+
126+
assert.Equal(t, "", stdout.String())
127+
assert.Equal(t, "", stderr.String())
128+
129+
assert.Equal(t, tt.wantsOpts.Body, opts.Body)
130+
assert.Equal(t, tt.wantsOpts.Title, opts.Title)
131+
assert.Equal(t, tt.wantsOpts.RecoverFile, opts.RecoverFile)
132+
assert.Equal(t, tt.wantsOpts.WebMode, opts.WebMode)
133+
assert.Equal(t, tt.wantsOpts.Interactive, opts.Interactive)
134+
})
135+
}
136+
}
137+
27138
func runCommand(rt http.RoundTripper, isTTY bool, cli string) (*test.CmdOut, error) {
28139
return runCommandWithRootDirOverridden(rt, isTTY, cli, "")
29140
}
@@ -69,14 +180,6 @@ func runCommandWithRootDirOverridden(rt http.RoundTripper, isTTY bool, cli strin
69180
}, err
70181
}
71182

72-
func TestIssueCreate_nontty_error(t *testing.T) {
73-
http := &httpmock.Registry{}
74-
defer http.Verify(t)
75-
76-
_, err := runCommand(http, false, `-t hello`)
77-
assert.EqualError(t, err, "must provide --title and --body when not running interactively")
78-
}
79-
80183
func TestIssueCreate(t *testing.T) {
81184
http := &httpmock.Registry{}
82185
defer http.Verify(t)

pkg/cmd/pr/create/create.go

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package create
33
import (
44
"errors"
55
"fmt"
6-
"io/ioutil"
76
"net/http"
87
"net/url"
98
"regexp"
@@ -113,7 +112,6 @@ func NewCmdCreate(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Co
113112
Args: cmdutil.NoArgsQuoteReminder,
114113
RunE: func(cmd *cobra.Command, args []string) error {
115114
opts.TitleProvided = cmd.Flags().Changed("title")
116-
opts.BodyProvided = cmd.Flags().Changed("body")
117115
opts.RepoOverride, _ = cmd.Flags().GetString("repo")
118116
noMaintainerEdit, _ := cmd.Flags().GetBool("no-maintainer-edit")
119117
opts.MaintainerCanModify = !noMaintainerEdit
@@ -136,14 +134,9 @@ func NewCmdCreate(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Co
136134
return errors.New("the `--no-maintainer-edit` flag is not supported with `--web`")
137135
}
138136

137+
opts.BodyProvided = cmd.Flags().Changed("body")
139138
if bodyFile != "" {
140-
var b []byte
141-
var err error
142-
if bodyFile == "-" {
143-
b, err = ioutil.ReadAll(opts.IO.In)
144-
} else {
145-
b, err = ioutil.ReadFile(bodyFile)
146-
}
139+
b, err := cmdutil.ReadFile(bodyFile, opts.IO.In)
147140
if err != nil {
148141
return err
149142
}
@@ -162,7 +155,7 @@ func NewCmdCreate(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Co
162155
fl.BoolVarP(&opts.IsDraft, "draft", "d", false, "Mark pull request as a draft")
163156
fl.StringVarP(&opts.Title, "title", "t", "", "Title for the pull request")
164157
fl.StringVarP(&opts.Body, "body", "b", "", "Body for the pull request")
165-
fl.StringVarP(&bodyFile, "body-file", "F", "", "Read body from file. Use - to read the body from the standard input.")
158+
fl.StringVarP(&bodyFile, "body-file", "F", "", "Read body text from `file`")
166159
fl.StringVarP(&opts.BaseBranch, "base", "B", "", "The `branch` into which you want your code merged")
167160
fl.StringVarP(&opts.HeadBranch, "head", "H", "", "The `branch` that contains commits for your pull request (default: current branch)")
168161
fl.BoolVarP(&opts.WebMode, "web", "w", false, "Open the web browser to create a pull request")

pkg/cmd/pr/create/create_test.go

Lines changed: 128 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"io/ioutil"
88
"net/http"
99
"os"
10+
"path/filepath"
1011
"strings"
1112
"testing"
1213

@@ -28,6 +29,133 @@ import (
2829
"github.com/stretchr/testify/require"
2930
)
3031

32+
func TestNewCmdCreate(t *testing.T) {
33+
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
34+
err := ioutil.WriteFile(tmpFile, []byte("a body from file"), 0600)
35+
require.NoError(t, err)
36+
37+
tests := []struct {
38+
name string
39+
tty bool
40+
stdin string
41+
cli string
42+
wantsErr bool
43+
wantsOpts CreateOptions
44+
}{
45+
{
46+
name: "empty non-tty",
47+
tty: false,
48+
cli: "",
49+
wantsErr: true,
50+
},
51+
{
52+
name: "empty tty",
53+
tty: true,
54+
cli: "",
55+
wantsErr: false,
56+
wantsOpts: CreateOptions{
57+
Title: "",
58+
TitleProvided: false,
59+
Body: "",
60+
BodyProvided: false,
61+
Autofill: false,
62+
RecoverFile: "",
63+
WebMode: false,
64+
IsDraft: false,
65+
BaseBranch: "",
66+
HeadBranch: "",
67+
MaintainerCanModify: true,
68+
},
69+
},
70+
{
71+
name: "body from stdin",
72+
tty: false,
73+
stdin: "this is on standard input",
74+
cli: "-t mytitle -F -",
75+
wantsErr: false,
76+
wantsOpts: CreateOptions{
77+
Title: "mytitle",
78+
TitleProvided: true,
79+
Body: "this is on standard input",
80+
BodyProvided: true,
81+
Autofill: false,
82+
RecoverFile: "",
83+
WebMode: false,
84+
IsDraft: false,
85+
BaseBranch: "",
86+
HeadBranch: "",
87+
MaintainerCanModify: true,
88+
},
89+
},
90+
{
91+
name: "body from file",
92+
tty: false,
93+
cli: fmt.Sprintf("-t mytitle -F '%s'", tmpFile),
94+
wantsErr: false,
95+
wantsOpts: CreateOptions{
96+
Title: "mytitle",
97+
TitleProvided: true,
98+
Body: "a body from file",
99+
BodyProvided: true,
100+
Autofill: false,
101+
RecoverFile: "",
102+
WebMode: false,
103+
IsDraft: false,
104+
BaseBranch: "",
105+
HeadBranch: "",
106+
MaintainerCanModify: true,
107+
},
108+
},
109+
}
110+
for _, tt := range tests {
111+
t.Run(tt.name, func(t *testing.T) {
112+
io, stdin, stdout, stderr := iostreams.Test()
113+
if tt.stdin != "" {
114+
_, _ = stdin.WriteString(tt.stdin)
115+
} else if tt.tty {
116+
io.SetStdinTTY(true)
117+
io.SetStdoutTTY(true)
118+
}
119+
120+
f := &cmdutil.Factory{
121+
IOStreams: io,
122+
}
123+
124+
var opts *CreateOptions
125+
cmd := NewCmdCreate(f, func(o *CreateOptions) error {
126+
opts = o
127+
return nil
128+
})
129+
130+
args, err := shlex.Split(tt.cli)
131+
require.NoError(t, err)
132+
cmd.SetArgs(args)
133+
_, err = cmd.ExecuteC()
134+
if tt.wantsErr {
135+
assert.Error(t, err)
136+
return
137+
} else {
138+
require.NoError(t, err)
139+
}
140+
141+
assert.Equal(t, "", stdout.String())
142+
assert.Equal(t, "", stderr.String())
143+
144+
assert.Equal(t, tt.wantsOpts.Body, opts.Body)
145+
assert.Equal(t, tt.wantsOpts.BodyProvided, opts.BodyProvided)
146+
assert.Equal(t, tt.wantsOpts.Title, opts.Title)
147+
assert.Equal(t, tt.wantsOpts.TitleProvided, opts.TitleProvided)
148+
assert.Equal(t, tt.wantsOpts.Autofill, opts.Autofill)
149+
assert.Equal(t, tt.wantsOpts.WebMode, opts.WebMode)
150+
assert.Equal(t, tt.wantsOpts.RecoverFile, opts.RecoverFile)
151+
assert.Equal(t, tt.wantsOpts.IsDraft, opts.IsDraft)
152+
assert.Equal(t, tt.wantsOpts.MaintainerCanModify, opts.MaintainerCanModify)
153+
assert.Equal(t, tt.wantsOpts.BaseBranch, opts.BaseBranch)
154+
assert.Equal(t, tt.wantsOpts.HeadBranch, opts.HeadBranch)
155+
})
156+
}
157+
}
158+
31159
func runCommand(rt http.RoundTripper, remotes context.Remotes, branch string, isTTY bool, cli string) (*test.CmdOut, error) {
32160
return runCommandWithRootDirOverridden(rt, remotes, branch, isTTY, cli, "")
33161
}
@@ -115,16 +243,6 @@ func TestPRCreate_nontty_web(t *testing.T) {
115243
assert.Equal(t, "", output.Stderr())
116244
}
117245

118-
func TestPRCreate_nontty_insufficient_flags(t *testing.T) {
119-
http := initFakeHTTP()
120-
defer http.Verify(t)
121-
122-
output, err := runCommand(http, nil, "feature", false, "")
123-
assert.EqualError(t, err, "`--title` or `--fill` required when not running interactively")
124-
125-
assert.Equal(t, "", output.String())
126-
}
127-
128246
func TestPRCreate_recover(t *testing.T) {
129247
http := initFakeHTTP()
130248
defer http.Verify(t)

0 commit comments

Comments
 (0)
X Tutup