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