github.com/ungtb10d/cli/v2@v2.0.0-20221110210412-98537dd9d6a1/pkg/cmd/gist/view/view_test.go (about) 1 package view 2 3 import ( 4 "bytes" 5 "fmt" 6 "net/http" 7 "testing" 8 "time" 9 10 "github.com/ungtb10d/cli/v2/internal/config" 11 "github.com/ungtb10d/cli/v2/pkg/cmd/gist/shared" 12 "github.com/ungtb10d/cli/v2/pkg/cmdutil" 13 "github.com/ungtb10d/cli/v2/pkg/httpmock" 14 "github.com/ungtb10d/cli/v2/pkg/iostreams" 15 "github.com/ungtb10d/cli/v2/pkg/prompt" 16 "github.com/google/shlex" 17 "github.com/stretchr/testify/assert" 18 ) 19 20 func TestNewCmdView(t *testing.T) { 21 tests := []struct { 22 name string 23 cli string 24 wants ViewOptions 25 tty bool 26 }{ 27 { 28 name: "tty no arguments", 29 tty: true, 30 cli: "123", 31 wants: ViewOptions{ 32 Raw: false, 33 Selector: "123", 34 ListFiles: false, 35 }, 36 }, 37 { 38 name: "nontty no arguments", 39 cli: "123", 40 wants: ViewOptions{ 41 Raw: true, 42 Selector: "123", 43 ListFiles: false, 44 }, 45 }, 46 { 47 name: "filename passed", 48 cli: "-fcool.txt 123", 49 tty: true, 50 wants: ViewOptions{ 51 Raw: false, 52 Selector: "123", 53 Filename: "cool.txt", 54 ListFiles: false, 55 }, 56 }, 57 { 58 name: "files passed", 59 cli: "--files 123", 60 tty: true, 61 wants: ViewOptions{ 62 Raw: false, 63 Selector: "123", 64 ListFiles: true, 65 }, 66 }, 67 { 68 name: "tty no ID supplied", 69 cli: "", 70 tty: true, 71 wants: ViewOptions{ 72 Raw: false, 73 Selector: "", 74 ListFiles: true, 75 }, 76 }, 77 } 78 79 for _, tt := range tests { 80 t.Run(tt.name, func(t *testing.T) { 81 ios, _, _, _ := iostreams.Test() 82 ios.SetStdoutTTY(tt.tty) 83 84 f := &cmdutil.Factory{ 85 IOStreams: ios, 86 } 87 88 argv, err := shlex.Split(tt.cli) 89 assert.NoError(t, err) 90 91 var gotOpts *ViewOptions 92 cmd := NewCmdView(f, func(opts *ViewOptions) error { 93 gotOpts = opts 94 return nil 95 }) 96 cmd.SetArgs(argv) 97 cmd.SetIn(&bytes.Buffer{}) 98 cmd.SetOut(&bytes.Buffer{}) 99 cmd.SetErr(&bytes.Buffer{}) 100 101 _, err = cmd.ExecuteC() 102 assert.NoError(t, err) 103 104 assert.Equal(t, tt.wants.Raw, gotOpts.Raw) 105 assert.Equal(t, tt.wants.Selector, gotOpts.Selector) 106 assert.Equal(t, tt.wants.Filename, gotOpts.Filename) 107 }) 108 } 109 } 110 111 func Test_viewRun(t *testing.T) { 112 tests := []struct { 113 name string 114 opts *ViewOptions 115 wantOut string 116 gist *shared.Gist 117 wantErr bool 118 mockGistList bool 119 }{ 120 { 121 name: "no such gist", 122 opts: &ViewOptions{ 123 Selector: "1234", 124 ListFiles: false, 125 }, 126 wantErr: true, 127 }, 128 { 129 name: "one file", 130 opts: &ViewOptions{ 131 Selector: "1234", 132 ListFiles: false, 133 }, 134 gist: &shared.Gist{ 135 Files: map[string]*shared.GistFile{ 136 "cicada.txt": { 137 Content: "bwhiizzzbwhuiiizzzz", 138 Type: "text/plain", 139 }, 140 }, 141 }, 142 wantOut: "bwhiizzzbwhuiiizzzz\n", 143 }, 144 { 145 name: "one file, no ID supplied", 146 opts: &ViewOptions{ 147 Selector: "", 148 ListFiles: false, 149 }, 150 mockGistList: true, 151 gist: &shared.Gist{ 152 Files: map[string]*shared.GistFile{ 153 "cicada.txt": { 154 Content: "test interactive mode", 155 Type: "text/plain", 156 }, 157 }, 158 }, 159 wantOut: "test interactive mode\n", 160 }, 161 { 162 name: "filename selected", 163 opts: &ViewOptions{ 164 Selector: "1234", 165 Filename: "cicada.txt", 166 ListFiles: false, 167 }, 168 gist: &shared.Gist{ 169 Files: map[string]*shared.GistFile{ 170 "cicada.txt": { 171 Content: "bwhiizzzbwhuiiizzzz", 172 Type: "text/plain", 173 }, 174 "foo.md": { 175 Content: "# foo", 176 Type: "application/markdown", 177 }, 178 }, 179 }, 180 wantOut: "bwhiizzzbwhuiiizzzz\n", 181 }, 182 { 183 name: "filename selected, raw", 184 opts: &ViewOptions{ 185 Selector: "1234", 186 Filename: "cicada.txt", 187 Raw: true, 188 ListFiles: false, 189 }, 190 gist: &shared.Gist{ 191 Files: map[string]*shared.GistFile{ 192 "cicada.txt": { 193 Content: "bwhiizzzbwhuiiizzzz", 194 Type: "text/plain", 195 }, 196 "foo.md": { 197 Content: "# foo", 198 Type: "application/markdown", 199 }, 200 }, 201 }, 202 wantOut: "bwhiizzzbwhuiiizzzz\n", 203 }, 204 { 205 name: "multiple files, no description", 206 opts: &ViewOptions{ 207 Selector: "1234", 208 ListFiles: false, 209 }, 210 gist: &shared.Gist{ 211 Files: map[string]*shared.GistFile{ 212 "cicada.txt": { 213 Content: "bwhiizzzbwhuiiizzzz", 214 Type: "text/plain", 215 }, 216 "foo.md": { 217 Content: "# foo", 218 Type: "application/markdown", 219 }, 220 }, 221 }, 222 wantOut: "cicada.txt\n\nbwhiizzzbwhuiiizzzz\n\nfoo.md\n\n\n # foo \n\n", 223 }, 224 { 225 name: "multiple files, trailing newlines", 226 opts: &ViewOptions{ 227 Selector: "1234", 228 ListFiles: false, 229 }, 230 gist: &shared.Gist{ 231 Files: map[string]*shared.GistFile{ 232 "cicada.txt": { 233 Content: "bwhiizzzbwhuiiizzzz\n", 234 Type: "text/plain", 235 }, 236 "foo.txt": { 237 Content: "bar\n", 238 Type: "text/plain", 239 }, 240 }, 241 }, 242 wantOut: "cicada.txt\n\nbwhiizzzbwhuiiizzzz\n\nfoo.txt\n\nbar\n", 243 }, 244 { 245 name: "multiple files, description", 246 opts: &ViewOptions{ 247 Selector: "1234", 248 ListFiles: false, 249 }, 250 gist: &shared.Gist{ 251 Description: "some files", 252 Files: map[string]*shared.GistFile{ 253 "cicada.txt": { 254 Content: "bwhiizzzbwhuiiizzzz", 255 Type: "text/plain", 256 }, 257 "foo.md": { 258 Content: "- foo", 259 Type: "application/markdown", 260 }, 261 }, 262 }, 263 wantOut: "some files\n\ncicada.txt\n\nbwhiizzzbwhuiiizzzz\n\nfoo.md\n\n\n \n • foo \n\n", 264 }, 265 { 266 name: "multiple files, raw", 267 opts: &ViewOptions{ 268 Selector: "1234", 269 Raw: true, 270 ListFiles: false, 271 }, 272 gist: &shared.Gist{ 273 Description: "some files", 274 Files: map[string]*shared.GistFile{ 275 "cicada.txt": { 276 Content: "bwhiizzzbwhuiiizzzz", 277 Type: "text/plain", 278 }, 279 "foo.md": { 280 Content: "- foo", 281 Type: "application/markdown", 282 }, 283 }, 284 }, 285 wantOut: "some files\n\ncicada.txt\n\nbwhiizzzbwhuiiizzzz\n\nfoo.md\n\n- foo\n", 286 }, 287 { 288 name: "one file, list files", 289 opts: &ViewOptions{ 290 Selector: "1234", 291 Raw: false, 292 ListFiles: true, 293 }, 294 gist: &shared.Gist{ 295 Description: "some files", 296 Files: map[string]*shared.GistFile{ 297 "cicada.txt": { 298 Content: "bwhiizzzbwhuiiizzzz", 299 Type: "text/plain", 300 }, 301 }, 302 }, 303 wantOut: "cicada.txt\n", 304 }, 305 { 306 name: "multiple file, list files", 307 opts: &ViewOptions{ 308 Selector: "1234", 309 Raw: false, 310 ListFiles: true, 311 }, 312 gist: &shared.Gist{ 313 Description: "some files", 314 Files: map[string]*shared.GistFile{ 315 "cicada.txt": { 316 Content: "bwhiizzzbwhuiiizzzz", 317 Type: "text/plain", 318 }, 319 "foo.md": { 320 Content: "- foo", 321 Type: "application/markdown", 322 }, 323 }, 324 }, 325 wantOut: "cicada.txt\nfoo.md\n", 326 }, 327 } 328 329 for _, tt := range tests { 330 reg := &httpmock.Registry{} 331 if tt.gist == nil { 332 reg.Register(httpmock.REST("GET", "gists/1234"), 333 httpmock.StatusStringResponse(404, "Not Found")) 334 } else { 335 reg.Register(httpmock.REST("GET", "gists/1234"), 336 httpmock.JSONResponse(tt.gist)) 337 } 338 339 if tt.mockGistList { 340 sixHours, _ := time.ParseDuration("6h") 341 sixHoursAgo := time.Now().Add(-sixHours) 342 reg.Register( 343 httpmock.GraphQL(`query GistList\b`), 344 httpmock.StringResponse(fmt.Sprintf( 345 `{ "data": { "viewer": { "gists": { "nodes": [ 346 { 347 "name": "1234", 348 "files": [{ "name": "cool.txt" }], 349 "description": "", 350 "updatedAt": "%s", 351 "isPublic": true 352 } 353 ] } } } }`, 354 sixHoursAgo.Format(time.RFC3339), 355 )), 356 ) 357 358 //nolint:staticcheck // SA1019: prompt.NewAskStubber is deprecated: use PrompterMock 359 as := prompt.NewAskStubber(t) 360 as.StubPrompt("Select a gist").AnswerDefault() 361 } 362 363 if tt.opts == nil { 364 tt.opts = &ViewOptions{} 365 } 366 367 tt.opts.HttpClient = func() (*http.Client, error) { 368 return &http.Client{Transport: reg}, nil 369 } 370 371 tt.opts.Config = func() (config.Config, error) { 372 return config.NewBlankConfig(), nil 373 } 374 375 ios, _, stdout, _ := iostreams.Test() 376 ios.SetStdoutTTY(true) 377 tt.opts.IO = ios 378 379 t.Run(tt.name, func(t *testing.T) { 380 err := viewRun(tt.opts) 381 if tt.wantErr { 382 assert.Error(t, err) 383 return 384 } 385 assert.NoError(t, err) 386 387 assert.Equal(t, tt.wantOut, stdout.String()) 388 reg.Verify(t) 389 }) 390 } 391 } 392 393 func Test_promptGists(t *testing.T) { 394 tests := []struct { 395 name string 396 askStubs func(as *prompt.AskStubber) 397 response string 398 wantOut string 399 gist *shared.Gist 400 wantErr bool 401 }{ 402 { 403 name: "multiple files, select first gist", 404 askStubs: func(as *prompt.AskStubber) { 405 as.StubPrompt("Select a gist").AnswerWith("cool.txt about 6 hours ago") 406 }, 407 response: `{ "data": { "viewer": { "gists": { "nodes": [ 408 { 409 "name": "gistid1", 410 "files": [{ "name": "cool.txt" }], 411 "description": "", 412 "updatedAt": "%[1]v", 413 "isPublic": true 414 }, 415 { 416 "name": "gistid2", 417 "files": [{ "name": "gistfile0.txt" }], 418 "description": "", 419 "updatedAt": "%[1]v", 420 "isPublic": true 421 } 422 ] } } } }`, 423 wantOut: "gistid1", 424 }, 425 { 426 name: "multiple files, select second gist", 427 askStubs: func(as *prompt.AskStubber) { 428 as.StubPrompt("Select a gist").AnswerWith("gistfile0.txt about 6 hours ago") 429 }, 430 response: `{ "data": { "viewer": { "gists": { "nodes": [ 431 { 432 "name": "gistid1", 433 "files": [{ "name": "cool.txt" }], 434 "description": "", 435 "updatedAt": "%[1]v", 436 "isPublic": true 437 }, 438 { 439 "name": "gistid2", 440 "files": [{ "name": "gistfile0.txt" }], 441 "description": "", 442 "updatedAt": "%[1]v", 443 "isPublic": true 444 } 445 ] } } } }`, 446 wantOut: "gistid2", 447 }, 448 { 449 name: "no files", 450 response: `{ "data": { "viewer": { "gists": { "nodes": [] } } } }`, 451 wantOut: "", 452 }, 453 } 454 455 ios, _, _, _ := iostreams.Test() 456 457 for _, tt := range tests { 458 reg := &httpmock.Registry{} 459 460 const query = `query GistList\b` 461 sixHours, _ := time.ParseDuration("6h") 462 sixHoursAgo := time.Now().Add(-sixHours) 463 reg.Register( 464 httpmock.GraphQL(query), 465 httpmock.StringResponse(fmt.Sprintf( 466 tt.response, 467 sixHoursAgo.Format(time.RFC3339), 468 )), 469 ) 470 client := &http.Client{Transport: reg} 471 472 t.Run(tt.name, func(t *testing.T) { 473 //nolint:staticcheck // SA1019: prompt.NewAskStubber is deprecated: use PrompterMock 474 as := prompt.NewAskStubber(t) 475 if tt.askStubs != nil { 476 tt.askStubs(as) 477 } 478 479 gistID, err := promptGists(client, "github.com", ios.ColorScheme()) 480 assert.NoError(t, err) 481 assert.Equal(t, tt.wantOut, gistID) 482 reg.Verify(t) 483 }) 484 } 485 }