github.com/cli/cli@v1.14.1-0.20210902173923-1af6a669e342/pkg/cmd/issue/comment/comment_test.go (about)

     1  package comment
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"net/http"
     8  	"path/filepath"
     9  	"testing"
    10  
    11  	"github.com/cli/cli/internal/ghrepo"
    12  	"github.com/cli/cli/pkg/cmd/pr/shared"
    13  	"github.com/cli/cli/pkg/cmdutil"
    14  	"github.com/cli/cli/pkg/httpmock"
    15  	"github.com/cli/cli/pkg/iostreams"
    16  	"github.com/google/shlex"
    17  	"github.com/stretchr/testify/assert"
    18  	"github.com/stretchr/testify/require"
    19  )
    20  
    21  func TestNewCmdComment(t *testing.T) {
    22  	tmpFile := filepath.Join(t.TempDir(), "my-body.md")
    23  	err := ioutil.WriteFile(tmpFile, []byte("a body from file"), 0600)
    24  	require.NoError(t, err)
    25  
    26  	tests := []struct {
    27  		name     string
    28  		input    string
    29  		stdin    string
    30  		output   shared.CommentableOptions
    31  		wantsErr bool
    32  	}{
    33  		{
    34  			name:     "no arguments",
    35  			input:    "",
    36  			output:   shared.CommentableOptions{},
    37  			wantsErr: true,
    38  		},
    39  		{
    40  			name:  "issue number",
    41  			input: "1",
    42  			output: shared.CommentableOptions{
    43  				Interactive: true,
    44  				InputType:   0,
    45  				Body:        "",
    46  			},
    47  			wantsErr: false,
    48  		},
    49  		{
    50  			name:  "issue url",
    51  			input: "https://github.com/OWNER/REPO/issues/12",
    52  			output: shared.CommentableOptions{
    53  				Interactive: true,
    54  				InputType:   0,
    55  				Body:        "",
    56  			},
    57  			wantsErr: false,
    58  		},
    59  		{
    60  			name:  "body flag",
    61  			input: "1 --body test",
    62  			output: shared.CommentableOptions{
    63  				Interactive: false,
    64  				InputType:   shared.InputTypeInline,
    65  				Body:        "test",
    66  			},
    67  			wantsErr: false,
    68  		},
    69  		{
    70  			name:  "body from stdin",
    71  			input: "1 --body-file -",
    72  			stdin: "this is on standard input",
    73  			output: shared.CommentableOptions{
    74  				Interactive: false,
    75  				InputType:   shared.InputTypeInline,
    76  				Body:        "this is on standard input",
    77  			},
    78  			wantsErr: false,
    79  		},
    80  		{
    81  			name:  "body from file",
    82  			input: fmt.Sprintf("1 --body-file '%s'", tmpFile),
    83  			output: shared.CommentableOptions{
    84  				Interactive: false,
    85  				InputType:   shared.InputTypeInline,
    86  				Body:        "a body from file",
    87  			},
    88  			wantsErr: false,
    89  		},
    90  		{
    91  			name:  "editor flag",
    92  			input: "1 --editor",
    93  			output: shared.CommentableOptions{
    94  				Interactive: false,
    95  				InputType:   shared.InputTypeEditor,
    96  				Body:        "",
    97  			},
    98  			wantsErr: false,
    99  		},
   100  		{
   101  			name:  "web flag",
   102  			input: "1 --web",
   103  			output: shared.CommentableOptions{
   104  				Interactive: false,
   105  				InputType:   shared.InputTypeWeb,
   106  				Body:        "",
   107  			},
   108  			wantsErr: false,
   109  		},
   110  		{
   111  			name:     "body and body-file flags",
   112  			input:    "1 --body 'test' --body-file 'test-file.txt'",
   113  			output:   shared.CommentableOptions{},
   114  			wantsErr: true,
   115  		},
   116  		{
   117  			name:     "editor and web flags",
   118  			input:    "1 --editor --web",
   119  			output:   shared.CommentableOptions{},
   120  			wantsErr: true,
   121  		},
   122  		{
   123  			name:     "editor and body flags",
   124  			input:    "1 --editor --body test",
   125  			output:   shared.CommentableOptions{},
   126  			wantsErr: true,
   127  		},
   128  		{
   129  			name:     "web and body flags",
   130  			input:    "1 --web --body test",
   131  			output:   shared.CommentableOptions{},
   132  			wantsErr: true,
   133  		},
   134  		{
   135  			name:     "editor, web, and body flags",
   136  			input:    "1 --editor --web --body test",
   137  			output:   shared.CommentableOptions{},
   138  			wantsErr: true,
   139  		},
   140  	}
   141  
   142  	for _, tt := range tests {
   143  		t.Run(tt.name, func(t *testing.T) {
   144  			io, stdin, _, _ := iostreams.Test()
   145  			io.SetStdoutTTY(true)
   146  			io.SetStdinTTY(true)
   147  			io.SetStderrTTY(true)
   148  
   149  			if tt.stdin != "" {
   150  				_, _ = stdin.WriteString(tt.stdin)
   151  			}
   152  
   153  			f := &cmdutil.Factory{
   154  				IOStreams: io,
   155  				Browser:   &cmdutil.TestBrowser{},
   156  			}
   157  
   158  			argv, err := shlex.Split(tt.input)
   159  			assert.NoError(t, err)
   160  
   161  			var gotOpts *shared.CommentableOptions
   162  			cmd := NewCmdComment(f, func(opts *shared.CommentableOptions) error {
   163  				gotOpts = opts
   164  				return nil
   165  			})
   166  			cmd.Flags().BoolP("help", "x", false, "")
   167  
   168  			cmd.SetArgs(argv)
   169  			cmd.SetIn(&bytes.Buffer{})
   170  			cmd.SetOut(&bytes.Buffer{})
   171  			cmd.SetErr(&bytes.Buffer{})
   172  
   173  			_, err = cmd.ExecuteC()
   174  			if tt.wantsErr {
   175  				assert.Error(t, err)
   176  				return
   177  			}
   178  
   179  			assert.NoError(t, err)
   180  			assert.Equal(t, tt.output.Interactive, gotOpts.Interactive)
   181  			assert.Equal(t, tt.output.InputType, gotOpts.InputType)
   182  			assert.Equal(t, tt.output.Body, gotOpts.Body)
   183  		})
   184  	}
   185  }
   186  
   187  func Test_commentRun(t *testing.T) {
   188  	tests := []struct {
   189  		name      string
   190  		input     *shared.CommentableOptions
   191  		httpStubs func(*testing.T, *httpmock.Registry)
   192  		stdout    string
   193  		stderr    string
   194  	}{
   195  		{
   196  			name: "interactive editor",
   197  			input: &shared.CommentableOptions{
   198  				Interactive: true,
   199  				InputType:   0,
   200  				Body:        "",
   201  
   202  				InteractiveEditSurvey: func() (string, error) { return "comment body", nil },
   203  				ConfirmSubmitSurvey:   func() (bool, error) { return true, nil },
   204  			},
   205  			httpStubs: func(t *testing.T, reg *httpmock.Registry) {
   206  				mockIssueFromNumber(t, reg)
   207  				mockCommentCreate(t, reg)
   208  			},
   209  			stdout: "https://github.com/OWNER/REPO/issues/123#issuecomment-456\n",
   210  		},
   211  		{
   212  			name: "non-interactive web",
   213  			input: &shared.CommentableOptions{
   214  				Interactive: false,
   215  				InputType:   shared.InputTypeWeb,
   216  				Body:        "",
   217  
   218  				OpenInBrowser: func(string) error { return nil },
   219  			},
   220  			httpStubs: func(t *testing.T, reg *httpmock.Registry) {
   221  				mockIssueFromNumber(t, reg)
   222  			},
   223  			stderr: "Opening github.com/OWNER/REPO/issues/123 in your browser.\n",
   224  		},
   225  		{
   226  			name: "non-interactive editor",
   227  			input: &shared.CommentableOptions{
   228  				Interactive: false,
   229  				InputType:   shared.InputTypeEditor,
   230  				Body:        "",
   231  
   232  				EditSurvey: func() (string, error) { return "comment body", nil },
   233  			},
   234  			httpStubs: func(t *testing.T, reg *httpmock.Registry) {
   235  				mockIssueFromNumber(t, reg)
   236  				mockCommentCreate(t, reg)
   237  			},
   238  			stdout: "https://github.com/OWNER/REPO/issues/123#issuecomment-456\n",
   239  		},
   240  		{
   241  			name: "non-interactive inline",
   242  			input: &shared.CommentableOptions{
   243  				Interactive: false,
   244  				InputType:   shared.InputTypeInline,
   245  				Body:        "comment body",
   246  			},
   247  			httpStubs: func(t *testing.T, reg *httpmock.Registry) {
   248  				mockIssueFromNumber(t, reg)
   249  				mockCommentCreate(t, reg)
   250  			},
   251  			stdout: "https://github.com/OWNER/REPO/issues/123#issuecomment-456\n",
   252  		},
   253  	}
   254  	for _, tt := range tests {
   255  		io, _, stdout, stderr := iostreams.Test()
   256  		io.SetStdoutTTY(true)
   257  		io.SetStdinTTY(true)
   258  		io.SetStderrTTY(true)
   259  
   260  		reg := &httpmock.Registry{}
   261  		defer reg.Verify(t)
   262  		tt.httpStubs(t, reg)
   263  
   264  		httpClient := func() (*http.Client, error) { return &http.Client{Transport: reg}, nil }
   265  		baseRepo := func() (ghrepo.Interface, error) { return ghrepo.New("OWNER", "REPO"), nil }
   266  
   267  		tt.input.IO = io
   268  		tt.input.HttpClient = httpClient
   269  		tt.input.RetrieveCommentable = retrieveIssue(tt.input.HttpClient, baseRepo, "123")
   270  
   271  		t.Run(tt.name, func(t *testing.T) {
   272  			err := shared.CommentableRun(tt.input)
   273  			assert.NoError(t, err)
   274  			assert.Equal(t, tt.stdout, stdout.String())
   275  			assert.Equal(t, tt.stderr, stderr.String())
   276  		})
   277  	}
   278  }
   279  
   280  func mockIssueFromNumber(_ *testing.T, reg *httpmock.Registry) {
   281  	reg.Register(
   282  		httpmock.GraphQL(`query IssueByNumber\b`),
   283  		httpmock.StringResponse(`
   284  			{ "data": { "repository": { "hasIssuesEnabled": true, "issue": {
   285  				"number": 123,
   286  				"url": "https://github.com/OWNER/REPO/issues/123"
   287  			} } } }`),
   288  	)
   289  }
   290  
   291  func mockCommentCreate(t *testing.T, reg *httpmock.Registry) {
   292  	reg.Register(
   293  		httpmock.GraphQL(`mutation CommentCreate\b`),
   294  		httpmock.GraphQLMutation(`
   295  		{ "data": { "addComment": { "commentEdge": { "node": {
   296  			"url": "https://github.com/OWNER/REPO/issues/123#issuecomment-456"
   297  		} } } } }`,
   298  			func(inputs map[string]interface{}) {
   299  				assert.Equal(t, "comment body", inputs["body"])
   300  			}),
   301  	)
   302  }