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 }