github.com/andrewhsu/cli/v2@v2.0.1-0.20210910131313-d4b4061f5b89/pkg/cmd/repo/view/view_test.go (about) 1 package view 2 3 import ( 4 "bytes" 5 "fmt" 6 "net/http" 7 "testing" 8 9 "github.com/MakeNowJust/heredoc" 10 "github.com/andrewhsu/cli/v2/api" 11 "github.com/andrewhsu/cli/v2/internal/ghrepo" 12 "github.com/andrewhsu/cli/v2/internal/run" 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/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 wantsErr bool 26 }{ 27 { 28 name: "no args", 29 cli: "", 30 wants: ViewOptions{ 31 RepoArg: "", 32 Web: false, 33 }, 34 }, 35 { 36 name: "sets repo arg", 37 cli: "some/repo", 38 wants: ViewOptions{ 39 RepoArg: "some/repo", 40 Web: false, 41 }, 42 }, 43 { 44 name: "sets web", 45 cli: "-w", 46 wants: ViewOptions{ 47 RepoArg: "", 48 Web: true, 49 }, 50 }, 51 { 52 name: "sets branch", 53 cli: "-b feat/awesome", 54 wants: ViewOptions{ 55 RepoArg: "", 56 Branch: "feat/awesome", 57 }, 58 }, 59 } 60 61 for _, tt := range tests { 62 t.Run(tt.name, func(t *testing.T) { 63 io, _, _, _ := iostreams.Test() 64 65 f := &cmdutil.Factory{ 66 IOStreams: io, 67 } 68 69 // THOUGHT: this seems ripe for cmdutil. It's almost identical to the set up for the same test 70 // in gist create. 71 argv, err := shlex.Split(tt.cli) 72 assert.NoError(t, err) 73 74 var gotOpts *ViewOptions 75 cmd := NewCmdView(f, func(opts *ViewOptions) error { 76 gotOpts = opts 77 return nil 78 }) 79 cmd.SetArgs(argv) 80 cmd.SetIn(&bytes.Buffer{}) 81 cmd.SetOut(&bytes.Buffer{}) 82 cmd.SetErr(&bytes.Buffer{}) 83 84 _, err = cmd.ExecuteC() 85 if tt.wantsErr { 86 assert.Error(t, err) 87 return 88 } 89 assert.NoError(t, err) 90 91 assert.Equal(t, tt.wants.Web, gotOpts.Web) 92 assert.Equal(t, tt.wants.Branch, gotOpts.Branch) 93 assert.Equal(t, tt.wants.RepoArg, gotOpts.RepoArg) 94 }) 95 } 96 } 97 98 func Test_RepoView_Web(t *testing.T) { 99 tests := []struct { 100 name string 101 stdoutTTY bool 102 wantStderr string 103 wantBrowse string 104 }{ 105 { 106 name: "tty", 107 stdoutTTY: true, 108 wantStderr: "Opening github.com/OWNER/REPO in your browser.\n", 109 wantBrowse: "https://github.com/OWNER/REPO", 110 }, 111 { 112 name: "nontty", 113 stdoutTTY: false, 114 wantStderr: "", 115 wantBrowse: "https://github.com/OWNER/REPO", 116 }, 117 } 118 119 for _, tt := range tests { 120 reg := &httpmock.Registry{} 121 reg.StubRepoInfoResponse("OWNER", "REPO", "main") 122 123 browser := &cmdutil.TestBrowser{} 124 opts := &ViewOptions{ 125 Web: true, 126 HttpClient: func() (*http.Client, error) { 127 return &http.Client{Transport: reg}, nil 128 }, 129 BaseRepo: func() (ghrepo.Interface, error) { 130 return ghrepo.New("OWNER", "REPO"), nil 131 }, 132 Browser: browser, 133 } 134 135 io, _, stdout, stderr := iostreams.Test() 136 137 opts.IO = io 138 139 t.Run(tt.name, func(t *testing.T) { 140 io.SetStdoutTTY(tt.stdoutTTY) 141 142 _, teardown := run.Stub() 143 defer teardown(t) 144 145 if err := viewRun(opts); err != nil { 146 t.Errorf("viewRun() error = %v", err) 147 } 148 assert.Equal(t, "", stdout.String()) 149 assert.Equal(t, tt.wantStderr, stderr.String()) 150 reg.Verify(t) 151 browser.Verify(t, tt.wantBrowse) 152 }) 153 } 154 } 155 156 func Test_ViewRun(t *testing.T) { 157 tests := []struct { 158 name string 159 opts *ViewOptions 160 repoName string 161 stdoutTTY bool 162 wantOut string 163 wantStderr string 164 wantErr bool 165 }{ 166 { 167 name: "nontty", 168 wantOut: heredoc.Doc(` 169 name: OWNER/REPO 170 description: social distancing 171 -- 172 # truly cool readme check it out 173 `), 174 }, 175 { 176 name: "url arg", 177 repoName: "jill/valentine", 178 opts: &ViewOptions{ 179 RepoArg: "https://github.com/jill/valentine", 180 }, 181 stdoutTTY: true, 182 wantOut: heredoc.Doc(` 183 jill/valentine 184 social distancing 185 186 187 # truly cool readme check it out 188 189 190 191 View this repository on GitHub: https://github.com/jill/valentine 192 `), 193 }, 194 { 195 name: "name arg", 196 repoName: "jill/valentine", 197 opts: &ViewOptions{ 198 RepoArg: "jill/valentine", 199 }, 200 stdoutTTY: true, 201 wantOut: heredoc.Doc(` 202 jill/valentine 203 social distancing 204 205 206 # truly cool readme check it out 207 208 209 210 View this repository on GitHub: https://github.com/jill/valentine 211 `), 212 }, 213 { 214 name: "branch arg", 215 opts: &ViewOptions{ 216 Branch: "feat/awesome", 217 }, 218 stdoutTTY: true, 219 wantOut: heredoc.Doc(` 220 OWNER/REPO 221 social distancing 222 223 224 # truly cool readme check it out 225 226 227 228 View this repository on GitHub: https://github.com/OWNER/REPO/tree/feat%2Fawesome 229 `), 230 }, 231 { 232 name: "no args", 233 stdoutTTY: true, 234 wantOut: heredoc.Doc(` 235 OWNER/REPO 236 social distancing 237 238 239 # truly cool readme check it out 240 241 242 243 View this repository on GitHub: https://github.com/OWNER/REPO 244 `), 245 }, 246 } 247 for _, tt := range tests { 248 if tt.opts == nil { 249 tt.opts = &ViewOptions{} 250 } 251 252 if tt.repoName == "" { 253 tt.repoName = "OWNER/REPO" 254 } 255 256 tt.opts.BaseRepo = func() (ghrepo.Interface, error) { 257 repo, _ := ghrepo.FromFullName(tt.repoName) 258 return repo, nil 259 } 260 261 reg := &httpmock.Registry{} 262 reg.Register( 263 httpmock.GraphQL(`query RepositoryInfo\b`), 264 httpmock.StringResponse(` 265 { "data": { 266 "repository": { 267 "description": "social distancing" 268 } } }`)) 269 reg.Register( 270 httpmock.REST("GET", fmt.Sprintf("repos/%s/readme", tt.repoName)), 271 httpmock.StringResponse(` 272 { "name": "readme.md", 273 "content": "IyB0cnVseSBjb29sIHJlYWRtZSBjaGVjayBpdCBvdXQ="}`)) 274 275 tt.opts.HttpClient = func() (*http.Client, error) { 276 return &http.Client{Transport: reg}, nil 277 } 278 279 io, _, stdout, stderr := iostreams.Test() 280 tt.opts.IO = io 281 282 t.Run(tt.name, func(t *testing.T) { 283 io.SetStdoutTTY(tt.stdoutTTY) 284 285 if err := viewRun(tt.opts); (err != nil) != tt.wantErr { 286 t.Errorf("viewRun() error = %v, wantErr %v", err, tt.wantErr) 287 } 288 assert.Equal(t, tt.wantStderr, stderr.String()) 289 assert.Equal(t, tt.wantOut, stdout.String()) 290 reg.Verify(t) 291 }) 292 } 293 } 294 295 func Test_ViewRun_NonMarkdownReadme(t *testing.T) { 296 tests := []struct { 297 name string 298 stdoutTTY bool 299 wantOut string 300 }{ 301 { 302 name: "tty", 303 wantOut: heredoc.Doc(` 304 OWNER/REPO 305 social distancing 306 307 # truly cool readme check it out 308 309 View this repository on GitHub: https://github.com/OWNER/REPO 310 `), 311 stdoutTTY: true, 312 }, 313 { 314 name: "nontty", 315 wantOut: heredoc.Doc(` 316 name: OWNER/REPO 317 description: social distancing 318 -- 319 # truly cool readme check it out 320 `), 321 }, 322 } 323 324 for _, tt := range tests { 325 reg := &httpmock.Registry{} 326 reg.Register( 327 httpmock.GraphQL(`query RepositoryInfo\b`), 328 httpmock.StringResponse(` 329 { "data": { 330 "repository": { 331 "description": "social distancing" 332 } } }`)) 333 reg.Register( 334 httpmock.REST("GET", "repos/OWNER/REPO/readme"), 335 httpmock.StringResponse(` 336 { "name": "readme.org", 337 "content": "IyB0cnVseSBjb29sIHJlYWRtZSBjaGVjayBpdCBvdXQ="}`)) 338 339 opts := &ViewOptions{ 340 HttpClient: func() (*http.Client, error) { 341 return &http.Client{Transport: reg}, nil 342 }, 343 BaseRepo: func() (ghrepo.Interface, error) { 344 return ghrepo.New("OWNER", "REPO"), nil 345 }, 346 } 347 348 io, _, stdout, stderr := iostreams.Test() 349 350 opts.IO = io 351 352 t.Run(tt.name, func(t *testing.T) { 353 io.SetStdoutTTY(tt.stdoutTTY) 354 355 if err := viewRun(opts); err != nil { 356 t.Errorf("viewRun() error = %v", err) 357 } 358 assert.Equal(t, tt.wantOut, stdout.String()) 359 assert.Equal(t, "", stderr.String()) 360 reg.Verify(t) 361 }) 362 } 363 } 364 365 func Test_ViewRun_NoReadme(t *testing.T) { 366 tests := []struct { 367 name string 368 stdoutTTY bool 369 wantOut string 370 }{ 371 { 372 name: "tty", 373 wantOut: heredoc.Doc(` 374 OWNER/REPO 375 social distancing 376 377 This repository does not have a README 378 379 View this repository on GitHub: https://github.com/OWNER/REPO 380 `), 381 stdoutTTY: true, 382 }, 383 { 384 name: "nontty", 385 wantOut: heredoc.Doc(` 386 name: OWNER/REPO 387 description: social distancing 388 `), 389 }, 390 } 391 392 for _, tt := range tests { 393 reg := &httpmock.Registry{} 394 reg.Register( 395 httpmock.GraphQL(`query RepositoryInfo\b`), 396 httpmock.StringResponse(` 397 { "data": { 398 "repository": { 399 "description": "social distancing" 400 } } }`)) 401 reg.Register( 402 httpmock.REST("GET", "repos/OWNER/REPO/readme"), 403 httpmock.StatusStringResponse(404, `{}`)) 404 405 opts := &ViewOptions{ 406 HttpClient: func() (*http.Client, error) { 407 return &http.Client{Transport: reg}, nil 408 }, 409 BaseRepo: func() (ghrepo.Interface, error) { 410 return ghrepo.New("OWNER", "REPO"), nil 411 }, 412 } 413 414 io, _, stdout, stderr := iostreams.Test() 415 416 opts.IO = io 417 418 t.Run(tt.name, func(t *testing.T) { 419 io.SetStdoutTTY(tt.stdoutTTY) 420 421 if err := viewRun(opts); err != nil { 422 t.Errorf("viewRun() error = %v", err) 423 } 424 assert.Equal(t, tt.wantOut, stdout.String()) 425 assert.Equal(t, "", stderr.String()) 426 reg.Verify(t) 427 }) 428 } 429 } 430 431 func Test_ViewRun_NoDescription(t *testing.T) { 432 tests := []struct { 433 name string 434 stdoutTTY bool 435 wantOut string 436 }{ 437 { 438 name: "tty", 439 wantOut: heredoc.Doc(` 440 OWNER/REPO 441 No description provided 442 443 # truly cool readme check it out 444 445 View this repository on GitHub: https://github.com/OWNER/REPO 446 `), 447 stdoutTTY: true, 448 }, 449 { 450 name: "nontty", 451 wantOut: heredoc.Doc(` 452 name: OWNER/REPO 453 description: 454 -- 455 # truly cool readme check it out 456 `), 457 }, 458 } 459 460 for _, tt := range tests { 461 reg := &httpmock.Registry{} 462 reg.Register( 463 httpmock.GraphQL(`query RepositoryInfo\b`), 464 httpmock.StringResponse(` 465 { "data": { 466 "repository": { 467 "description": "" 468 } } }`)) 469 reg.Register( 470 httpmock.REST("GET", "repos/OWNER/REPO/readme"), 471 httpmock.StringResponse(` 472 { "name": "readme.org", 473 "content": "IyB0cnVseSBjb29sIHJlYWRtZSBjaGVjayBpdCBvdXQ="}`)) 474 475 opts := &ViewOptions{ 476 HttpClient: func() (*http.Client, error) { 477 return &http.Client{Transport: reg}, nil 478 }, 479 BaseRepo: func() (ghrepo.Interface, error) { 480 return ghrepo.New("OWNER", "REPO"), nil 481 }, 482 } 483 484 io, _, stdout, stderr := iostreams.Test() 485 486 opts.IO = io 487 488 t.Run(tt.name, func(t *testing.T) { 489 io.SetStdoutTTY(tt.stdoutTTY) 490 491 if err := viewRun(opts); err != nil { 492 t.Errorf("viewRun() error = %v", err) 493 } 494 assert.Equal(t, tt.wantOut, stdout.String()) 495 assert.Equal(t, "", stderr.String()) 496 reg.Verify(t) 497 }) 498 } 499 } 500 501 func Test_ViewRun_WithoutUsername(t *testing.T) { 502 reg := &httpmock.Registry{} 503 reg.Register( 504 httpmock.GraphQL(`query UserCurrent\b`), 505 httpmock.StringResponse(` 506 { "data": { "viewer": { 507 "login": "OWNER" 508 }}}`)) 509 reg.Register( 510 httpmock.GraphQL(`query RepositoryInfo\b`), 511 httpmock.StringResponse(` 512 { "data": { 513 "repository": { 514 "description": "social distancing" 515 } } }`)) 516 reg.Register( 517 httpmock.REST("GET", "repos/OWNER/REPO/readme"), 518 httpmock.StringResponse(` 519 { "name": "readme.md", 520 "content": "IyB0cnVseSBjb29sIHJlYWRtZSBjaGVjayBpdCBvdXQ="}`)) 521 522 io, _, stdout, stderr := iostreams.Test() 523 io.SetStdoutTTY(false) 524 525 opts := &ViewOptions{ 526 RepoArg: "REPO", 527 HttpClient: func() (*http.Client, error) { 528 return &http.Client{Transport: reg}, nil 529 }, 530 IO: io, 531 } 532 533 if err := viewRun(opts); err != nil { 534 t.Errorf("viewRun() error = %v", err) 535 } 536 537 assert.Equal(t, heredoc.Doc(` 538 name: OWNER/REPO 539 description: social distancing 540 -- 541 # truly cool readme check it out 542 `), stdout.String()) 543 assert.Equal(t, "", stderr.String()) 544 reg.Verify(t) 545 } 546 547 func Test_ViewRun_HandlesSpecialCharacters(t *testing.T) { 548 tests := []struct { 549 name string 550 opts *ViewOptions 551 repoName string 552 stdoutTTY bool 553 wantOut string 554 wantStderr string 555 wantErr bool 556 }{ 557 { 558 name: "nontty", 559 wantOut: heredoc.Doc(` 560 name: OWNER/REPO 561 description: Some basic special characters " & / < > ' 562 -- 563 # < is always > than & ' and " 564 `), 565 }, 566 { 567 name: "no args", 568 stdoutTTY: true, 569 wantOut: heredoc.Doc(` 570 OWNER/REPO 571 Some basic special characters " & / < > ' 572 573 574 # < is always > than & ' and " 575 576 577 578 View this repository on GitHub: https://github.com/OWNER/REPO 579 `), 580 }, 581 } 582 for _, tt := range tests { 583 if tt.opts == nil { 584 tt.opts = &ViewOptions{} 585 } 586 587 if tt.repoName == "" { 588 tt.repoName = "OWNER/REPO" 589 } 590 591 tt.opts.BaseRepo = func() (ghrepo.Interface, error) { 592 repo, _ := ghrepo.FromFullName(tt.repoName) 593 return repo, nil 594 } 595 596 reg := &httpmock.Registry{} 597 reg.Register( 598 httpmock.GraphQL(`query RepositoryInfo\b`), 599 httpmock.StringResponse(` 600 { "data": { 601 "repository": { 602 "description": "Some basic special characters \" & / < > '" 603 } } }`)) 604 reg.Register( 605 httpmock.REST("GET", fmt.Sprintf("repos/%s/readme", tt.repoName)), 606 httpmock.StringResponse(` 607 { "name": "readme.md", 608 "content": "IyA8IGlzIGFsd2F5cyA+IHRoYW4gJiAnIGFuZCAi"}`)) 609 610 tt.opts.HttpClient = func() (*http.Client, error) { 611 return &http.Client{Transport: reg}, nil 612 } 613 614 io, _, stdout, stderr := iostreams.Test() 615 tt.opts.IO = io 616 617 t.Run(tt.name, func(t *testing.T) { 618 io.SetStdoutTTY(tt.stdoutTTY) 619 620 if err := viewRun(tt.opts); (err != nil) != tt.wantErr { 621 t.Errorf("viewRun() error = %v, wantErr %v", err, tt.wantErr) 622 } 623 assert.Equal(t, tt.wantStderr, stderr.String()) 624 assert.Equal(t, tt.wantOut, stdout.String()) 625 reg.Verify(t) 626 }) 627 } 628 } 629 630 func Test_viewRun_json(t *testing.T) { 631 io, _, stdout, stderr := iostreams.Test() 632 io.SetStdoutTTY(false) 633 634 reg := &httpmock.Registry{} 635 defer reg.Verify(t) 636 reg.StubRepoInfoResponse("OWNER", "REPO", "main") 637 638 opts := &ViewOptions{ 639 IO: io, 640 HttpClient: func() (*http.Client, error) { 641 return &http.Client{Transport: reg}, nil 642 }, 643 BaseRepo: func() (ghrepo.Interface, error) { 644 return ghrepo.New("OWNER", "REPO"), nil 645 }, 646 Exporter: &testExporter{ 647 fields: []string{"name", "defaultBranchRef"}, 648 }, 649 } 650 651 _, teardown := run.Stub() 652 defer teardown(t) 653 654 err := viewRun(opts) 655 assert.NoError(t, err) 656 assert.Equal(t, heredoc.Doc(` 657 name: REPO 658 defaultBranchRef: main 659 `), stdout.String()) 660 assert.Equal(t, "", stderr.String()) 661 } 662 663 type testExporter struct { 664 fields []string 665 } 666 667 func (e *testExporter) Fields() []string { 668 return e.fields 669 } 670 671 func (e *testExporter) Write(io *iostreams.IOStreams, data interface{}) error { 672 r := data.(*api.Repository) 673 fmt.Fprintf(io.Out, "name: %s\n", r.Name) 674 fmt.Fprintf(io.Out, "defaultBranchRef: %s\n", r.DefaultBranchRef.Name) 675 return nil 676 }