github.com/ungtb10d/cli/v2@v2.0.0-20221110210412-98537dd9d6a1/pkg/cmd/workflow/view/view_test.go (about)

     1  package view
     2  
     3  import (
     4  	"bytes"
     5  	"io"
     6  	"net/http"
     7  	"testing"
     8  
     9  	"github.com/MakeNowJust/heredoc"
    10  	"github.com/ungtb10d/cli/v2/internal/browser"
    11  	"github.com/ungtb10d/cli/v2/internal/ghrepo"
    12  	runShared "github.com/ungtb10d/cli/v2/pkg/cmd/run/shared"
    13  	"github.com/ungtb10d/cli/v2/pkg/cmd/workflow/shared"
    14  	"github.com/ungtb10d/cli/v2/pkg/cmdutil"
    15  	"github.com/ungtb10d/cli/v2/pkg/httpmock"
    16  	"github.com/ungtb10d/cli/v2/pkg/iostreams"
    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  		tty      bool
    26  		wants    ViewOptions
    27  		wantsErr bool
    28  	}{
    29  		{
    30  			name: "blank tty",
    31  			tty:  true,
    32  			wants: ViewOptions{
    33  				Prompt: true,
    34  			},
    35  		},
    36  		{
    37  			name:     "blank nontty",
    38  			wantsErr: true,
    39  		},
    40  		{
    41  			name: "arg tty",
    42  			cli:  "123",
    43  			tty:  true,
    44  			wants: ViewOptions{
    45  				Selector: "123",
    46  			},
    47  		},
    48  		{
    49  			name: "arg nontty",
    50  			cli:  "123",
    51  			wants: ViewOptions{
    52  				Selector: "123",
    53  				Raw:      true,
    54  			},
    55  		},
    56  		{
    57  			name: "web tty",
    58  			cli:  "--web",
    59  			tty:  true,
    60  			wants: ViewOptions{
    61  				Prompt: true,
    62  				Web:    true,
    63  			},
    64  		},
    65  		{
    66  			name: "web nontty",
    67  			cli:  "-w 123",
    68  			wants: ViewOptions{
    69  				Raw:      true,
    70  				Web:      true,
    71  				Selector: "123",
    72  			},
    73  		},
    74  		{
    75  			name: "yaml tty",
    76  			cli:  "--yaml",
    77  			tty:  true,
    78  			wants: ViewOptions{
    79  				Prompt: true,
    80  				YAML:   true,
    81  			},
    82  		},
    83  		{
    84  			name: "yaml nontty",
    85  			cli:  "-y 123",
    86  			wants: ViewOptions{
    87  				Raw:      true,
    88  				YAML:     true,
    89  				Selector: "123",
    90  			},
    91  		},
    92  		{
    93  			name:     "ref tty",
    94  			cli:      "--ref 456",
    95  			tty:      true,
    96  			wantsErr: true,
    97  		},
    98  		{
    99  			name:     "ref nontty",
   100  			cli:      "123 -r 456",
   101  			wantsErr: true,
   102  		},
   103  		{
   104  			name: "yaml ref tty",
   105  			cli:  "--yaml --ref 456",
   106  			tty:  true,
   107  			wants: ViewOptions{
   108  				Prompt: true,
   109  				YAML:   true,
   110  				Ref:    "456",
   111  			},
   112  		},
   113  		{
   114  			name: "yaml ref nontty",
   115  			cli:  "123 -y -r 456",
   116  			wants: ViewOptions{
   117  				Raw:      true,
   118  				YAML:     true,
   119  				Ref:      "456",
   120  				Selector: "123",
   121  			},
   122  		},
   123  	}
   124  
   125  	for _, tt := range tests {
   126  		t.Run(tt.name, func(t *testing.T) {
   127  			ios, _, _, _ := iostreams.Test()
   128  			ios.SetStdinTTY(tt.tty)
   129  			ios.SetStdoutTTY(tt.tty)
   130  
   131  			f := &cmdutil.Factory{
   132  				IOStreams: ios,
   133  			}
   134  
   135  			argv, err := shlex.Split(tt.cli)
   136  			assert.NoError(t, err)
   137  
   138  			var gotOpts *ViewOptions
   139  			cmd := NewCmdView(f, func(opts *ViewOptions) error {
   140  				gotOpts = opts
   141  				return nil
   142  			})
   143  			cmd.SetArgs(argv)
   144  			cmd.SetIn(&bytes.Buffer{})
   145  			cmd.SetOut(io.Discard)
   146  			cmd.SetErr(io.Discard)
   147  
   148  			_, err = cmd.ExecuteC()
   149  			if tt.wantsErr {
   150  				assert.Error(t, err)
   151  				return
   152  			}
   153  
   154  			assert.NoError(t, err)
   155  			assert.Equal(t, tt.wants.Selector, gotOpts.Selector)
   156  			assert.Equal(t, tt.wants.Ref, gotOpts.Ref)
   157  			assert.Equal(t, tt.wants.Web, gotOpts.Web)
   158  			assert.Equal(t, tt.wants.Prompt, gotOpts.Prompt)
   159  			assert.Equal(t, tt.wants.Raw, gotOpts.Raw)
   160  			assert.Equal(t, tt.wants.YAML, gotOpts.YAML)
   161  		})
   162  	}
   163  }
   164  
   165  func TestViewRun(t *testing.T) {
   166  	aWorkflow := shared.Workflow{
   167  		Name:  "a workflow",
   168  		ID:    123,
   169  		Path:  ".github/workflows/flow.yml",
   170  		State: shared.Active,
   171  	}
   172  	aWorkflowContent := `{"content":"bmFtZTogYSB3b3JrZmxvdwo="}`
   173  	aWorkflowInfo := heredoc.Doc(`
   174  		a workflow - flow.yml
   175  		ID: 123
   176  
   177  		Total runs 10
   178  		Recent runs
   179  		X  cool commit  a workflow  trunk  push  1
   180  		*  cool commit  a workflow  trunk  push  2
   181  		✓  cool commit  a workflow  trunk  push  3
   182  		X  cool commit  a workflow  trunk  push  4
   183  
   184  		To see more runs for this workflow, try: gh run list --workflow flow.yml
   185  		To see the YAML for this workflow, try: gh workflow view flow.yml --yaml
   186  	`)
   187  
   188  	tests := []struct {
   189  		name       string
   190  		opts       *ViewOptions
   191  		httpStubs  func(*httpmock.Registry)
   192  		tty        bool
   193  		wantOut    string
   194  		wantErrOut string
   195  		wantErr    bool
   196  	}{
   197  		{
   198  			name: "no enabled workflows",
   199  			tty:  true,
   200  			opts: &ViewOptions{
   201  				Prompt: true,
   202  			},
   203  			httpStubs: func(reg *httpmock.Registry) {
   204  				reg.Register(
   205  					httpmock.REST("GET", "repos/OWNER/REPO/actions/workflows"),
   206  					httpmock.JSONResponse(shared.WorkflowsPayload{}))
   207  			},
   208  			wantErrOut: "could not fetch workflows for OWNER/REPO: no workflows are enabled",
   209  			wantErr:    true,
   210  		},
   211  		{
   212  			name: "web",
   213  			tty:  true,
   214  			opts: &ViewOptions{
   215  				Selector: "123",
   216  				Web:      true,
   217  			},
   218  			httpStubs: func(reg *httpmock.Registry) {
   219  				reg.Register(
   220  					httpmock.REST("GET", "repos/OWNER/REPO/actions/workflows/123"),
   221  					httpmock.JSONResponse(aWorkflow),
   222  				)
   223  			},
   224  			wantOut: "Opening github.com/OWNER/REPO/actions/workflows/flow.yml in your browser.\n",
   225  		},
   226  		{
   227  			name: "web notty",
   228  			tty:  false,
   229  			opts: &ViewOptions{
   230  				Selector: "123",
   231  				Web:      true,
   232  			},
   233  			httpStubs: func(reg *httpmock.Registry) {
   234  				reg.Register(
   235  					httpmock.REST("GET", "repos/OWNER/REPO/actions/workflows/123"),
   236  					httpmock.JSONResponse(aWorkflow),
   237  				)
   238  			},
   239  			wantOut: "",
   240  		},
   241  		{
   242  			name: "web with yaml",
   243  			tty:  true,
   244  			opts: &ViewOptions{
   245  				Selector: "123",
   246  				YAML:     true,
   247  				Web:      true,
   248  			},
   249  			httpStubs: func(reg *httpmock.Registry) {
   250  				reg.Register(
   251  					httpmock.REST("GET", "repos/OWNER/REPO/actions/workflows/123"),
   252  					httpmock.JSONResponse(aWorkflow),
   253  				)
   254  				reg.Register(
   255  					httpmock.GraphQL(`query RepositoryInfo\b`),
   256  					httpmock.StringResponse(`{ "data": { "repository": { "defaultBranchRef": { "name": "trunk" } } } }`),
   257  				)
   258  			},
   259  			wantOut: "Opening github.com/OWNER/REPO/blob/trunk/.github/workflows/flow.yml in your browser.\n",
   260  		},
   261  		{
   262  			name: "web with yaml and ref",
   263  			tty:  true,
   264  			opts: &ViewOptions{
   265  				Selector: "123",
   266  				Ref:      "base",
   267  				YAML:     true,
   268  				Web:      true,
   269  			},
   270  			httpStubs: func(reg *httpmock.Registry) {
   271  				reg.Register(
   272  					httpmock.REST("GET", "repos/OWNER/REPO/actions/workflows/123"),
   273  					httpmock.JSONResponse(aWorkflow),
   274  				)
   275  			},
   276  			wantOut: "Opening github.com/OWNER/REPO/blob/base/.github/workflows/flow.yml in your browser.\n",
   277  		},
   278  		{
   279  			name: "workflow with yaml",
   280  			tty:  true,
   281  			opts: &ViewOptions{
   282  				Selector: "123",
   283  				YAML:     true,
   284  			},
   285  			httpStubs: func(reg *httpmock.Registry) {
   286  				reg.Register(
   287  					httpmock.REST("GET", "repos/OWNER/REPO/actions/workflows/123"),
   288  					httpmock.JSONResponse(aWorkflow),
   289  				)
   290  				reg.Register(
   291  					httpmock.REST("GET", "repos/OWNER/REPO/contents/.github/workflows/flow.yml"),
   292  					httpmock.StringResponse(aWorkflowContent),
   293  				)
   294  			},
   295  			wantOut: "a workflow - flow.yml\nID: 123\n\nname: a workflow\n\n\n",
   296  		},
   297  		{
   298  			name: "workflow with yaml notty",
   299  			tty:  false,
   300  			opts: &ViewOptions{
   301  				Selector: "123",
   302  				YAML:     true,
   303  			},
   304  			httpStubs: func(reg *httpmock.Registry) {
   305  				reg.Register(
   306  					httpmock.REST("GET", "repos/OWNER/REPO/actions/workflows/123"),
   307  					httpmock.JSONResponse(aWorkflow),
   308  				)
   309  				reg.Register(
   310  					httpmock.REST("GET", "repos/OWNER/REPO/contents/.github/workflows/flow.yml"),
   311  					httpmock.StringResponse(aWorkflowContent),
   312  				)
   313  			},
   314  			wantOut: "a workflow - flow.yml\nID: 123\n\nname: a workflow\n\n\n",
   315  		},
   316  		{
   317  			name: "workflow with yaml not found",
   318  			tty:  true,
   319  			opts: &ViewOptions{
   320  				Selector: "123",
   321  				YAML:     true,
   322  			},
   323  			httpStubs: func(reg *httpmock.Registry) {
   324  				reg.Register(
   325  					httpmock.REST("GET", "repos/OWNER/REPO/actions/workflows/123"),
   326  					httpmock.JSONResponse(aWorkflow),
   327  				)
   328  				reg.Register(
   329  					httpmock.REST("GET", "repos/OWNER/REPO/contents/.github/workflows/flow.yml"),
   330  					httpmock.StatusStringResponse(404, "not Found"),
   331  				)
   332  			},
   333  			wantErr:    true,
   334  			wantErrOut: "could not find workflow file flow.yml, try specifying a branch or tag using `--ref`",
   335  		},
   336  		{
   337  			name: "workflow with yaml and ref",
   338  			tty:  true,
   339  			opts: &ViewOptions{
   340  				Selector: "123",
   341  				Ref:      "456",
   342  				YAML:     true,
   343  			},
   344  			httpStubs: func(reg *httpmock.Registry) {
   345  				reg.Register(
   346  					httpmock.REST("GET", "repos/OWNER/REPO/actions/workflows/123"),
   347  					httpmock.JSONResponse(aWorkflow),
   348  				)
   349  				reg.Register(
   350  					httpmock.REST("GET", "repos/OWNER/REPO/contents/.github/workflows/flow.yml"),
   351  					httpmock.StringResponse(aWorkflowContent),
   352  				)
   353  			},
   354  			wantOut: "a workflow - flow.yml\nID: 123\n\nname: a workflow\n\n\n",
   355  		},
   356  		{
   357  			name: "workflow info",
   358  			tty:  true,
   359  			opts: &ViewOptions{
   360  				Selector: "123",
   361  			},
   362  			httpStubs: func(reg *httpmock.Registry) {
   363  				reg.Register(
   364  					httpmock.REST("GET", "repos/OWNER/REPO/actions/workflows/123"),
   365  					httpmock.JSONResponse(aWorkflow),
   366  				)
   367  				reg.Register(
   368  					httpmock.REST("GET", "repos/OWNER/REPO/actions/workflows/123/runs"),
   369  					httpmock.JSONResponse(runShared.RunsPayload{
   370  						TotalCount:   10,
   371  						WorkflowRuns: runShared.TestRuns[0:4],
   372  					}),
   373  				)
   374  			},
   375  			wantOut: aWorkflowInfo,
   376  		},
   377  		{
   378  			name: "workflow info notty",
   379  			tty:  true,
   380  			opts: &ViewOptions{
   381  				Selector: "123",
   382  			},
   383  			httpStubs: func(reg *httpmock.Registry) {
   384  				reg.Register(
   385  					httpmock.REST("GET", "repos/OWNER/REPO/actions/workflows/123"),
   386  					httpmock.JSONResponse(aWorkflow),
   387  				)
   388  				reg.Register(
   389  					httpmock.REST("GET", "repos/OWNER/REPO/actions/workflows/123/runs"),
   390  					httpmock.JSONResponse(runShared.RunsPayload{
   391  						TotalCount:   10,
   392  						WorkflowRuns: runShared.TestRuns[0:4],
   393  					}),
   394  				)
   395  			},
   396  			wantOut: aWorkflowInfo,
   397  		},
   398  	}
   399  
   400  	for _, tt := range tests {
   401  		reg := &httpmock.Registry{}
   402  		tt.httpStubs(reg)
   403  		tt.opts.HttpClient = func() (*http.Client, error) {
   404  			return &http.Client{Transport: reg}, nil
   405  		}
   406  
   407  		ios, _, stdout, _ := iostreams.Test()
   408  		ios.SetStdoutTTY(tt.tty)
   409  		ios.SetStdinTTY(tt.tty)
   410  		tt.opts.IO = ios
   411  
   412  		tt.opts.BaseRepo = func() (ghrepo.Interface, error) {
   413  			return ghrepo.FromFullName("OWNER/REPO")
   414  		}
   415  
   416  		browser := &browser.Stub{}
   417  		tt.opts.Browser = browser
   418  
   419  		t.Run(tt.name, func(t *testing.T) {
   420  			err := runView(tt.opts)
   421  			if tt.wantErr {
   422  				assert.Error(t, err)
   423  				assert.Equal(t, tt.wantErrOut, err.Error())
   424  				return
   425  			}
   426  			assert.NoError(t, err)
   427  			assert.Equal(t, tt.wantOut, stdout.String())
   428  			reg.Verify(t)
   429  		})
   430  	}
   431  }