diff --git a/command/pr.go b/command/pr.go index 2ef865066..3f8e53481 100644 --- a/command/pr.go +++ b/command/pr.go @@ -16,6 +16,7 @@ import ( "github.com/cli/cli/git" "github.com/cli/cli/internal/ghrepo" "github.com/cli/cli/pkg/cmdutil" + "github.com/cli/cli/pkg/prompt" "github.com/cli/cli/pkg/text" "github.com/cli/cli/utils" "github.com/spf13/cobra" @@ -619,7 +620,7 @@ func prInteractiveMerge(deleteLocalBranch bool, crossRepoPR bool) (api.PullReque DeleteBranch bool }{} - err := SurveyAsk(qs, &answers) + err := prompt.SurveyAsk(qs, &answers) if err != nil { return 0, false, fmt.Errorf("could not prompt: %w", err) } diff --git a/command/pr_create_test.go b/command/pr_create_test.go index 4d82c2b11..c6daf8bef 100644 --- a/command/pr_create_test.go +++ b/command/pr_create_test.go @@ -11,6 +11,7 @@ import ( "github.com/cli/cli/git" "github.com/cli/cli/internal/ghrepo" "github.com/cli/cli/pkg/httpmock" + "github.com/cli/cli/pkg/prompt" "github.com/cli/cli/test" "github.com/stretchr/testify/assert" ) @@ -602,10 +603,10 @@ func TestPRCreate_survey_defaults_multicommit(t *testing.T) { cs.Stub("") // git rev-parse cs.Stub("") // git push - as, surveyTeardown := initAskStubber() + as, surveyTeardown := prompt.InitAskStubber() defer surveyTeardown() - as.Stub([]*QuestionStub{ + as.Stub([]*prompt.QuestionStub{ { Name: "title", Default: true, @@ -615,7 +616,7 @@ func TestPRCreate_survey_defaults_multicommit(t *testing.T) { Default: true, }, }) - as.Stub([]*QuestionStub{ + as.Stub([]*prompt.QuestionStub{ { Name: "confirmation", Value: 0, @@ -687,10 +688,10 @@ func TestPRCreate_survey_defaults_monocommit(t *testing.T) { cs.Stub("") // git rev-parse cs.Stub("") // git push - as, surveyTeardown := initAskStubber() + as, surveyTeardown := prompt.InitAskStubber() defer surveyTeardown() - as.Stub([]*QuestionStub{ + as.Stub([]*prompt.QuestionStub{ { Name: "title", Default: true, @@ -700,7 +701,7 @@ func TestPRCreate_survey_defaults_monocommit(t *testing.T) { Default: true, }, }) - as.Stub([]*QuestionStub{ + as.Stub([]*prompt.QuestionStub{ { Name: "confirmation", Value: 0, @@ -896,10 +897,10 @@ func TestPRCreate_defaults_error_interactive(t *testing.T) { cs.Stub("") // git push cs.Stub("") // browser open - as, surveyTeardown := initAskStubber() + as, surveyTeardown := prompt.InitAskStubber() defer surveyTeardown() - as.Stub([]*QuestionStub{ + as.Stub([]*prompt.QuestionStub{ { Name: "title", Default: true, @@ -909,7 +910,7 @@ func TestPRCreate_defaults_error_interactive(t *testing.T) { Value: "social distancing", }, }) - as.Stub([]*QuestionStub{ + as.Stub([]*prompt.QuestionStub{ { Name: "confirmation", Value: 1, diff --git a/command/pr_review.go b/command/pr_review.go index a9681d619..f2cbb68be 100644 --- a/command/pr_review.go +++ b/command/pr_review.go @@ -9,6 +9,7 @@ import ( "github.com/spf13/cobra" "github.com/cli/cli/api" + "github.com/cli/cli/pkg/prompt" "github.com/cli/cli/pkg/surveyext" "github.com/cli/cli/utils" ) @@ -166,7 +167,7 @@ func reviewSurvey(cmd *cobra.Command) (*api.PullRequestReviewInput, error) { }, } - err = SurveyAsk(typeQs, &typeAnswers) + err = prompt.SurveyAsk(typeQs, &typeAnswers) if err != nil { return nil, err } @@ -207,7 +208,7 @@ func reviewSurvey(cmd *cobra.Command) (*api.PullRequestReviewInput, error) { }, } - err = SurveyAsk(bodyQs, &bodyAnswers) + err = prompt.SurveyAsk(bodyQs, &bodyAnswers) if err != nil { return nil, err } @@ -237,7 +238,7 @@ func reviewSurvey(cmd *cobra.Command) (*api.PullRequestReviewInput, error) { }, } - err = SurveyAsk(confirmQs, &confirm) + err = prompt.SurveyAsk(confirmQs, &confirm) if err != nil { return nil, err } diff --git a/command/pr_review_test.go b/command/pr_review_test.go index 9b2c88b53..b2bb36536 100644 --- a/command/pr_review_test.go +++ b/command/pr_review_test.go @@ -7,6 +7,7 @@ import ( "regexp" "testing" + "github.com/cli/cli/pkg/prompt" "github.com/cli/cli/test" "github.com/stretchr/testify/assert" ) @@ -337,22 +338,22 @@ func TestPRReview_interactive(t *testing.T) { ] } } } } `)) http.StubResponse(200, bytes.NewBufferString(`{"data": {} }`)) - as, teardown := initAskStubber() + as, teardown := prompt.InitAskStubber() defer teardown() - as.Stub([]*QuestionStub{ + as.Stub([]*prompt.QuestionStub{ { Name: "reviewType", Value: "Approve", }, }) - as.Stub([]*QuestionStub{ + as.Stub([]*prompt.QuestionStub{ { Name: "body", Value: "cool story", }, }) - as.Stub([]*QuestionStub{ + as.Stub([]*prompt.QuestionStub{ { Name: "confirm", Value: true, @@ -399,22 +400,22 @@ func TestPRReview_interactive_no_body(t *testing.T) { ] } } } } `)) http.StubResponse(200, bytes.NewBufferString(`{"data": {} }`)) - as, teardown := initAskStubber() + as, teardown := prompt.InitAskStubber() defer teardown() - as.Stub([]*QuestionStub{ + as.Stub([]*prompt.QuestionStub{ { Name: "reviewType", Value: "Request changes", }, }) - as.Stub([]*QuestionStub{ + as.Stub([]*prompt.QuestionStub{ { Name: "body", Default: true, }, }) - as.Stub([]*QuestionStub{ + as.Stub([]*prompt.QuestionStub{ { Name: "confirm", Value: true, @@ -443,22 +444,22 @@ func TestPRReview_interactive_blank_approve(t *testing.T) { ] } } } } `)) http.StubResponse(200, bytes.NewBufferString(`{"data": {} }`)) - as, teardown := initAskStubber() + as, teardown := prompt.InitAskStubber() defer teardown() - as.Stub([]*QuestionStub{ + as.Stub([]*prompt.QuestionStub{ { Name: "reviewType", Value: "Approve", }, }) - as.Stub([]*QuestionStub{ + as.Stub([]*prompt.QuestionStub{ { Name: "body", Default: true, }, }) - as.Stub([]*QuestionStub{ + as.Stub([]*prompt.QuestionStub{ { Name: "confirm", Value: true, diff --git a/command/pr_test.go b/command/pr_test.go index 9aa75dae9..fdf314022 100644 --- a/command/pr_test.go +++ b/command/pr_test.go @@ -11,6 +11,7 @@ import ( "github.com/cli/cli/api" "github.com/cli/cli/internal/run" "github.com/cli/cli/pkg/httpmock" + "github.com/cli/cli/pkg/prompt" "github.com/cli/cli/test" "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" @@ -1618,10 +1619,10 @@ func TestPRMerge_interactive(t *testing.T) { cs.Stub("") // git push origin --delete blueberries cs.Stub("") // git branch -d - as, surveyTeardown := initAskStubber() + as, surveyTeardown := prompt.InitAskStubber() defer surveyTeardown() - as.Stub([]*QuestionStub{ + as.Stub([]*prompt.QuestionStub{ { Name: "mergeMethod", Value: 0, diff --git a/command/testing.go b/command/testing.go index 0b68a4906..e12cc38e3 100644 --- a/command/testing.go +++ b/command/testing.go @@ -4,10 +4,7 @@ import ( "bytes" "errors" "fmt" - "reflect" - "github.com/AlecAivazis/survey/v2" - "github.com/AlecAivazis/survey/v2/core" "github.com/cli/cli/api" "github.com/cli/cli/context" "github.com/cli/cli/internal/config" @@ -23,57 +20,6 @@ const defaultTestConfig = `hosts: oauth_token: "1234567890" ` -type askStubber struct { - Asks [][]*survey.Question - Count int - Stubs [][]*QuestionStub -} - -func initAskStubber() (*askStubber, func()) { - origSurveyAsk := SurveyAsk - as := askStubber{} - SurveyAsk = func(qs []*survey.Question, response interface{}, opts ...survey.AskOpt) error { - as.Asks = append(as.Asks, qs) - count := as.Count - as.Count += 1 - if count >= len(as.Stubs) { - panic(fmt.Sprintf("more asks than stubs. most recent call: %v", qs)) - } - - // actually set response - stubbedQuestions := as.Stubs[count] - for i, sq := range stubbedQuestions { - q := qs[i] - if q.Name != sq.Name { - panic(fmt.Sprintf("stubbed question mismatch: %s != %s", q.Name, sq.Name)) - } - if sq.Default { - defaultValue := reflect.ValueOf(q.Prompt).Elem().FieldByName("Default") - _ = core.WriteAnswer(response, q.Name, defaultValue) - } else { - _ = core.WriteAnswer(response, q.Name, sq.Value) - } - } - - return nil - } - teardown := func() { - SurveyAsk = origSurveyAsk - } - return &as, teardown -} - -type QuestionStub struct { - Name string - Value interface{} - Default bool -} - -func (as *askStubber) Stub(stubbedQuestions []*QuestionStub) { - // A call to .Ask takes a list of questions; a stub is then a list of questions in the same order. - as.Stubs = append(as.Stubs, stubbedQuestions) -} - func initBlankContext(cfg, repo, branch string) { initContext = func() context.Context { ctx := context.NewBlank() diff --git a/command/title_body_survey.go b/command/title_body_survey.go index 53de6cf2b..94ec89914 100644 --- a/command/title_body_survey.go +++ b/command/title_body_survey.go @@ -7,6 +7,7 @@ import ( "github.com/cli/cli/api" "github.com/cli/cli/internal/ghrepo" "github.com/cli/cli/pkg/githubtemplate" + "github.com/cli/cli/pkg/prompt" "github.com/cli/cli/pkg/surveyext" "github.com/cli/cli/utils" "github.com/spf13/cobra" @@ -54,10 +55,6 @@ const ( noMilestone = "(none)" ) -var SurveyAsk = func(qs []*survey.Question, response interface{}, opts ...survey.AskOpt) error { - return survey.Ask(qs, response, opts...) -} - func confirmSubmission(allowPreview bool, allowMetadata bool) (Action, error) { const ( submitLabel = "Submit" @@ -88,7 +85,7 @@ func confirmSubmission(allowPreview bool, allowMetadata bool) (Action, error) { }, } - err := SurveyAsk(confirmQs, &confirmAnswers) + err := prompt.SurveyAsk(confirmQs, &confirmAnswers) if err != nil { return -1, fmt.Errorf("could not prompt: %w", err) } @@ -130,7 +127,7 @@ func selectTemplate(nonLegacyTemplatePaths []string, legacyTemplatePath *string, }, }, } - if err := SurveyAsk(selectQs, &templateResponse); err != nil { + if err := prompt.SurveyAsk(selectQs, &templateResponse); err != nil { return "", fmt.Errorf("could not prompt: %w", err) } @@ -201,7 +198,7 @@ func titleBodySurvey(cmd *cobra.Command, issueState *issueMetadataState, apiClie qs = append(qs, bodyQuestion) } - err = SurveyAsk(qs, issueState) + err = prompt.SurveyAsk(qs, issueState) if err != nil { return fmt.Errorf("could not prompt: %w", err) } @@ -232,7 +229,7 @@ func titleBodySurvey(cmd *cobra.Command, issueState *issueMetadataState, apiClie } extraFieldsOptions = append(extraFieldsOptions, "Assignees", "Labels", "Projects", "Milestone") - err = SurveyAsk([]*survey.Question{ + err = prompt.SurveyAsk([]*survey.Question{ { Name: "metadata", Prompt: &survey.MultiSelect{ @@ -364,7 +361,7 @@ func titleBodySurvey(cmd *cobra.Command, issueState *issueMetadataState, apiClie } } values := metadataValues{} - err = SurveyAsk(mqs, &values, survey.WithKeepFilter(true)) + err = prompt.SurveyAsk(mqs, &values, survey.WithKeepFilter(true)) if err != nil { return fmt.Errorf("could not prompt: %w", err) } diff --git a/pkg/prompt/prompt.go b/pkg/prompt/prompt.go index 76c58a11e..05683640e 100644 --- a/pkg/prompt/prompt.go +++ b/pkg/prompt/prompt.go @@ -20,3 +20,7 @@ var Confirm = func(prompt string, result *bool) error { } return survey.AskOne(p, result) } + +var SurveyAsk = func(qs []*survey.Question, response interface{}, opts ...survey.AskOpt) error { + return survey.Ask(qs, response, opts...) +} diff --git a/pkg/prompt/stubber.go b/pkg/prompt/stubber.go new file mode 100644 index 000000000..e988962d5 --- /dev/null +++ b/pkg/prompt/stubber.go @@ -0,0 +1,60 @@ +package prompt + +import ( + "fmt" + "reflect" + + "github.com/AlecAivazis/survey/v2" + "github.com/AlecAivazis/survey/v2/core" +) + +type askStubber struct { + Asks [][]*survey.Question + Count int + Stubs [][]*QuestionStub +} + +func InitAskStubber() (*askStubber, func()) { + origSurveyAsk := SurveyAsk + as := askStubber{} + SurveyAsk = func(qs []*survey.Question, response interface{}, opts ...survey.AskOpt) error { + as.Asks = append(as.Asks, qs) + count := as.Count + as.Count += 1 + if count >= len(as.Stubs) { + panic(fmt.Sprintf("more asks than stubs. most recent call: %v", qs)) + } + + // actually set response + stubbedQuestions := as.Stubs[count] + for i, sq := range stubbedQuestions { + q := qs[i] + if q.Name != sq.Name { + panic(fmt.Sprintf("stubbed question mismatch: %s != %s", q.Name, sq.Name)) + } + if sq.Default { + defaultValue := reflect.ValueOf(q.Prompt).Elem().FieldByName("Default") + _ = core.WriteAnswer(response, q.Name, defaultValue) + } else { + _ = core.WriteAnswer(response, q.Name, sq.Value) + } + } + + return nil + } + teardown := func() { + SurveyAsk = origSurveyAsk + } + return &as, teardown +} + +type QuestionStub struct { + Name string + Value interface{} + Default bool +} + +func (as *askStubber) Stub(stubbedQuestions []*QuestionStub) { + // A call to .Ask takes a list of questions; a stub is then a list of questions in the same order. + as.Stubs = append(as.Stubs, stubbedQuestions) +}