cli/pkg/cmd/issue/comment/comment_test.go
Mislav Marohnić 111e8dbcf2 Pass web browser to each individual command
This removes sensitivity to the BROWSER environment variable in tests
and makes it easier to verify the URL that the browser was invoked with
without having to stub sub-processes.
2021-03-19 21:22:37 +01:00

302 lines
7.4 KiB
Go

package comment
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"path/filepath"
"testing"
"github.com/cli/cli/internal/ghrepo"
"github.com/cli/cli/pkg/cmd/pr/shared"
"github.com/cli/cli/pkg/cmdutil"
"github.com/cli/cli/pkg/httpmock"
"github.com/cli/cli/pkg/iostreams"
"github.com/google/shlex"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestNewCmdComment(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := ioutil.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output shared.CommentableOptions
wantsErr bool
}{
{
name: "no arguments",
input: "",
output: shared.CommentableOptions{},
wantsErr: true,
},
{
name: "issue number",
input: "1",
output: shared.CommentableOptions{
Interactive: true,
InputType: 0,
Body: "",
},
wantsErr: false,
},
{
name: "issue url",
input: "https://github.com/OWNER/REPO/issues/12",
output: shared.CommentableOptions{
Interactive: true,
InputType: 0,
Body: "",
},
wantsErr: false,
},
{
name: "body flag",
input: "1 --body test",
output: shared.CommentableOptions{
Interactive: false,
InputType: shared.InputTypeInline,
Body: "test",
},
wantsErr: false,
},
{
name: "body from stdin",
input: "1 --body-file -",
stdin: "this is on standard input",
output: shared.CommentableOptions{
Interactive: false,
InputType: shared.InputTypeInline,
Body: "this is on standard input",
},
wantsErr: false,
},
{
name: "body from file",
input: fmt.Sprintf("1 --body-file '%s'", tmpFile),
output: shared.CommentableOptions{
Interactive: false,
InputType: shared.InputTypeInline,
Body: "a body from file",
},
wantsErr: false,
},
{
name: "editor flag",
input: "1 --editor",
output: shared.CommentableOptions{
Interactive: false,
InputType: shared.InputTypeEditor,
Body: "",
},
wantsErr: false,
},
{
name: "web flag",
input: "1 --web",
output: shared.CommentableOptions{
Interactive: false,
InputType: shared.InputTypeWeb,
Body: "",
},
wantsErr: false,
},
{
name: "body and body-file flags",
input: "1 --body 'test' --body-file 'test-file.txt'",
output: shared.CommentableOptions{},
wantsErr: true,
},
{
name: "editor and web flags",
input: "1 --editor --web",
output: shared.CommentableOptions{},
wantsErr: true,
},
{
name: "editor and body flags",
input: "1 --editor --body test",
output: shared.CommentableOptions{},
wantsErr: true,
},
{
name: "web and body flags",
input: "1 --web --body test",
output: shared.CommentableOptions{},
wantsErr: true,
},
{
name: "editor, web, and body flags",
input: "1 --editor --web --body test",
output: shared.CommentableOptions{},
wantsErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
io, stdin, _, _ := iostreams.Test()
io.SetStdoutTTY(true)
io.SetStdinTTY(true)
io.SetStderrTTY(true)
if tt.stdin != "" {
_, _ = stdin.WriteString(tt.stdin)
}
f := &cmdutil.Factory{
IOStreams: io,
Browser: &cmdutil.TestBrowser{},
}
argv, err := shlex.Split(tt.input)
assert.NoError(t, err)
var gotOpts *shared.CommentableOptions
cmd := NewCmdComment(f, func(opts *shared.CommentableOptions) error {
gotOpts = opts
return nil
})
cmd.Flags().BoolP("help", "x", false, "")
cmd.SetArgs(argv)
cmd.SetIn(&bytes.Buffer{})
cmd.SetOut(&bytes.Buffer{})
cmd.SetErr(&bytes.Buffer{})
_, err = cmd.ExecuteC()
if tt.wantsErr {
assert.Error(t, err)
return
}
assert.NoError(t, err)
assert.Equal(t, tt.output.Interactive, gotOpts.Interactive)
assert.Equal(t, tt.output.InputType, gotOpts.InputType)
assert.Equal(t, tt.output.Body, gotOpts.Body)
})
}
}
func Test_commentRun(t *testing.T) {
tests := []struct {
name string
input *shared.CommentableOptions
httpStubs func(*testing.T, *httpmock.Registry)
stdout string
stderr string
}{
{
name: "interactive editor",
input: &shared.CommentableOptions{
Interactive: true,
InputType: 0,
Body: "",
InteractiveEditSurvey: func() (string, error) { return "comment body", nil },
ConfirmSubmitSurvey: func() (bool, error) { return true, nil },
},
httpStubs: func(t *testing.T, reg *httpmock.Registry) {
mockIssueFromNumber(t, reg)
mockCommentCreate(t, reg)
},
stdout: "https://github.com/OWNER/REPO/issues/123#issuecomment-456\n",
},
{
name: "non-interactive web",
input: &shared.CommentableOptions{
Interactive: false,
InputType: shared.InputTypeWeb,
Body: "",
OpenInBrowser: func(string) error { return nil },
},
httpStubs: func(t *testing.T, reg *httpmock.Registry) {
mockIssueFromNumber(t, reg)
},
stderr: "Opening github.com/OWNER/REPO/issues/123 in your browser.\n",
},
{
name: "non-interactive editor",
input: &shared.CommentableOptions{
Interactive: false,
InputType: shared.InputTypeEditor,
Body: "",
EditSurvey: func() (string, error) { return "comment body", nil },
},
httpStubs: func(t *testing.T, reg *httpmock.Registry) {
mockIssueFromNumber(t, reg)
mockCommentCreate(t, reg)
},
stdout: "https://github.com/OWNER/REPO/issues/123#issuecomment-456\n",
},
{
name: "non-interactive inline",
input: &shared.CommentableOptions{
Interactive: false,
InputType: shared.InputTypeInline,
Body: "comment body",
},
httpStubs: func(t *testing.T, reg *httpmock.Registry) {
mockIssueFromNumber(t, reg)
mockCommentCreate(t, reg)
},
stdout: "https://github.com/OWNER/REPO/issues/123#issuecomment-456\n",
},
}
for _, tt := range tests {
io, _, stdout, stderr := iostreams.Test()
io.SetStdoutTTY(true)
io.SetStdinTTY(true)
io.SetStderrTTY(true)
reg := &httpmock.Registry{}
defer reg.Verify(t)
tt.httpStubs(t, reg)
httpClient := func() (*http.Client, error) { return &http.Client{Transport: reg}, nil }
baseRepo := func() (ghrepo.Interface, error) { return ghrepo.New("OWNER", "REPO"), nil }
tt.input.IO = io
tt.input.HttpClient = httpClient
tt.input.RetrieveCommentable = retrieveIssue(tt.input.HttpClient, baseRepo, "123")
t.Run(tt.name, func(t *testing.T) {
err := shared.CommentableRun(tt.input)
assert.NoError(t, err)
assert.Equal(t, tt.stdout, stdout.String())
assert.Equal(t, tt.stderr, stderr.String())
})
}
}
func mockIssueFromNumber(_ *testing.T, reg *httpmock.Registry) {
reg.Register(
httpmock.GraphQL(`query IssueByNumber\b`),
httpmock.StringResponse(`
{ "data": { "repository": { "hasIssuesEnabled": true, "issue": {
"number": 123,
"url": "https://github.com/OWNER/REPO/issues/123"
} } } }`),
)
}
func mockCommentCreate(t *testing.T, reg *httpmock.Registry) {
reg.Register(
httpmock.GraphQL(`mutation CommentCreate\b`),
httpmock.GraphQLMutation(`
{ "data": { "addComment": { "commentEdge": { "node": {
"url": "https://github.com/OWNER/REPO/issues/123#issuecomment-456"
} } } } }`,
func(inputs map[string]interface{}) {
assert.Equal(t, "comment body", inputs["body"])
}),
)
}