github.com/andrewhsu/cli/v2@v2.0.1-0.20210910131313-d4b4061f5b89/pkg/cmd/alias/set/set_test.go (about) 1 package set 2 3 import ( 4 "bytes" 5 "io/ioutil" 6 "testing" 7 8 "github.com/MakeNowJust/heredoc" 9 "github.com/andrewhsu/cli/v2/internal/config" 10 "github.com/andrewhsu/cli/v2/pkg/cmdutil" 11 "github.com/andrewhsu/cli/v2/pkg/extensions" 12 "github.com/andrewhsu/cli/v2/pkg/iostreams" 13 "github.com/andrewhsu/cli/v2/test" 14 "github.com/google/shlex" 15 "github.com/spf13/cobra" 16 "github.com/stretchr/testify/assert" 17 "github.com/stretchr/testify/require" 18 ) 19 20 func runCommand(cfg config.Config, isTTY bool, cli string, in string) (*test.CmdOut, error) { 21 io, stdin, stdout, stderr := iostreams.Test() 22 io.SetStdoutTTY(isTTY) 23 io.SetStdinTTY(isTTY) 24 io.SetStderrTTY(isTTY) 25 stdin.WriteString(in) 26 27 factory := &cmdutil.Factory{ 28 IOStreams: io, 29 Config: func() (config.Config, error) { 30 return cfg, nil 31 }, 32 ExtensionManager: &extensions.ExtensionManagerMock{ 33 ListFunc: func(bool) []extensions.Extension { 34 return []extensions.Extension{} 35 }, 36 }, 37 } 38 39 cmd := NewCmdSet(factory, nil) 40 41 // fake command nesting structure needed for validCommand 42 rootCmd := &cobra.Command{} 43 rootCmd.AddCommand(cmd) 44 prCmd := &cobra.Command{Use: "pr"} 45 prCmd.AddCommand(&cobra.Command{Use: "checkout"}) 46 prCmd.AddCommand(&cobra.Command{Use: "status"}) 47 rootCmd.AddCommand(prCmd) 48 issueCmd := &cobra.Command{Use: "issue"} 49 issueCmd.AddCommand(&cobra.Command{Use: "list"}) 50 rootCmd.AddCommand(issueCmd) 51 apiCmd := &cobra.Command{Use: "api"} 52 apiCmd.AddCommand(&cobra.Command{Use: "graphql"}) 53 rootCmd.AddCommand(apiCmd) 54 55 argv, err := shlex.Split("set " + cli) 56 if err != nil { 57 return nil, err 58 } 59 rootCmd.SetArgs(argv) 60 61 rootCmd.SetIn(stdin) 62 rootCmd.SetOut(ioutil.Discard) 63 rootCmd.SetErr(ioutil.Discard) 64 65 _, err = rootCmd.ExecuteC() 66 return &test.CmdOut{ 67 OutBuf: stdout, 68 ErrBuf: stderr, 69 }, err 70 } 71 72 func TestAliasSet_gh_command(t *testing.T) { 73 defer config.StubWriteConfig(ioutil.Discard, ioutil.Discard)() 74 75 cfg := config.NewFromString(``) 76 77 _, err := runCommand(cfg, true, "pr 'pr status'", "") 78 assert.EqualError(t, err, `could not create alias: "pr" is already a gh command`) 79 } 80 81 func TestAliasSet_empty_aliases(t *testing.T) { 82 mainBuf := bytes.Buffer{} 83 defer config.StubWriteConfig(&mainBuf, ioutil.Discard)() 84 85 cfg := config.NewFromString(heredoc.Doc(` 86 aliases: 87 editor: vim 88 `)) 89 90 output, err := runCommand(cfg, true, "co 'pr checkout'", "") 91 92 if err != nil { 93 t.Fatalf("unexpected error: %s", err) 94 } 95 96 //nolint:staticcheck // prefer exact matchers over ExpectLines 97 test.ExpectLines(t, output.Stderr(), "Added alias") 98 //nolint:staticcheck // prefer exact matchers over ExpectLines 99 test.ExpectLines(t, output.String(), "") 100 101 expected := `aliases: 102 co: pr checkout 103 editor: vim 104 ` 105 assert.Equal(t, expected, mainBuf.String()) 106 } 107 108 func TestAliasSet_existing_alias(t *testing.T) { 109 mainBuf := bytes.Buffer{} 110 defer config.StubWriteConfig(&mainBuf, ioutil.Discard)() 111 112 cfg := config.NewFromString(heredoc.Doc(` 113 aliases: 114 co: pr checkout 115 `)) 116 117 output, err := runCommand(cfg, true, "co 'pr checkout -Rcool/repo'", "") 118 require.NoError(t, err) 119 120 //nolint:staticcheck // prefer exact matchers over ExpectLines 121 test.ExpectLines(t, output.Stderr(), "Changed alias.*co.*from.*pr checkout.*to.*pr checkout -Rcool/repo") 122 } 123 124 func TestAliasSet_space_args(t *testing.T) { 125 mainBuf := bytes.Buffer{} 126 defer config.StubWriteConfig(&mainBuf, ioutil.Discard)() 127 128 cfg := config.NewFromString(``) 129 130 output, err := runCommand(cfg, true, `il 'issue list -l "cool story"'`, "") 131 require.NoError(t, err) 132 133 //nolint:staticcheck // prefer exact matchers over ExpectLines 134 test.ExpectLines(t, output.Stderr(), `Adding alias for.*il.*issue list -l "cool story"`) 135 136 //nolint:staticcheck // prefer exact matchers over ExpectLines 137 test.ExpectLines(t, mainBuf.String(), `il: issue list -l "cool story"`) 138 } 139 140 func TestAliasSet_arg_processing(t *testing.T) { 141 cases := []struct { 142 Cmd string 143 ExpectedOutputLine string 144 ExpectedConfigLine string 145 }{ 146 {`il "issue list"`, "- Adding alias for.*il.*issue list", "il: issue list"}, 147 148 {`iz 'issue list'`, "- Adding alias for.*iz.*issue list", "iz: issue list"}, 149 150 {`ii 'issue list --author="$1" --label="$2"'`, 151 `- Adding alias for.*ii.*issue list --author="\$1" --label="\$2"`, 152 `ii: issue list --author="\$1" --label="\$2"`}, 153 154 {`ix "issue list --author='\$1' --label='\$2'"`, 155 `- Adding alias for.*ix.*issue list --author='\$1' --label='\$2'`, 156 `ix: issue list --author='\$1' --label='\$2'`}, 157 } 158 159 for _, c := range cases { 160 t.Run(c.Cmd, func(t *testing.T) { 161 mainBuf := bytes.Buffer{} 162 defer config.StubWriteConfig(&mainBuf, ioutil.Discard)() 163 164 cfg := config.NewFromString(``) 165 166 output, err := runCommand(cfg, true, c.Cmd, "") 167 if err != nil { 168 t.Fatalf("got unexpected error running %s: %s", c.Cmd, err) 169 } 170 171 //nolint:staticcheck // prefer exact matchers over ExpectLines 172 test.ExpectLines(t, output.Stderr(), c.ExpectedOutputLine) 173 //nolint:staticcheck // prefer exact matchers over ExpectLines 174 test.ExpectLines(t, mainBuf.String(), c.ExpectedConfigLine) 175 }) 176 } 177 } 178 179 func TestAliasSet_init_alias_cfg(t *testing.T) { 180 mainBuf := bytes.Buffer{} 181 defer config.StubWriteConfig(&mainBuf, ioutil.Discard)() 182 183 cfg := config.NewFromString(heredoc.Doc(` 184 editor: vim 185 `)) 186 187 output, err := runCommand(cfg, true, "diff 'pr diff'", "") 188 require.NoError(t, err) 189 190 expected := `editor: vim 191 aliases: 192 diff: pr diff 193 ` 194 195 //nolint:staticcheck // prefer exact matchers over ExpectLines 196 test.ExpectLines(t, output.Stderr(), "Adding alias for.*diff.*pr diff", "Added alias.") 197 assert.Equal(t, expected, mainBuf.String()) 198 } 199 200 func TestAliasSet_existing_aliases(t *testing.T) { 201 mainBuf := bytes.Buffer{} 202 defer config.StubWriteConfig(&mainBuf, ioutil.Discard)() 203 204 cfg := config.NewFromString(heredoc.Doc(` 205 aliases: 206 foo: bar 207 `)) 208 209 output, err := runCommand(cfg, true, "view 'pr view'", "") 210 require.NoError(t, err) 211 212 expected := `aliases: 213 foo: bar 214 view: pr view 215 ` 216 217 //nolint:staticcheck // prefer exact matchers over ExpectLines 218 test.ExpectLines(t, output.Stderr(), "Adding alias for.*view.*pr view", "Added alias.") 219 assert.Equal(t, expected, mainBuf.String()) 220 221 } 222 223 func TestAliasSet_invalid_command(t *testing.T) { 224 defer config.StubWriteConfig(ioutil.Discard, ioutil.Discard)() 225 226 cfg := config.NewFromString(``) 227 228 _, err := runCommand(cfg, true, "co 'pe checkout'", "") 229 assert.EqualError(t, err, "could not create alias: pe checkout does not correspond to a gh command") 230 } 231 232 func TestShellAlias_flag(t *testing.T) { 233 mainBuf := bytes.Buffer{} 234 defer config.StubWriteConfig(&mainBuf, ioutil.Discard)() 235 236 cfg := config.NewFromString(``) 237 238 output, err := runCommand(cfg, true, "--shell igrep 'gh issue list | grep'", "") 239 if err != nil { 240 t.Fatalf("unexpected error: %s", err) 241 } 242 243 //nolint:staticcheck // prefer exact matchers over ExpectLines 244 test.ExpectLines(t, output.Stderr(), "Adding alias for.*igrep") 245 246 expected := `aliases: 247 igrep: '!gh issue list | grep' 248 ` 249 assert.Equal(t, expected, mainBuf.String()) 250 } 251 252 func TestShellAlias_bang(t *testing.T) { 253 mainBuf := bytes.Buffer{} 254 defer config.StubWriteConfig(&mainBuf, ioutil.Discard)() 255 256 cfg := config.NewFromString(``) 257 258 output, err := runCommand(cfg, true, "igrep '!gh issue list | grep'", "") 259 require.NoError(t, err) 260 261 //nolint:staticcheck // prefer exact matchers over ExpectLines 262 test.ExpectLines(t, output.Stderr(), "Adding alias for.*igrep") 263 264 expected := `aliases: 265 igrep: '!gh issue list | grep' 266 ` 267 assert.Equal(t, expected, mainBuf.String()) 268 } 269 270 func TestShellAlias_from_stdin(t *testing.T) { 271 mainBuf := bytes.Buffer{} 272 defer config.StubWriteConfig(&mainBuf, ioutil.Discard)() 273 274 cfg := config.NewFromString(``) 275 276 output, err := runCommand(cfg, true, "users -", `api graphql -F name="$1" -f query=' 277 query ($name: String!) { 278 user(login: $name) { 279 name 280 } 281 }'`) 282 283 require.NoError(t, err) 284 285 //nolint:staticcheck // prefer exact matchers over ExpectLines 286 test.ExpectLines(t, output.Stderr(), "Adding alias for.*users") 287 288 expected := `aliases: 289 users: |- 290 api graphql -F name="$1" -f query=' 291 query ($name: String!) { 292 user(login: $name) { 293 name 294 } 295 }' 296 ` 297 298 assert.Equal(t, expected, mainBuf.String()) 299 } 300 301 func TestShellAlias_getExpansion(t *testing.T) { 302 tests := []struct { 303 name string 304 want string 305 expansionArg string 306 stdin string 307 }{ 308 { 309 name: "co", 310 want: "pr checkout", 311 expansionArg: "pr checkout", 312 }, 313 { 314 name: "co", 315 want: "pr checkout", 316 expansionArg: "pr checkout", 317 stdin: "api graphql -F name=\"$1\"", 318 }, 319 { 320 name: "stdin", 321 expansionArg: "-", 322 want: "api graphql -F name=\"$1\"", 323 stdin: "api graphql -F name=\"$1\"", 324 }, 325 } 326 327 for _, tt := range tests { 328 t.Run(tt.name, func(t *testing.T) { 329 io, stdin, _, _ := iostreams.Test() 330 331 io.SetStdinTTY(false) 332 333 _, err := stdin.WriteString(tt.stdin) 334 assert.NoError(t, err) 335 336 expansion, err := getExpansion(&SetOptions{ 337 Expansion: tt.expansionArg, 338 IO: io, 339 }) 340 assert.NoError(t, err) 341 342 assert.Equal(t, expansion, tt.want) 343 }) 344 } 345 }