From a3b1bb7fb2a5efb7f7ed65692668dcfec6d6080a Mon Sep 17 00:00:00 2001 From: vilmibm Date: Mon, 17 Oct 2022 14:13:54 -0700 Subject: [PATCH] use Prompter in pr create Note that this isn't done; it's leaving the metadata piece alone until better testing utils are in place --- pkg/cmd/issue/create/create.go | 16 ++-- pkg/cmd/issue/create/create_test.go | 93 ++++++++++++++------ pkg/cmd/pr/create/create.go | 28 ++---- pkg/cmd/pr/create/create_test.go | 131 ++++++++++++++++++++++------ pkg/cmd/pr/shared/survey.go | 88 +++++-------------- 5 files changed, 208 insertions(+), 148 deletions(-) diff --git a/pkg/cmd/issue/create/create.go b/pkg/cmd/issue/create/create.go index c6f057388..b704ecfdc 100644 --- a/pkg/cmd/issue/create/create.go +++ b/pkg/cmd/issue/create/create.go @@ -23,6 +23,7 @@ type CreateOptions struct { IO *iostreams.IOStreams BaseRepo func() (ghrepo.Interface, error) Browser browser.Browser + Prompter prShared.Prompt RootDirOverride string @@ -46,6 +47,7 @@ func NewCmdCreate(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Co HttpClient: f.HttpClient, Config: f.Config, Browser: f.Browser, + Prompter: f.Prompter, } var bodyFile string @@ -194,16 +196,10 @@ func createRun(opts *CreateOptions) (err error) { var openURL string if opts.Interactive { - var editorCommand string - editorCommand, err = cmdutil.DetermineEditor(opts.Config) - if err != nil { - return - } - defer prShared.PreserveInput(opts.IO, &tb, &err)() if opts.Title == "" { - err = prShared.TitleSurvey(&tb) + err = prShared.TitleSurvey(opts.Prompter, &tb) if err != nil { return } @@ -227,7 +223,7 @@ func createRun(opts *CreateOptions) (err error) { } } - err = prShared.BodySurvey(&tb, templateContent, editorCommand) + err = prShared.BodySurvey(opts.Prompter, &tb, templateContent) if err != nil { return } @@ -239,7 +235,7 @@ func createRun(opts *CreateOptions) (err error) { } allowPreview := !tb.HasMetadata() && prShared.ValidURL(openURL) - action, err = prShared.ConfirmIssueSubmission(allowPreview, repo.ViewerCanTriage()) + action, err = prShared.ConfirmIssueSubmission(opts.Prompter, allowPreview, repo.ViewerCanTriage()) if err != nil { err = fmt.Errorf("unable to confirm: %w", err) return @@ -257,7 +253,7 @@ func createRun(opts *CreateOptions) (err error) { return } - action, err = prShared.ConfirmIssueSubmission(!tb.HasMetadata(), false) + action, err = prShared.ConfirmIssueSubmission(opts.Prompter, !tb.HasMetadata(), false) if err != nil { return } diff --git a/pkg/cmd/issue/create/create_test.go b/pkg/cmd/issue/create/create_test.go index 9fc9c30ea..800293bf3 100644 --- a/pkg/cmd/issue/create/create_test.go +++ b/pkg/cmd/issue/create/create_test.go @@ -15,6 +15,7 @@ import ( "github.com/cli/cli/v2/internal/browser" "github.com/cli/cli/v2/internal/config" "github.com/cli/cli/v2/internal/ghrepo" + "github.com/cli/cli/v2/internal/prompter" "github.com/cli/cli/v2/internal/run" prShared "github.com/cli/cli/v2/pkg/cmd/pr/shared" "github.com/cli/cli/v2/pkg/cmdutil" @@ -288,11 +289,11 @@ func Test_createRun(t *testing.T) { /*** LEGACY TESTS ***/ -func runCommand(rt http.RoundTripper, isTTY bool, cli string) (*test.CmdOut, error) { - return runCommandWithRootDirOverridden(rt, isTTY, cli, "") +func runCommand(rt http.RoundTripper, isTTY bool, cli string, pm *prompter.PrompterMock) (*test.CmdOut, error) { + return runCommandWithRootDirOverridden(rt, isTTY, cli, "", pm) } -func runCommandWithRootDirOverridden(rt http.RoundTripper, isTTY bool, cli string, rootDir string) (*test.CmdOut, error) { +func runCommandWithRootDirOverridden(rt http.RoundTripper, isTTY bool, cli string, rootDir string, pm *prompter.PrompterMock) (*test.CmdOut, error) { ios, _, stdout, stderr := iostreams.Test() ios.SetStdoutTTY(isTTY) ios.SetStdinTTY(isTTY) @@ -310,7 +311,8 @@ func runCommandWithRootDirOverridden(rt http.RoundTripper, isTTY bool, cli strin BaseRepo: func() (ghrepo.Interface, error) { return ghrepo.New("OWNER", "REPO"), nil }, - Browser: browser, + Browser: browser, + Prompter: pm, } cmd := NewCmdCreate(factory, func(opts *CreateOptions) error { @@ -361,7 +363,7 @@ func TestIssueCreate(t *testing.T) { }), ) - output, err := runCommand(http, true, `-t hello -b "cash rules everything around me"`) + output, err := runCommand(http, true, `-t hello -b "cash rules everything around me"`, nil) if err != nil { t.Errorf("error running command `issue create`: %v", err) } @@ -403,12 +405,28 @@ func TestIssueCreate_recover(t *testing.T) { assert.Equal(t, []interface{}{"BUGID", "TODOID"}, inputs["labelIds"]) })) - //nolint:staticcheck // SA1019: prompt.NewAskStubber is deprecated: use PrompterMock - as := prompt.NewAskStubber(t) - - as.StubPrompt("Title").AnswerDefault() - as.StubPrompt("Body").AnswerDefault() - as.StubPrompt("What's next?").AnswerWith("Submit") + pm := &prompter.PrompterMock{} + pm.InputFunc = func(p, d string) (string, error) { + if p == "Title" { + return d, nil + } else { + return "", prompter.NoSuchPromptErr(p) + } + } + pm.MarkdownEditorFunc = func(p, d string, ba bool) (string, error) { + if p == "Body" { + return d, nil + } else { + return "", prompter.NoSuchPromptErr(p) + } + } + pm.SelectFunc = func(p, _ string, opts []string) (int, error) { + if p == "What's next?" { + return prompter.IndexFor(opts, "Submit") + } else { + return -1, prompter.NoSuchPromptErr(p) + } + } tmpfile, err := os.CreateTemp(t.TempDir(), "testrecover*") assert.NoError(t, err) @@ -428,7 +446,7 @@ func TestIssueCreate_recover(t *testing.T) { args := fmt.Sprintf("--recover '%s'", tmpfile.Name()) - output, err := runCommandWithRootDirOverridden(http, true, args, "") + output, err := runCommandWithRootDirOverridden(http, true, args, "", pm) if err != nil { t.Errorf("error running command `issue create`: %v", err) } @@ -474,13 +492,26 @@ func TestIssueCreate_nonLegacyTemplate(t *testing.T) { //nolint:staticcheck // SA1019: prompt.NewAskStubber is deprecated: use PrompterMock as := prompt.NewAskStubber(t) + // TODO fix as.StubPrompt("Choose a template").AnswerWith("Submit a request") - as.StubPrompt("Body").AnswerDefault() - as.StubPrompt("What's next?"). - AssertOptions([]string{"Submit", "Continue in browser", "Cancel"}). - AnswerWith("Submit") - output, err := runCommandWithRootDirOverridden(http, true, `-t hello`, "./fixtures/repoWithNonLegacyIssueTemplates") + pm := &prompter.PrompterMock{} + pm.MarkdownEditorFunc = func(p, d string, ba bool) (string, error) { + if p == "Body" { + return d, nil + } else { + return "", prompter.NoSuchPromptErr(p) + } + } + pm.SelectFunc = func(p, _ string, opts []string) (int, error) { + if p == "What's next?" { + return prompter.IndexFor(opts, "Submit") + } else { + return -1, prompter.NoSuchPromptErr(p) + } + } + + output, err := runCommandWithRootDirOverridden(http, true, `-t hello`, "./fixtures/repoWithNonLegacyIssueTemplates", pm) if err != nil { t.Errorf("error running command `issue create`: %v", err) } @@ -502,16 +533,26 @@ func TestIssueCreate_continueInBrowser(t *testing.T) { } } }`), ) - //nolint:staticcheck // SA1019: prompt.NewAskStubber is deprecated: use PrompterMock - as := prompt.NewAskStubber(t) - - as.StubPrompt("Title").AnswerWith("hello") - as.StubPrompt("What's next?").AnswerWith("Continue in browser") + pm := &prompter.PrompterMock{} + pm.InputFunc = func(p, d string) (string, error) { + if p == "Title" { + return "hello", nil + } else { + return "", prompter.NoSuchPromptErr(p) + } + } + pm.SelectFunc = func(p, _ string, opts []string) (int, error) { + if p == "What's next?" { + return prompter.IndexFor(opts, "Continue in browser") + } else { + return -1, prompter.NoSuchPromptErr(p) + } + } _, cmdTeardown := run.Stub() defer cmdTeardown(t) - output, err := runCommand(http, true, `-b body`) + output, err := runCommand(http, true, `-b body`, pm) if err != nil { t.Errorf("error running command `issue create`: %v", err) } @@ -596,7 +637,7 @@ func TestIssueCreate_metadata(t *testing.T) { } })) - output, err := runCommand(http, true, `-t TITLE -b BODY -a monalisa -l bug -l todo -p roadmap -m 'big one.oh'`) + output, err := runCommand(http, true, `-t TITLE -b BODY -a monalisa -l bug -l todo -p roadmap -m 'big one.oh'`, nil) if err != nil { t.Errorf("error running command `issue create`: %v", err) } @@ -617,7 +658,7 @@ func TestIssueCreate_disabledIssues(t *testing.T) { } } }`), ) - _, err := runCommand(http, true, `-t heres -b johnny`) + _, err := runCommand(http, true, `-t heres -b johnny`, nil) if err == nil || err.Error() != "the 'OWNER/REPO' repository has disabled issues" { t.Errorf("error running command `issue create`: %v", err) } @@ -668,7 +709,7 @@ func TestIssueCreate_AtMeAssignee(t *testing.T) { assert.Equal(t, []interface{}{"MONAID", "SOMEID"}, inputs["assigneeIds"]) })) - output, err := runCommand(http, true, `-a @me -a someoneelse -t hello -b "cash rules everything around me"`) + output, err := runCommand(http, true, `-a @me -a someoneelse -t hello -b "cash rules everything around me"`, nil) if err != nil { t.Errorf("error running command `issue create`: %v", err) } diff --git a/pkg/cmd/pr/create/create.go b/pkg/cmd/pr/create/create.go index 75d4b58f1..a1e246bc0 100644 --- a/pkg/cmd/pr/create/create.go +++ b/pkg/cmd/pr/create/create.go @@ -10,7 +10,6 @@ import ( "strings" "time" - "github.com/AlecAivazis/survey/v2" "github.com/MakeNowJust/heredoc" "github.com/cli/cli/v2/api" ghContext "github.com/cli/cli/v2/context" @@ -22,14 +21,9 @@ import ( "github.com/cli/cli/v2/pkg/cmd/pr/shared" "github.com/cli/cli/v2/pkg/cmdutil" "github.com/cli/cli/v2/pkg/iostreams" - "github.com/cli/cli/v2/pkg/prompt" "github.com/spf13/cobra" ) -type iprompter interface { - Select(string, string, []string) (int, error) -} - type CreateOptions struct { // This struct stores user input and factory functions HttpClient func() (*http.Client, error) @@ -39,7 +33,7 @@ type CreateOptions struct { Remotes func() (ghContext.Remotes, error) Branch func() (string, error) Browser browser.Browser - Prompter iprompter + Prompter shared.Prompt Finder shared.PRFinder TitleProvided bool @@ -276,17 +270,12 @@ func createRun(opts *CreateOptions) (err error) { } if !opts.TitleProvided { - err = shared.TitleSurvey(state) + err = shared.TitleSurvey(opts.Prompter, state) if err != nil { return } } - editorCommand, err := cmdutil.DetermineEditor(opts.Config) - if err != nil { - return - } - defer shared.PreserveInput(opts.IO, state, &err)() if !opts.BodyProvided { @@ -306,7 +295,7 @@ func createRun(opts *CreateOptions) (err error) { } } - err = shared.BodySurvey(state, templateContent, editorCommand) + err = shared.BodySurvey(opts.Prompter, state, templateContent) if err != nil { return } @@ -319,7 +308,7 @@ func createRun(opts *CreateOptions) (err error) { allowPreview := !state.HasMetadata() && shared.ValidURL(openURL) allowMetadata := ctx.BaseRepo.ViewerCanTriage() - action, err := shared.ConfirmPRSubmission(allowPreview, allowMetadata, state.Draft) + action, err := shared.ConfirmPRSubmission(opts.Prompter, allowPreview, allowMetadata, state.Draft) if err != nil { return fmt.Errorf("unable to confirm: %w", err) } @@ -336,7 +325,7 @@ func createRun(opts *CreateOptions) (err error) { return } - action, err = shared.ConfirmPRSubmission(!state.HasMetadata(), false, state.Draft) + action, err = shared.ConfirmPRSubmission(opts.Prompter, !state.HasMetadata(), false, state.Draft) if err != nil { return } @@ -577,12 +566,7 @@ func NewCreateContext(opts *CreateOptions) (*CreateContext, error) { pushOptions = append(pushOptions, "Skip pushing the branch") pushOptions = append(pushOptions, "Cancel") - var selectedOption int - //nolint:staticcheck // SA1019: prompt.SurveyAskOne is deprecated: use Prompter - err = prompt.SurveyAskOne(&survey.Select{ - Message: fmt.Sprintf("Where should we push the '%s' branch?", headBranch), - Options: pushOptions, - }, &selectedOption) + selectedOption, err := opts.Prompter.Select(fmt.Sprintf("Where should we push the '%s' branch?", headBranch), "", pushOptions) if err != nil { return nil, err } diff --git a/pkg/cmd/pr/create/create_test.go b/pkg/cmd/pr/create/create_test.go index 0764ffe3b..d75335824 100644 --- a/pkg/cmd/pr/create/create_test.go +++ b/pkg/cmd/pr/create/create_test.go @@ -15,6 +15,7 @@ import ( "github.com/cli/cli/v2/internal/browser" "github.com/cli/cli/v2/internal/config" "github.com/cli/cli/v2/internal/ghrepo" + "github.com/cli/cli/v2/internal/prompter" "github.com/cli/cli/v2/internal/run" "github.com/cli/cli/v2/pkg/cmd/pr/shared" prShared "github.com/cli/cli/v2/pkg/cmd/pr/shared" @@ -188,6 +189,7 @@ func Test_createRun(t *testing.T) { setup func(*CreateOptions, *testing.T) func() cmdStubs func(*run.CommandStubber) askStubs func(*prompt.AskStubber) // TODO eventually migrate to PrompterMock + promptStubs func(*prompter.PrompterMock) httpStubs func(*httpmock.Registry, *testing.T) expectedOut string expectedErrOut string @@ -268,8 +270,14 @@ func Test_createRun(t *testing.T) { cs.Register(`git show-ref --verify -- HEAD refs/remotes/origin/feature`, 0, "") cs.Register(`git push --set-upstream origin HEAD:feature`, 0, "") }, - askStubs: func(as *prompt.AskStubber) { - as.StubPrompt("Where should we push the 'feature' branch?").AnswerDefault() + promptStubs: func(pm *prompter.PrompterMock) { + pm.SelectFunc = func(p, _ string, opts []string) (int, error) { + if p == "Where should we push the 'feature' branch?" { + return 0, nil + } else { + return -1, prompter.NoSuchPromptErr(p) + } + } }, expectedOut: "https://github.com/OWNER/REPO/pull/12\n", expectedErrOut: "\nCreating pull request for feature into master in OWNER/REPO\n\n", @@ -309,8 +317,14 @@ func Test_createRun(t *testing.T) { cs.Register(`git show-ref --verify -- HEAD refs/remotes/origin/feature`, 0, "") cs.Register(`git push --set-upstream origin HEAD:feature`, 0, "") }, - askStubs: func(as *prompt.AskStubber) { - as.StubPrompt("Where should we push the 'feature' branch?").AnswerDefault() + promptStubs: func(pm *prompter.PrompterMock) { + pm.SelectFunc = func(p, _ string, opts []string) (int, error) { + if p == "Where should we push the 'feature' branch?" { + return 0, nil + } else { + return -1, prompter.NoSuchPromptErr(p) + } + } }, expectedOut: "https://github.com/OWNER/REPO/pull/12\n", expectedErrOut: "\nCreating pull request for feature into master in OWNER/REPO\n\n", @@ -354,10 +368,14 @@ func Test_createRun(t *testing.T) { cs.Register(`git remote add -f fork https://github.com/monalisa/REPO.git`, 0, "") cs.Register(`git push --set-upstream fork HEAD:feature`, 0, "") }, - askStubs: func(as *prompt.AskStubber) { - as.StubPrompt("Where should we push the 'feature' branch?"). - AssertOptions([]string{"OWNER/REPO", "Create a fork of OWNER/REPO", "Skip pushing the branch", "Cancel"}). - AnswerWith("Create a fork of OWNER/REPO") + promptStubs: func(pm *prompter.PrompterMock) { + pm.SelectFunc = func(p, _ string, opts []string) (int, error) { + if p == "Where should we push the 'feature' branch?" { + return prompter.IndexFor(opts, "Create a fork of OWNER/REPO") + } else { + return -1, prompter.NoSuchPromptErr(p) + } + } }, expectedOut: "https://github.com/OWNER/REPO/pull/12\n", expectedErrOut: "\nCreating pull request for monalisa:feature into master in OWNER/REPO\n\n", @@ -486,10 +504,22 @@ func Test_createRun(t *testing.T) { as.StubPrompt("Choose a template"). AssertOptions([]string{"template1", "template2", "Open a blank pull request"}). AnswerWith("template1") - as.StubPrompt("Body").AnswerDefault() - as.StubPrompt("What's next?"). - AssertOptions([]string{"Submit", "Submit as draft", "Continue in browser", "Add metadata", "Cancel"}). - AnswerDefault() + }, + promptStubs: func(pm *prompter.PrompterMock) { + pm.MarkdownEditorFunc = func(p, d string, ba bool) (string, error) { + if p == "Body" { + return d, nil + } else { + return "", prompter.NoSuchPromptErr(p) + } + } + pm.SelectFunc = func(p, _ string, opts []string) (int, error) { + if p == "What's next?" { + return 0, nil + } else { + return -1, prompter.NoSuchPromptErr(p) + } + } }, expectedOut: "https://github.com/OWNER/REPO/pull/12\n", expectedErrOut: "\nCreating pull request for feature into master in OWNER/REPO\n\n", @@ -636,10 +666,14 @@ func Test_createRun(t *testing.T) { cs.Register(`git( .+)? log( .+)? origin/master\.\.\.feature`, 0, "") cs.Register(`git push --set-upstream origin HEAD:feature`, 0, "") }, - askStubs: func(as *prompt.AskStubber) { - as.StubPrompt("Where should we push the 'feature' branch?"). - AssertOptions([]string{"OWNER/REPO", "Skip pushing the branch", "Cancel"}). - AnswerDefault() + promptStubs: func(pm *prompter.PrompterMock) { + pm.SelectFunc = func(p, _ string, opts []string) (int, error) { + if p == "Where should we push the 'feature' branch?" { + return 0, nil + } else { + return -1, prompter.NoSuchPromptErr(p) + } + } }, expectedErrOut: "Opening github.com/OWNER/REPO/compare/master...feature in your browser.\n", expectedBrowse: "https://github.com/OWNER/REPO/compare/master...feature?body=&expand=1", @@ -685,8 +719,14 @@ func Test_createRun(t *testing.T) { cs.Register(`git push --set-upstream origin HEAD:feature`, 0, "") }, - askStubs: func(as *prompt.AskStubber) { - as.StubPrompt("Where should we push the 'feature' branch?").AnswerDefault() + promptStubs: func(pm *prompter.PrompterMock) { + pm.SelectFunc = func(p, _ string, opts []string) (int, error) { + if p == "Where should we push the 'feature' branch?" { + return 0, nil + } else { + return -1, prompter.NoSuchPromptErr(p) + } + } }, expectedErrOut: "Opening github.com/OWNER/REPO/compare/master...feature in your browser.\n", expectedBrowse: "https://github.com/OWNER/REPO/compare/master...feature?body=&expand=1&projects=ORG%2F1", @@ -727,10 +767,22 @@ func Test_createRun(t *testing.T) { }, askStubs: func(as *prompt.AskStubber) { as.StubPrompt("Choose a template").AnswerDefault() - as.StubPrompt("Body").AnswerDefault() - as.StubPrompt("What's next?"). - AssertOptions([]string{"Submit", "Submit as draft", "Continue in browser", "Add metadata", "Cancel"}). - AnswerWith("Submit as draft") + }, + promptStubs: func(pm *prompter.PrompterMock) { + pm.MarkdownEditorFunc = func(p, d string, ba bool) (string, error) { + if p == "Body" { + return d, nil + } else { + return "", prompter.NoSuchPromptErr(p) + } + } + pm.SelectFunc = func(p, _ string, opts []string) (int, error) { + if p == "What's next?" { + return prompter.IndexFor(opts, "Submit as draft") + } else { + return -1, prompter.NoSuchPromptErr(p) + } + } }, expectedOut: "https://github.com/OWNER/REPO/pull/12\n", expectedErrOut: "\nCreating pull request for feature into master in OWNER/REPO\n\n", @@ -771,10 +823,28 @@ func Test_createRun(t *testing.T) { cmdStubs: func(cs *run.CommandStubber) { cs.Register(`git( .+)? log( .+)? origin/master\.\.\.feature`, 0, "") }, - askStubs: func(as *prompt.AskStubber) { - as.StubPrompt("Title").AnswerDefault() - as.StubPrompt("Body").AnswerDefault() - as.StubPrompt("What's next?").AnswerDefault() + promptStubs: func(pm *prompter.PrompterMock) { + pm.InputFunc = func(p, d string) (string, error) { + if p == "Title" { + return d, nil + } else { + return "", prompter.NoSuchPromptErr(p) + } + } + pm.MarkdownEditorFunc = func(p, d string, ba bool) (string, error) { + if p == "Body" { + return d, nil + } else { + return "", prompter.NoSuchPromptErr(p) + } + } + pm.SelectFunc = func(p, _ string, opts []string) (int, error) { + if p == "What's next?" { + return 0, nil + } else { + return -1, prompter.NoSuchPromptErr(p) + } + } }, setup: func(opts *CreateOptions, t *testing.T) func() { tmpfile, err := os.CreateTemp(t.TempDir(), "testrecover*") @@ -830,6 +900,12 @@ func Test_createRun(t *testing.T) { tt.askStubs(ask) } + pm := &prompter.PrompterMock{} + + if tt.promptStubs != nil { + tt.promptStubs(pm) + } + cs, cmdTeardown := run.Stub() defer cmdTeardown(t) cs.Register(`git status --porcelain`, 0, "") @@ -839,6 +915,7 @@ func Test_createRun(t *testing.T) { } opts := CreateOptions{} + opts.Prompter = pm ios, _, stdout, stderr := iostreams.Test() // TODO do i need to bother with this @@ -1064,3 +1141,5 @@ func Test_generateCompareURL(t *testing.T) { }) } } + +// TODO interactive metadata tests once: 1) we have test utils for Prompter and 2) metadata questions use Prompter diff --git a/pkg/cmd/pr/shared/survey.go b/pkg/cmd/pr/shared/survey.go index dd4ba085d..963439d04 100644 --- a/pkg/cmd/pr/shared/survey.go +++ b/pkg/cmd/pr/shared/survey.go @@ -9,7 +9,6 @@ import ( "github.com/cli/cli/v2/internal/ghrepo" "github.com/cli/cli/v2/pkg/iostreams" "github.com/cli/cli/v2/pkg/prompt" - "github.com/cli/cli/v2/pkg/surveyext" ) type Action int @@ -32,15 +31,21 @@ const ( cancelLabel = "Cancel" ) -func ConfirmIssueSubmission(allowPreview bool, allowMetadata bool) (Action, error) { - return confirmSubmission(allowPreview, allowMetadata, false, false) +type Prompt interface { + Input(string, string) (string, error) + Select(string, string, []string) (int, error) + MarkdownEditor(string, string, bool) (string, error) } -func ConfirmPRSubmission(allowPreview, allowMetadata, isDraft bool) (Action, error) { - return confirmSubmission(allowPreview, allowMetadata, true, isDraft) +func ConfirmIssueSubmission(p Prompt, allowPreview bool, allowMetadata bool) (Action, error) { + return confirmSubmission(p, allowPreview, allowMetadata, false, false) } -func confirmSubmission(allowPreview, allowMetadata, allowDraft, isDraft bool) (Action, error) { +func ConfirmPRSubmission(p Prompt, allowPreview, allowMetadata, isDraft bool) (Action, error) { + return confirmSubmission(p, allowPreview, allowMetadata, true, isDraft) +} + +func confirmSubmission(p Prompt, allowPreview, allowMetadata, allowDraft, isDraft bool) (Action, error) { var options []string if !isDraft { options = append(options, submitLabel) @@ -56,26 +61,12 @@ func confirmSubmission(allowPreview, allowMetadata, allowDraft, isDraft bool) (A } options = append(options, cancelLabel) - confirmAnswers := struct { - Confirmation int - }{} - confirmQs := []*survey.Question{ - { - Name: "confirmation", - Prompt: &survey.Select{ - Message: "What's next?", - Options: options, - }, - }, - } - - //nolint:staticcheck // SA1019: prompt.SurveyAsk is deprecated: use Prompter - err := prompt.SurveyAsk(confirmQs, &confirmAnswers) + result, err := p.Select("What's next?", "", options) if err != nil { return -1, fmt.Errorf("could not prompt: %w", err) } - switch options[confirmAnswers.Confirmation] { + switch options[result] { case submitLabel: return SubmitAction, nil case submitDraftLabel: @@ -87,11 +78,11 @@ func confirmSubmission(allowPreview, allowMetadata, allowDraft, isDraft bool) (A case cancelLabel: return CancelAction, nil default: - return -1, fmt.Errorf("invalid index: %d", confirmAnswers.Confirmation) + return -1, fmt.Errorf("invalid index: %d", result) } } -func BodySurvey(state *IssueMetadataState, templateContent, editorCommand string) error { +func BodySurvey(p Prompt, state *IssueMetadataState, templateContent string) error { if templateContent != "" { if state.Body != "" { // prevent excessive newlines between default body and template @@ -101,63 +92,32 @@ func BodySurvey(state *IssueMetadataState, templateContent, editorCommand string state.Body += templateContent } - preBody := state.Body - - // TODO should just be an AskOne but ran into problems with the stubber - qs := []*survey.Question{ - { - Name: "Body", - Prompt: &surveyext.GhEditor{ - BlankAllowed: true, - EditorCommand: editorCommand, - Editor: &survey.Editor{ - Message: "Body", - FileName: "*.md", - Default: state.Body, - HideDefault: true, - AppendDefault: true, - }, - }, - }, - } - - //nolint:staticcheck // SA1019: prompt.SurveyAsk is deprecated: use Prompter - err := prompt.SurveyAsk(qs, state) + result, err := p.MarkdownEditor("Body", state.Body, true) if err != nil { return err } - if preBody != state.Body { + if state.Body != result { state.MarkDirty() } + state.Body = result + return nil } -func TitleSurvey(state *IssueMetadataState) error { - preTitle := state.Title - - // TODO should just be an AskOne but ran into problems with the stubber - qs := []*survey.Question{ - { - Name: "Title", - Prompt: &survey.Input{ - Message: "Title", - Default: state.Title, - }, - }, - } - - //nolint:staticcheck // SA1019: prompt.SurveyAsk is deprecated: use Prompter - err := prompt.SurveyAsk(qs, state) +func TitleSurvey(p Prompt, state *IssueMetadataState) error { + result, err := p.Input("Title", state.Title) if err != nil { return err } - if preTitle != state.Title { + if result != state.Title { state.MarkDirty() } + state.Title = result + return nil }