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