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