github.com/cli/cli@v1.14.1-0.20210902173923-1af6a669e342/pkg/cmd/issue/comment/comment_test.go (about) 1 package comment 2 3 import ( 4 "bytes" 5 "fmt" 6 "io/ioutil" 7 "net/http" 8 "path/filepath" 9 "testing" 10 11 "github.com/cli/cli/internal/ghrepo" 12 "github.com/cli/cli/pkg/cmd/pr/shared" 13 "github.com/cli/cli/pkg/cmdutil" 14 "github.com/cli/cli/pkg/httpmock" 15 "github.com/cli/cli/pkg/iostreams" 16 "github.com/google/shlex" 17 "github.com/stretchr/testify/assert" 18 "github.com/stretchr/testify/require" 19 ) 20 21 func TestNewCmdComment(t *testing.T) { 22 tmpFile := filepath.Join(t.TempDir(), "my-body.md") 23 err := ioutil.WriteFile(tmpFile, []byte("a body from file"), 0600) 24 require.NoError(t, err) 25 26 tests := []struct { 27 name string 28 input string 29 stdin string 30 output shared.CommentableOptions 31 wantsErr bool 32 }{ 33 { 34 name: "no arguments", 35 input: "", 36 output: shared.CommentableOptions{}, 37 wantsErr: true, 38 }, 39 { 40 name: "issue number", 41 input: "1", 42 output: shared.CommentableOptions{ 43 Interactive: true, 44 InputType: 0, 45 Body: "", 46 }, 47 wantsErr: false, 48 }, 49 { 50 name: "issue url", 51 input: "https://github.com/OWNER/REPO/issues/12", 52 output: shared.CommentableOptions{ 53 Interactive: true, 54 InputType: 0, 55 Body: "", 56 }, 57 wantsErr: false, 58 }, 59 { 60 name: "body flag", 61 input: "1 --body test", 62 output: shared.CommentableOptions{ 63 Interactive: false, 64 InputType: shared.InputTypeInline, 65 Body: "test", 66 }, 67 wantsErr: false, 68 }, 69 { 70 name: "body from stdin", 71 input: "1 --body-file -", 72 stdin: "this is on standard input", 73 output: shared.CommentableOptions{ 74 Interactive: false, 75 InputType: shared.InputTypeInline, 76 Body: "this is on standard input", 77 }, 78 wantsErr: false, 79 }, 80 { 81 name: "body from file", 82 input: fmt.Sprintf("1 --body-file '%s'", tmpFile), 83 output: shared.CommentableOptions{ 84 Interactive: false, 85 InputType: shared.InputTypeInline, 86 Body: "a body from file", 87 }, 88 wantsErr: false, 89 }, 90 { 91 name: "editor flag", 92 input: "1 --editor", 93 output: shared.CommentableOptions{ 94 Interactive: false, 95 InputType: shared.InputTypeEditor, 96 Body: "", 97 }, 98 wantsErr: false, 99 }, 100 { 101 name: "web flag", 102 input: "1 --web", 103 output: shared.CommentableOptions{ 104 Interactive: false, 105 InputType: shared.InputTypeWeb, 106 Body: "", 107 }, 108 wantsErr: false, 109 }, 110 { 111 name: "body and body-file flags", 112 input: "1 --body 'test' --body-file 'test-file.txt'", 113 output: shared.CommentableOptions{}, 114 wantsErr: true, 115 }, 116 { 117 name: "editor and web flags", 118 input: "1 --editor --web", 119 output: shared.CommentableOptions{}, 120 wantsErr: true, 121 }, 122 { 123 name: "editor and body flags", 124 input: "1 --editor --body test", 125 output: shared.CommentableOptions{}, 126 wantsErr: true, 127 }, 128 { 129 name: "web and body flags", 130 input: "1 --web --body test", 131 output: shared.CommentableOptions{}, 132 wantsErr: true, 133 }, 134 { 135 name: "editor, web, and body flags", 136 input: "1 --editor --web --body test", 137 output: shared.CommentableOptions{}, 138 wantsErr: true, 139 }, 140 } 141 142 for _, tt := range tests { 143 t.Run(tt.name, func(t *testing.T) { 144 io, stdin, _, _ := iostreams.Test() 145 io.SetStdoutTTY(true) 146 io.SetStdinTTY(true) 147 io.SetStderrTTY(true) 148 149 if tt.stdin != "" { 150 _, _ = stdin.WriteString(tt.stdin) 151 } 152 153 f := &cmdutil.Factory{ 154 IOStreams: io, 155 Browser: &cmdutil.TestBrowser{}, 156 } 157 158 argv, err := shlex.Split(tt.input) 159 assert.NoError(t, err) 160 161 var gotOpts *shared.CommentableOptions 162 cmd := NewCmdComment(f, func(opts *shared.CommentableOptions) error { 163 gotOpts = opts 164 return nil 165 }) 166 cmd.Flags().BoolP("help", "x", false, "") 167 168 cmd.SetArgs(argv) 169 cmd.SetIn(&bytes.Buffer{}) 170 cmd.SetOut(&bytes.Buffer{}) 171 cmd.SetErr(&bytes.Buffer{}) 172 173 _, err = cmd.ExecuteC() 174 if tt.wantsErr { 175 assert.Error(t, err) 176 return 177 } 178 179 assert.NoError(t, err) 180 assert.Equal(t, tt.output.Interactive, gotOpts.Interactive) 181 assert.Equal(t, tt.output.InputType, gotOpts.InputType) 182 assert.Equal(t, tt.output.Body, gotOpts.Body) 183 }) 184 } 185 } 186 187 func Test_commentRun(t *testing.T) { 188 tests := []struct { 189 name string 190 input *shared.CommentableOptions 191 httpStubs func(*testing.T, *httpmock.Registry) 192 stdout string 193 stderr string 194 }{ 195 { 196 name: "interactive editor", 197 input: &shared.CommentableOptions{ 198 Interactive: true, 199 InputType: 0, 200 Body: "", 201 202 InteractiveEditSurvey: func() (string, error) { return "comment body", nil }, 203 ConfirmSubmitSurvey: func() (bool, error) { return true, nil }, 204 }, 205 httpStubs: func(t *testing.T, reg *httpmock.Registry) { 206 mockIssueFromNumber(t, reg) 207 mockCommentCreate(t, reg) 208 }, 209 stdout: "https://github.com/OWNER/REPO/issues/123#issuecomment-456\n", 210 }, 211 { 212 name: "non-interactive web", 213 input: &shared.CommentableOptions{ 214 Interactive: false, 215 InputType: shared.InputTypeWeb, 216 Body: "", 217 218 OpenInBrowser: func(string) error { return nil }, 219 }, 220 httpStubs: func(t *testing.T, reg *httpmock.Registry) { 221 mockIssueFromNumber(t, reg) 222 }, 223 stderr: "Opening github.com/OWNER/REPO/issues/123 in your browser.\n", 224 }, 225 { 226 name: "non-interactive editor", 227 input: &shared.CommentableOptions{ 228 Interactive: false, 229 InputType: shared.InputTypeEditor, 230 Body: "", 231 232 EditSurvey: func() (string, error) { return "comment body", nil }, 233 }, 234 httpStubs: func(t *testing.T, reg *httpmock.Registry) { 235 mockIssueFromNumber(t, reg) 236 mockCommentCreate(t, reg) 237 }, 238 stdout: "https://github.com/OWNER/REPO/issues/123#issuecomment-456\n", 239 }, 240 { 241 name: "non-interactive inline", 242 input: &shared.CommentableOptions{ 243 Interactive: false, 244 InputType: shared.InputTypeInline, 245 Body: "comment body", 246 }, 247 httpStubs: func(t *testing.T, reg *httpmock.Registry) { 248 mockIssueFromNumber(t, reg) 249 mockCommentCreate(t, reg) 250 }, 251 stdout: "https://github.com/OWNER/REPO/issues/123#issuecomment-456\n", 252 }, 253 } 254 for _, tt := range tests { 255 io, _, stdout, stderr := iostreams.Test() 256 io.SetStdoutTTY(true) 257 io.SetStdinTTY(true) 258 io.SetStderrTTY(true) 259 260 reg := &httpmock.Registry{} 261 defer reg.Verify(t) 262 tt.httpStubs(t, reg) 263 264 httpClient := func() (*http.Client, error) { return &http.Client{Transport: reg}, nil } 265 baseRepo := func() (ghrepo.Interface, error) { return ghrepo.New("OWNER", "REPO"), nil } 266 267 tt.input.IO = io 268 tt.input.HttpClient = httpClient 269 tt.input.RetrieveCommentable = retrieveIssue(tt.input.HttpClient, baseRepo, "123") 270 271 t.Run(tt.name, func(t *testing.T) { 272 err := shared.CommentableRun(tt.input) 273 assert.NoError(t, err) 274 assert.Equal(t, tt.stdout, stdout.String()) 275 assert.Equal(t, tt.stderr, stderr.String()) 276 }) 277 } 278 } 279 280 func mockIssueFromNumber(_ *testing.T, reg *httpmock.Registry) { 281 reg.Register( 282 httpmock.GraphQL(`query IssueByNumber\b`), 283 httpmock.StringResponse(` 284 { "data": { "repository": { "hasIssuesEnabled": true, "issue": { 285 "number": 123, 286 "url": "https://github.com/OWNER/REPO/issues/123" 287 } } } }`), 288 ) 289 } 290 291 func mockCommentCreate(t *testing.T, reg *httpmock.Registry) { 292 reg.Register( 293 httpmock.GraphQL(`mutation CommentCreate\b`), 294 httpmock.GraphQLMutation(` 295 { "data": { "addComment": { "commentEdge": { "node": { 296 "url": "https://github.com/OWNER/REPO/issues/123#issuecomment-456" 297 } } } } }`, 298 func(inputs map[string]interface{}) { 299 assert.Equal(t, "comment body", inputs["body"]) 300 }), 301 ) 302 }