github.com/andrewhsu/cli/v2@v2.0.1-0.20210910131313-d4b4061f5b89/pkg/cmd/gist/view/view_test.go (about)

     1  package view
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"net/http"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/andrewhsu/cli/v2/internal/config"
    11  	"github.com/andrewhsu/cli/v2/internal/ghinstance"
    12  	"github.com/andrewhsu/cli/v2/pkg/cmd/gist/shared"
    13  	"github.com/andrewhsu/cli/v2/pkg/cmdutil"
    14  	"github.com/andrewhsu/cli/v2/pkg/httpmock"
    15  	"github.com/andrewhsu/cli/v2/pkg/iostreams"
    16  	"github.com/andrewhsu/cli/v2/pkg/prompt"
    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  		wants ViewOptions
    26  		tty   bool
    27  	}{
    28  		{
    29  			name: "tty no arguments",
    30  			tty:  true,
    31  			cli:  "123",
    32  			wants: ViewOptions{
    33  				Raw:       false,
    34  				Selector:  "123",
    35  				ListFiles: false,
    36  			},
    37  		},
    38  		{
    39  			name: "nontty no arguments",
    40  			cli:  "123",
    41  			wants: ViewOptions{
    42  				Raw:       true,
    43  				Selector:  "123",
    44  				ListFiles: false,
    45  			},
    46  		},
    47  		{
    48  			name: "filename passed",
    49  			cli:  "-fcool.txt 123",
    50  			tty:  true,
    51  			wants: ViewOptions{
    52  				Raw:       false,
    53  				Selector:  "123",
    54  				Filename:  "cool.txt",
    55  				ListFiles: false,
    56  			},
    57  		},
    58  		{
    59  			name: "files passed",
    60  			cli:  "--files 123",
    61  			tty:  true,
    62  			wants: ViewOptions{
    63  				Raw:       false,
    64  				Selector:  "123",
    65  				ListFiles: true,
    66  			},
    67  		},
    68  		{
    69  			name: "tty no ID supplied",
    70  			cli:  "",
    71  			tty:  true,
    72  			wants: ViewOptions{
    73  				Raw:       false,
    74  				Selector:  "",
    75  				ListFiles: true,
    76  			},
    77  		},
    78  	}
    79  
    80  	for _, tt := range tests {
    81  		t.Run(tt.name, func(t *testing.T) {
    82  			io, _, _, _ := iostreams.Test()
    83  			io.SetStdoutTTY(tt.tty)
    84  
    85  			f := &cmdutil.Factory{
    86  				IOStreams: io,
    87  			}
    88  
    89  			argv, err := shlex.Split(tt.cli)
    90  			assert.NoError(t, err)
    91  
    92  			var gotOpts *ViewOptions
    93  			cmd := NewCmdView(f, func(opts *ViewOptions) error {
    94  				gotOpts = opts
    95  				return nil
    96  			})
    97  			cmd.SetArgs(argv)
    98  			cmd.SetIn(&bytes.Buffer{})
    99  			cmd.SetOut(&bytes.Buffer{})
   100  			cmd.SetErr(&bytes.Buffer{})
   101  
   102  			_, err = cmd.ExecuteC()
   103  			assert.NoError(t, err)
   104  
   105  			assert.Equal(t, tt.wants.Raw, gotOpts.Raw)
   106  			assert.Equal(t, tt.wants.Selector, gotOpts.Selector)
   107  			assert.Equal(t, tt.wants.Filename, gotOpts.Filename)
   108  		})
   109  	}
   110  }
   111  
   112  func Test_viewRun(t *testing.T) {
   113  	tests := []struct {
   114  		name         string
   115  		opts         *ViewOptions
   116  		wantOut      string
   117  		gist         *shared.Gist
   118  		wantErr      bool
   119  		mockGistList bool
   120  	}{
   121  		{
   122  			name: "no such gist",
   123  			opts: &ViewOptions{
   124  				Selector:  "1234",
   125  				ListFiles: false,
   126  			},
   127  			wantErr: true,
   128  		},
   129  		{
   130  			name: "one file",
   131  			opts: &ViewOptions{
   132  				Selector:  "1234",
   133  				ListFiles: false,
   134  			},
   135  			gist: &shared.Gist{
   136  				Files: map[string]*shared.GistFile{
   137  					"cicada.txt": {
   138  						Content: "bwhiizzzbwhuiiizzzz",
   139  						Type:    "text/plain",
   140  					},
   141  				},
   142  			},
   143  			wantOut: "bwhiizzzbwhuiiizzzz\n",
   144  		},
   145  		{
   146  			name: "one file, no ID supplied",
   147  			opts: &ViewOptions{
   148  				Selector:  "",
   149  				ListFiles: false,
   150  			},
   151  			mockGistList: true,
   152  			gist: &shared.Gist{
   153  				Files: map[string]*shared.GistFile{
   154  					"cicada.txt": {
   155  						Content: "test interactive mode",
   156  						Type:    "text/plain",
   157  					},
   158  				},
   159  			},
   160  			wantOut: "test interactive mode\n",
   161  		},
   162  		{
   163  			name: "filename selected",
   164  			opts: &ViewOptions{
   165  				Selector:  "1234",
   166  				Filename:  "cicada.txt",
   167  				ListFiles: false,
   168  			},
   169  			gist: &shared.Gist{
   170  				Files: map[string]*shared.GistFile{
   171  					"cicada.txt": {
   172  						Content: "bwhiizzzbwhuiiizzzz",
   173  						Type:    "text/plain",
   174  					},
   175  					"foo.md": {
   176  						Content: "# foo",
   177  						Type:    "application/markdown",
   178  					},
   179  				},
   180  			},
   181  			wantOut: "bwhiizzzbwhuiiizzzz\n",
   182  		},
   183  		{
   184  			name: "filename selected, raw",
   185  			opts: &ViewOptions{
   186  				Selector:  "1234",
   187  				Filename:  "cicada.txt",
   188  				Raw:       true,
   189  				ListFiles: false,
   190  			},
   191  			gist: &shared.Gist{
   192  				Files: map[string]*shared.GistFile{
   193  					"cicada.txt": {
   194  						Content: "bwhiizzzbwhuiiizzzz",
   195  						Type:    "text/plain",
   196  					},
   197  					"foo.md": {
   198  						Content: "# foo",
   199  						Type:    "application/markdown",
   200  					},
   201  				},
   202  			},
   203  			wantOut: "bwhiizzzbwhuiiizzzz\n",
   204  		},
   205  		{
   206  			name: "multiple files, no description",
   207  			opts: &ViewOptions{
   208  				Selector:  "1234",
   209  				ListFiles: false,
   210  			},
   211  			gist: &shared.Gist{
   212  				Files: map[string]*shared.GistFile{
   213  					"cicada.txt": {
   214  						Content: "bwhiizzzbwhuiiizzzz",
   215  						Type:    "text/plain",
   216  					},
   217  					"foo.md": {
   218  						Content: "# foo",
   219  						Type:    "application/markdown",
   220  					},
   221  				},
   222  			},
   223  			wantOut: "cicada.txt\n\nbwhiizzzbwhuiiizzzz\n\nfoo.md\n\n\n  # foo                                                                       \n\n",
   224  		},
   225  		{
   226  			name: "multiple files, trailing newlines",
   227  			opts: &ViewOptions{
   228  				Selector:  "1234",
   229  				ListFiles: false,
   230  			},
   231  			gist: &shared.Gist{
   232  				Files: map[string]*shared.GistFile{
   233  					"cicada.txt": {
   234  						Content: "bwhiizzzbwhuiiizzzz\n",
   235  						Type:    "text/plain",
   236  					},
   237  					"foo.txt": {
   238  						Content: "bar\n",
   239  						Type:    "text/plain",
   240  					},
   241  				},
   242  			},
   243  			wantOut: "cicada.txt\n\nbwhiizzzbwhuiiizzzz\n\nfoo.txt\n\nbar\n",
   244  		},
   245  		{
   246  			name: "multiple files, description",
   247  			opts: &ViewOptions{
   248  				Selector:  "1234",
   249  				ListFiles: false,
   250  			},
   251  			gist: &shared.Gist{
   252  				Description: "some files",
   253  				Files: map[string]*shared.GistFile{
   254  					"cicada.txt": {
   255  						Content: "bwhiizzzbwhuiiizzzz",
   256  						Type:    "text/plain",
   257  					},
   258  					"foo.md": {
   259  						Content: "- foo",
   260  						Type:    "application/markdown",
   261  					},
   262  				},
   263  			},
   264  			wantOut: "some files\n\ncicada.txt\n\nbwhiizzzbwhuiiizzzz\n\nfoo.md\n\n\n                                                                              \n  • foo                                                                       \n\n",
   265  		},
   266  		{
   267  			name: "multiple files, raw",
   268  			opts: &ViewOptions{
   269  				Selector:  "1234",
   270  				Raw:       true,
   271  				ListFiles: false,
   272  			},
   273  			gist: &shared.Gist{
   274  				Description: "some files",
   275  				Files: map[string]*shared.GistFile{
   276  					"cicada.txt": {
   277  						Content: "bwhiizzzbwhuiiizzzz",
   278  						Type:    "text/plain",
   279  					},
   280  					"foo.md": {
   281  						Content: "- foo",
   282  						Type:    "application/markdown",
   283  					},
   284  				},
   285  			},
   286  			wantOut: "some files\n\ncicada.txt\n\nbwhiizzzbwhuiiizzzz\n\nfoo.md\n\n- foo\n",
   287  		},
   288  		{
   289  			name: "one file, list files",
   290  			opts: &ViewOptions{
   291  				Selector:  "1234",
   292  				Raw:       false,
   293  				ListFiles: true,
   294  			},
   295  			gist: &shared.Gist{
   296  				Description: "some files",
   297  				Files: map[string]*shared.GistFile{
   298  					"cicada.txt": {
   299  						Content: "bwhiizzzbwhuiiizzzz",
   300  						Type:    "text/plain",
   301  					},
   302  				},
   303  			},
   304  			wantOut: "cicada.txt\n",
   305  		},
   306  		{
   307  			name: "multiple file, list files",
   308  			opts: &ViewOptions{
   309  				Selector:  "1234",
   310  				Raw:       false,
   311  				ListFiles: true,
   312  			},
   313  			gist: &shared.Gist{
   314  				Description: "some files",
   315  				Files: map[string]*shared.GistFile{
   316  					"cicada.txt": {
   317  						Content: "bwhiizzzbwhuiiizzzz",
   318  						Type:    "text/plain",
   319  					},
   320  					"foo.md": {
   321  						Content: "- foo",
   322  						Type:    "application/markdown",
   323  					},
   324  				},
   325  			},
   326  			wantOut: "cicada.txt\nfoo.md\n",
   327  		},
   328  	}
   329  
   330  	for _, tt := range tests {
   331  		reg := &httpmock.Registry{}
   332  		if tt.gist == nil {
   333  			reg.Register(httpmock.REST("GET", "gists/1234"),
   334  				httpmock.StatusStringResponse(404, "Not Found"))
   335  		} else {
   336  			reg.Register(httpmock.REST("GET", "gists/1234"),
   337  				httpmock.JSONResponse(tt.gist))
   338  		}
   339  
   340  		if tt.mockGistList {
   341  			sixHours, _ := time.ParseDuration("6h")
   342  			sixHoursAgo := time.Now().Add(-sixHours)
   343  			reg.Register(
   344  				httpmock.GraphQL(`query GistList\b`),
   345  				httpmock.StringResponse(fmt.Sprintf(
   346  					`{ "data": { "viewer": { "gists": { "nodes": [
   347  							{
   348  								"name": "1234",
   349  								"files": [{ "name": "cool.txt" }],
   350  								"description": "",
   351  								"updatedAt": "%s",
   352  								"isPublic": true
   353  							}
   354  						] } } } }`,
   355  					sixHoursAgo.Format(time.RFC3339),
   356  				)),
   357  			)
   358  
   359  			as, surveyteardown := prompt.InitAskStubber()
   360  			defer surveyteardown()
   361  			as.StubOne(0)
   362  		}
   363  
   364  		if tt.opts == nil {
   365  			tt.opts = &ViewOptions{}
   366  		}
   367  
   368  		tt.opts.HttpClient = func() (*http.Client, error) {
   369  			return &http.Client{Transport: reg}, nil
   370  		}
   371  
   372  		tt.opts.Config = func() (config.Config, error) {
   373  			return config.NewBlankConfig(), nil
   374  		}
   375  
   376  		io, _, stdout, _ := iostreams.Test()
   377  		io.SetStdoutTTY(true)
   378  		tt.opts.IO = io
   379  
   380  		t.Run(tt.name, func(t *testing.T) {
   381  			err := viewRun(tt.opts)
   382  			if tt.wantErr {
   383  				assert.Error(t, err)
   384  				return
   385  			}
   386  			assert.NoError(t, err)
   387  
   388  			assert.Equal(t, tt.wantOut, stdout.String())
   389  			reg.Verify(t)
   390  		})
   391  	}
   392  }
   393  
   394  func Test_promptGists(t *testing.T) {
   395  	tests := []struct {
   396  		name      string
   397  		gistIndex int
   398  		response  string
   399  		wantOut   string
   400  		gist      *shared.Gist
   401  		wantErr   bool
   402  	}{
   403  		{
   404  			name:      "multiple files, select first gist",
   405  			gistIndex: 0,
   406  			response: `{ "data": { "viewer": { "gists": { "nodes": [
   407  							{
   408  								"name": "gistid1",
   409  								"files": [{ "name": "cool.txt" }],
   410  								"description": "",
   411  								"updatedAt": "%[1]v",
   412  								"isPublic": true
   413  							},
   414  							{
   415  								"name": "gistid2",
   416  								"files": [{ "name": "gistfile0.txt" }],
   417  								"description": "",
   418  								"updatedAt": "%[1]v",
   419  								"isPublic": true
   420  							}
   421  						] } } } }`,
   422  			wantOut: "gistid1",
   423  		},
   424  		{
   425  			name:      "multiple files, select second gist",
   426  			gistIndex: 1,
   427  			response: `{ "data": { "viewer": { "gists": { "nodes": [
   428  							{
   429  								"name": "gistid1",
   430  								"files": [{ "name": "cool.txt" }],
   431  								"description": "",
   432  								"updatedAt": "%[1]v",
   433  								"isPublic": true
   434  							},
   435  							{
   436  								"name": "gistid2",
   437  								"files": [{ "name": "gistfile0.txt" }],
   438  								"description": "",
   439  								"updatedAt": "%[1]v",
   440  								"isPublic": true
   441  							}
   442  						] } } } }`,
   443  			wantOut: "gistid2",
   444  		},
   445  		{
   446  			name:     "no files",
   447  			response: `{ "data": { "viewer": { "gists": { "nodes": [] } } } }`,
   448  			wantOut:  "",
   449  		},
   450  	}
   451  
   452  	io, _, _, _ := iostreams.Test()
   453  
   454  	for _, tt := range tests {
   455  		reg := &httpmock.Registry{}
   456  
   457  		const query = `query GistList\b`
   458  		sixHours, _ := time.ParseDuration("6h")
   459  		sixHoursAgo := time.Now().Add(-sixHours)
   460  		reg.Register(
   461  			httpmock.GraphQL(query),
   462  			httpmock.StringResponse(fmt.Sprintf(
   463  				tt.response,
   464  				sixHoursAgo.Format(time.RFC3339),
   465  			)),
   466  		)
   467  		client := &http.Client{Transport: reg}
   468  
   469  		as, surveyteardown := prompt.InitAskStubber()
   470  		defer surveyteardown()
   471  		as.StubOne(tt.gistIndex)
   472  
   473  		t.Run(tt.name, func(t *testing.T) {
   474  			gistID, err := promptGists(client, ghinstance.Default(), io.ColorScheme())
   475  			assert.NoError(t, err)
   476  			assert.Equal(t, tt.wantOut, gistID)
   477  			reg.Verify(t)
   478  		})
   479  	}
   480  }