github.com/ungtb10d/cli/v2@v2.0.0-20221110210412-98537dd9d6a1/pkg/cmd/label/list_test.go (about)

     1  package label
     2  
     3  import (
     4  	"bytes"
     5  	"net/http"
     6  	"testing"
     7  
     8  	"github.com/MakeNowJust/heredoc"
     9  	"github.com/ungtb10d/cli/v2/internal/browser"
    10  	"github.com/ungtb10d/cli/v2/internal/ghrepo"
    11  	"github.com/ungtb10d/cli/v2/pkg/cmdutil"
    12  	"github.com/ungtb10d/cli/v2/pkg/httpmock"
    13  	"github.com/ungtb10d/cli/v2/pkg/iostreams"
    14  	"github.com/google/shlex"
    15  	"github.com/stretchr/testify/assert"
    16  )
    17  
    18  func TestNewCmdList(t *testing.T) {
    19  	tests := []struct {
    20  		name    string
    21  		input   string
    22  		output  listOptions
    23  		wantErr bool
    24  		errMsg  string
    25  	}{
    26  		{
    27  			name:   "no argument",
    28  			input:  "",
    29  			output: listOptions{Query: listQueryOptions{Limit: 30, Order: "asc", Sort: "created"}},
    30  		},
    31  		{
    32  			name:   "limit flag",
    33  			input:  "--limit 10",
    34  			output: listOptions{Query: listQueryOptions{Limit: 10, Order: "asc", Sort: "created"}},
    35  		},
    36  		{
    37  			name:    "invalid limit flag",
    38  			input:   "--limit 0",
    39  			wantErr: true,
    40  			errMsg:  "invalid limit: 0",
    41  		},
    42  		{
    43  			name:   "web flag",
    44  			input:  "--web",
    45  			output: listOptions{Query: listQueryOptions{Limit: 30, Order: "asc", Sort: "created"}, WebMode: true},
    46  		},
    47  		{
    48  			name:   "search flag",
    49  			input:  "--search core",
    50  			output: listOptions{Query: listQueryOptions{Limit: 30, Query: "core", Order: "asc", Sort: "created"}},
    51  		},
    52  		{
    53  			name:   "sort name flag",
    54  			input:  "--sort name",
    55  			output: listOptions{Query: listQueryOptions{Limit: 30, Order: "asc", Sort: "name"}},
    56  		},
    57  		{
    58  			name:   "sort created flag",
    59  			input:  "--sort created",
    60  			output: listOptions{Query: listQueryOptions{Limit: 30, Order: "asc", Sort: "created"}},
    61  		},
    62  		{
    63  			name:    "sort invalid flag",
    64  			input:   "--sort invalid",
    65  			wantErr: true,
    66  			errMsg:  `invalid argument "invalid" for "--sort" flag: valid values are {created|name}`,
    67  		},
    68  		{
    69  			name:   "order asc flag",
    70  			input:  "--order asc",
    71  			output: listOptions{Query: listQueryOptions{Limit: 30, Order: "asc", Sort: "created"}},
    72  		},
    73  		{
    74  			name:   "order desc flag",
    75  			input:  "--order desc",
    76  			output: listOptions{Query: listQueryOptions{Limit: 30, Order: "desc", Sort: "created"}},
    77  		},
    78  		{
    79  			name:    "order invalid flag",
    80  			input:   "--order invalid",
    81  			wantErr: true,
    82  			errMsg:  `invalid argument "invalid" for "--order" flag: valid values are {asc|desc}`,
    83  		},
    84  		{
    85  			name:    "search flag with sort flag",
    86  			input:   "--search test --sort name",
    87  			wantErr: true,
    88  			errMsg:  "cannot specify `--order` or `--sort` with `--search`",
    89  		},
    90  		{
    91  			name:    "search flag with order flag",
    92  			input:   "--search test --order asc",
    93  			wantErr: true,
    94  			errMsg:  "cannot specify `--order` or `--sort` with `--search`",
    95  		},
    96  		{
    97  			name:    "search flag with order and sort flags",
    98  			input:   "--search test --order asc --sort name",
    99  			wantErr: true,
   100  			errMsg:  "cannot specify `--order` or `--sort` with `--search`",
   101  		},
   102  		{
   103  			name:  "json flag",
   104  			input: "--json name,color,description,createdAt",
   105  			output: listOptions{
   106  				Query: listQueryOptions{
   107  					Limit: 30,
   108  					Order: "asc",
   109  					Sort:  "created",
   110  					fields: []string{
   111  						"name",
   112  						"color",
   113  						"description",
   114  						"createdAt",
   115  					},
   116  				},
   117  			},
   118  		},
   119  		{
   120  			name:    "invalid json flag",
   121  			input:   "--json invalid",
   122  			wantErr: true,
   123  			errMsg: heredoc.Doc(`
   124  				Unknown JSON field: "invalid"
   125  				Available fields:
   126  				  color
   127  				  createdAt
   128  				  description
   129  				  id
   130  				  isDefault
   131  				  name
   132  				  updatedAt
   133  				  url`),
   134  		},
   135  	}
   136  
   137  	for _, tt := range tests {
   138  		t.Run(tt.name, func(t *testing.T) {
   139  			ios, _, _, _ := iostreams.Test()
   140  			f := &cmdutil.Factory{
   141  				IOStreams: ios,
   142  			}
   143  			argv, err := shlex.Split(tt.input)
   144  			assert.NoError(t, err)
   145  			var gotOpts *listOptions
   146  			cmd := newCmdList(f, func(opts *listOptions) error {
   147  				gotOpts = opts
   148  				return nil
   149  			})
   150  			cmd.SetArgs(argv)
   151  			cmd.SetIn(&bytes.Buffer{})
   152  			cmd.SetOut(&bytes.Buffer{})
   153  			cmd.SetErr(&bytes.Buffer{})
   154  
   155  			_, err = cmd.ExecuteC()
   156  			if tt.wantErr {
   157  				assert.EqualError(t, err, tt.errMsg)
   158  				return
   159  			}
   160  
   161  			assert.NoError(t, err)
   162  			assert.Equal(t, tt.output.Query.Limit, gotOpts.Query.Limit)
   163  			assert.Equal(t, tt.output.Query.Order, gotOpts.Query.Order)
   164  			assert.Equal(t, tt.output.Query.Query, tt.output.Query.Query)
   165  			assert.Equal(t, tt.output.Query.Sort, gotOpts.Query.Sort)
   166  			assert.Equal(t, tt.output.WebMode, gotOpts.WebMode)
   167  		})
   168  	}
   169  }
   170  
   171  func TestListRun(t *testing.T) {
   172  	tests := []struct {
   173  		name       string
   174  		tty        bool
   175  		opts       *listOptions
   176  		httpStubs  func(*httpmock.Registry)
   177  		wantErr    bool
   178  		wantErrMsg string
   179  		wantStdout string
   180  		wantStderr string
   181  	}{
   182  		{
   183  			name: "lists labels",
   184  			tty:  true,
   185  			opts: &listOptions{},
   186  			httpStubs: func(reg *httpmock.Registry) {
   187  				reg.Register(
   188  					httpmock.GraphQL(`query LabelList\b`),
   189  					httpmock.StringResponse(`
   190  						{
   191  							"data": {
   192  								"repository": {
   193  									"labels": {
   194  										"totalCount": 2,
   195  										"nodes": [
   196  											{
   197  												"name": "bug",
   198  												"color": "d73a4a",
   199  												"description": "This is a bug label"
   200  											},
   201  											{
   202  												"name": "docs",
   203  												"color": "ffa8da",
   204  												"description": "This is a docs label"
   205  											}
   206  										],
   207  										"pageInfo": {
   208  											"hasNextPage": false,
   209  											"endCursor": "Y3Vyc29yOnYyOpK5MjAxOS0xMC0xMVQwMTozODowMyswODowMM5f3HZq"
   210  										}
   211  									}
   212  								}
   213  							}
   214  						}`,
   215  					),
   216  				)
   217  			},
   218  			wantStdout: "\nShowing 2 of 2 labels in OWNER/REPO\n\nbug   This is a bug label   #d73a4a\ndocs  This is a docs label  #ffa8da\n",
   219  		},
   220  		{
   221  			name: "lists labels notty",
   222  			tty:  false,
   223  			opts: &listOptions{},
   224  			httpStubs: func(reg *httpmock.Registry) {
   225  				reg.Register(
   226  					httpmock.GraphQL(`query LabelList\b`),
   227  					httpmock.StringResponse(`
   228  						{
   229  							"data": {
   230  								"repository": {
   231  									"labels": {
   232  										"totalCount": 2,
   233  										"nodes": [
   234  											{
   235  												"name": "bug",
   236  												"color": "d73a4a",
   237  												"description": "This is a bug label"
   238  											},
   239  											{
   240  												"name": "docs",
   241  												"color": "ffa8da",
   242  												"description": "This is a docs label"
   243  											}
   244  										],
   245  										"pageInfo": {
   246  											"hasNextPage": false,
   247  											"endCursor": "Y3Vyc29yOnYyOpK5MjAxOS0xMC0xMVQwMTozODowMyswODowMM5f3HZq"
   248  										}
   249  									}
   250  								}
   251  							}
   252  						}`,
   253  					),
   254  				)
   255  			},
   256  			wantStdout: "bug\tThis is a bug label\t#d73a4a\ndocs\tThis is a docs label\t#ffa8da\n",
   257  		},
   258  		{
   259  			name: "empty label list",
   260  			tty:  true,
   261  			opts: &listOptions{},
   262  			httpStubs: func(reg *httpmock.Registry) {
   263  				reg.Register(
   264  					httpmock.GraphQL(`query LabelList\b`),
   265  					httpmock.StringResponse(`
   266  						{"data":{"repository":{"labels":{"totalCount":0,"nodes":[],"pageInfo":{"hasNextPage":false,"endCursor":null}}}}}`,
   267  					),
   268  				)
   269  			},
   270  			wantErr:    true,
   271  			wantErrMsg: "no labels found in OWNER/REPO",
   272  		},
   273  		{
   274  			name: "empty label list search",
   275  			tty:  true,
   276  			opts: &listOptions{Query: listQueryOptions{Query: "test"}},
   277  			httpStubs: func(reg *httpmock.Registry) {
   278  				reg.Register(
   279  					httpmock.GraphQL(`query LabelList\b`),
   280  					httpmock.StringResponse(`
   281  						{"data":{"repository":{"labels":{"totalCount":0,"nodes":[],"pageInfo":{"hasNextPage":false,"endCursor":null}}}}}`,
   282  					),
   283  				)
   284  			},
   285  			wantErr:    true,
   286  			wantErrMsg: "no labels in OWNER/REPO matched your search",
   287  		},
   288  		{
   289  			name:       "web mode",
   290  			tty:        true,
   291  			opts:       &listOptions{WebMode: true},
   292  			wantStderr: "Opening github.com/OWNER/REPO/labels in your browser.\n",
   293  		},
   294  		{
   295  			name: "order by name ascending",
   296  			tty:  true,
   297  			opts: &listOptions{
   298  				Query: listQueryOptions{
   299  					Limit: 30,
   300  					Order: "asc",
   301  					Sort:  "name",
   302  				},
   303  			},
   304  			httpStubs: func(reg *httpmock.Registry) {
   305  				reg.Register(
   306  					httpmock.GraphQL(`query LabelList\b`),
   307  					httpmock.GraphQLQuery(`
   308  					{
   309  						"data": {
   310  							"repository": {
   311  								"labels": {
   312  									"totalCount": 2,
   313  									"nodes": [
   314  										{
   315  											"name": "bug",
   316  											"color": "d73a4a",
   317  											"description": "This is a bug label",
   318  											"createdAt": "2022-04-20T06:17:50Z"
   319  										},
   320  										{
   321  											"name": "docs",
   322  											"color": "ffa8da",
   323  											"description": "This is a docs label",
   324  											"createdAt": "2022-04-20T06:17:49Z"
   325  										}
   326  									],
   327  									"pageInfo": {
   328  										"hasNextPage": false,
   329  										"endCursor": "Y3Vyc29yOnYyOpK5MjAxOS0xMC0xMVQwMTozODowMyswODowMM5f3HZq"
   330  									}
   331  								}
   332  							}
   333  						}
   334  					}`, func(s string, m map[string]interface{}) {
   335  						assert.Equal(t, "OWNER", m["owner"])
   336  						assert.Equal(t, "REPO", m["repo"])
   337  						assert.Equal(t, float64(30), m["limit"].(float64))
   338  						assert.Equal(t, map[string]interface{}{"direction": "ASC", "field": "NAME"}, m["orderBy"])
   339  					}),
   340  				)
   341  			},
   342  			wantStdout: heredoc.Doc(`
   343  
   344  			Showing 2 of 2 labels in OWNER/REPO
   345  
   346  			bug   This is a bug label   #d73a4a
   347  			docs  This is a docs label  #ffa8da
   348  			`),
   349  		},
   350  	}
   351  
   352  	for _, tt := range tests {
   353  		t.Run(tt.name, func(t *testing.T) {
   354  			reg := &httpmock.Registry{}
   355  			if tt.httpStubs != nil {
   356  				tt.httpStubs(reg)
   357  			}
   358  			tt.opts.HttpClient = func() (*http.Client, error) {
   359  				return &http.Client{Transport: reg}, nil
   360  			}
   361  			ios, _, stdout, stderr := iostreams.Test()
   362  			ios.SetStdoutTTY(tt.tty)
   363  			ios.SetStdinTTY(tt.tty)
   364  			ios.SetStderrTTY(tt.tty)
   365  			tt.opts.IO = ios
   366  			tt.opts.Browser = &browser.Stub{}
   367  			tt.opts.BaseRepo = func() (ghrepo.Interface, error) {
   368  				return ghrepo.New("OWNER", "REPO"), nil
   369  			}
   370  			defer reg.Verify(t)
   371  			err := listRun(tt.opts)
   372  			if tt.wantErr {
   373  				if tt.wantErrMsg != "" {
   374  					assert.EqualError(t, err, tt.wantErrMsg)
   375  				} else {
   376  					assert.Error(t, err)
   377  				}
   378  			} else {
   379  				assert.NoError(t, err)
   380  			}
   381  			assert.Equal(t, tt.wantStdout, stdout.String())
   382  			assert.Equal(t, tt.wantStderr, stderr.String())
   383  		})
   384  	}
   385  }