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  }