diff --git a/internal/prompter/test.go b/internal/prompter/test.go index ae7574ea9..19d38dd25 100644 --- a/internal/prompter/test.go +++ b/internal/prompter/test.go @@ -1,6 +1,11 @@ package prompter -import "fmt" +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) // Test helpers @@ -13,6 +18,10 @@ func IndexFor(options []string, answer string) (int, error) { return -1, NoSuchAnswerErr(answer) } +func AssertOptions(t *testing.T, expected, actual []string) { + assert.Equal(t, expected, actual) +} + func NoSuchAnswerErr(answer string) error { return fmt.Errorf("no such answer '%s'", answer) } diff --git a/pkg/cmd/release/create/create.go b/pkg/cmd/release/create/create.go index 01fc2e33c..ee9ad3429 100644 --- a/pkg/cmd/release/create/create.go +++ b/pkg/cmd/release/create/create.go @@ -9,7 +9,6 @@ import ( "net/http" "strings" - "github.com/AlecAivazis/survey/v2" "github.com/MakeNowJust/heredoc" "github.com/cli/cli/v2/git" "github.com/cli/cli/v2/internal/config" @@ -18,11 +17,16 @@ import ( "github.com/cli/cli/v2/pkg/cmd/release/shared" "github.com/cli/cli/v2/pkg/cmdutil" "github.com/cli/cli/v2/pkg/iostreams" - "github.com/cli/cli/v2/pkg/prompt" "github.com/cli/cli/v2/pkg/surveyext" "github.com/spf13/cobra" ) +type iprompter interface { + Select(string, string, []string) (int, error) + Input(string, string) (string, error) + Confirm(string, bool) (bool, error) +} + type CreateOptions struct { IO *iostreams.IOStreams Config func() (config.Config, error) @@ -30,6 +34,7 @@ type CreateOptions struct { GitClient *git.Client BaseRepo func() (ghrepo.Interface, error) Edit func(string, string, string, io.Reader, io.Writer, io.Writer) (string, error) + Prompter iprompter TagName string Target string @@ -62,6 +67,7 @@ func NewCmdCreate(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Co HttpClient: f.HttpClient, GitClient: f.GitClient, Config: f.Config, + Prompter: f.Prompter, Edit: surveyext.Edit, } @@ -199,17 +205,11 @@ func createRun(opts *CreateOptions) error { } createNewTagOption := "Create a new tag" options = append(options, createNewTagOption) - var tag string - q := &survey.Select{ - Message: "Choose a tag", - Options: options, - Default: options[0], - } - //nolint:staticcheck // SA1019: prompt.SurveyAskOne is deprecated: use Prompter - err := prompt.SurveyAskOne(q, &tag) + selected, err := opts.Prompter.Select("Choose a tag", options[0], options) if err != nil { return fmt.Errorf("could not prompt: %w", err) } + tag := options[selected] if tag != createNewTagOption { existingTag = true opts.TagName = tag @@ -217,11 +217,7 @@ func createRun(opts *CreateOptions) error { } if opts.TagName == "" { - q := &survey.Input{ - Message: "Tag name", - } - //nolint:staticcheck // SA1019: prompt.SurveyAskOne is deprecated: use Prompter - err := prompt.SurveyAskOne(q, &opts.TagName) + opts.TagName, err = opts.Prompter.Input("Tag name", "") if err != nil { return fmt.Errorf("could not prompt: %w", err) } @@ -313,28 +309,18 @@ func createRun(opts *CreateOptions) error { if defaultName == "" && generatedNotes != nil { defaultName = generatedNotes.Name } - qs := []*survey.Question{ - { - Name: "name", - Prompt: &survey.Input{ - Message: "Title (optional)", - Default: defaultName, - }, - }, - { - Name: "releaseNotesAction", - Prompt: &survey.Select{ - Message: "Release notes", - Options: editorOptions, - }, - }, - } - //nolint:staticcheck // SA1019: prompt.SurveyAsk is deprecated: use Prompter - err = prompt.SurveyAsk(qs, opts) + + opts.Name, err = opts.Prompter.Input("Title (optional)", defaultName) if err != nil { return fmt.Errorf("could not prompt: %w", err) } + selected, err := opts.Prompter.Select("Release notes", "", editorOptions) + if err != nil { + return fmt.Errorf("could not prompt: %w", err) + } + opts.ReleaseNotesAction = editorOptions[selected] + var openEditor bool var editorContents string @@ -372,34 +358,19 @@ func createRun(opts *CreateOptions) error { defaultSubmit = saveAsDraft } - qs = []*survey.Question{ - { - Name: "prerelease", - Prompt: &survey.Confirm{ - Message: "Is this a prerelease?", - Default: opts.Prerelease, - }, - }, - { - Name: "submitAction", - Prompt: &survey.Select{ - Message: "Submit?", - Options: []string{ - publishRelease, - saveAsDraft, - "Cancel", - }, - Default: defaultSubmit, - }, - }, + opts.Prerelease, err = opts.Prompter.Confirm("Is this a prerelease?", opts.Prerelease) + if err != nil { + return err } - //nolint:staticcheck // SA1019: prompt.SurveyAsk is deprecated: use Prompter - err = prompt.SurveyAsk(qs, opts) + options := []string{publishRelease, saveAsDraft, "Cancel"} + selected, err = opts.Prompter.Select("Submit?", defaultSubmit, options) if err != nil { return fmt.Errorf("could not prompt: %w", err) } + opts.SubmitAction = options[selected] + switch opts.SubmitAction { case "Publish release": opts.Draft = false diff --git a/pkg/cmd/release/create/create_test.go b/pkg/cmd/release/create/create_test.go index 8c842945e..aec10911a 100644 --- a/pkg/cmd/release/create/create_test.go +++ b/pkg/cmd/release/create/create_test.go @@ -13,12 +13,12 @@ import ( "github.com/cli/cli/v2/git" "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/release/shared" "github.com/cli/cli/v2/pkg/cmdutil" "github.com/cli/cli/v2/pkg/httpmock" "github.com/cli/cli/v2/pkg/iostreams" - "github.com/cli/cli/v2/pkg/prompt" "github.com/google/shlex" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -828,29 +828,49 @@ func Test_createRun(t *testing.T) { func Test_createRun_interactive(t *testing.T) { tests := []struct { - name string - httpStubs func(*httpmock.Registry) - askStubs func(*prompt.AskStubber) - runStubs func(*run.CommandStubber) - opts *CreateOptions - wantParams map[string]interface{} - wantOut string - wantErr string + name string + httpStubs func(*httpmock.Registry) + prompterStubs func(*testing.T, *prompter.PrompterMock) + runStubs func(*run.CommandStubber) + opts *CreateOptions + wantParams map[string]interface{} + wantOut string + wantErr string }{ { name: "create a release from existing tag", opts: &CreateOptions{}, - askStubs: func(as *prompt.AskStubber) { - as.StubPrompt("Choose a tag"). - AssertOptions([]string{"v1.2.3", "v1.2.2", "v1.0.0", "v0.1.2", "Create a new tag"}). - AnswerWith("v1.2.3") - as.StubPrompt("Title (optional)").AnswerWith("") - as.StubPrompt("Release notes"). - AssertOptions([]string{"Write my own", "Write using generated notes as template", "Leave blank"}). - AnswerWith("Leave blank") - as.StubPrompt("Is this a prerelease?").AnswerWith(false) - as.StubPrompt("Submit?"). - AssertOptions([]string{"Publish release", "Save as draft", "Cancel"}).AnswerWith("Publish release") + prompterStubs: func(t *testing.T, pm *prompter.PrompterMock) { + pm.SelectFunc = func(p, d string, opts []string) (int, error) { + switch p { + case "Choose a tag": + prompter.AssertOptions(t, []string{"v1.2.3", "v1.2.2", "v1.0.0", "v0.1.2", "Create a new tag"}, opts) + return prompter.IndexFor(opts, "v1.2.3") + case "Release notes": + prompter.AssertOptions(t, []string{"Write my own", "Write using generated notes as template", "Leave blank"}, opts) + return prompter.IndexFor(opts, "Leave blank") + case "Submit?": + prompter.AssertOptions(t, []string{"Publish release", "Save as draft", "Cancel"}, opts) + return prompter.IndexFor(opts, "Publish release") + default: + return -1, prompter.NoSuchPromptErr(p) + } + } + pm.InputFunc = func(p, d string) (string, error) { + if p == "Title (optional)" { + return d, nil + } + + return "", prompter.NoSuchPromptErr(p) + } + pm.ConfirmFunc = func(p string, d bool) (bool, error) { + switch p { + case "Is this a prerelease?": + return false, nil + default: + return false, prompter.NoSuchPromptErr(p) + } + } }, runStubs: func(rs *run.CommandStubber) { rs.Register(`git tag --list`, 1, "") @@ -875,15 +895,40 @@ func Test_createRun_interactive(t *testing.T) { { name: "create a release from new tag", opts: &CreateOptions{}, - askStubs: func(as *prompt.AskStubber) { - as.StubPrompt("Choose a tag").AnswerWith("Create a new tag") - as.StubPrompt("Tag name").AnswerWith("v1.2.3") - as.StubPrompt("Title (optional)").AnswerWith("") - as.StubPrompt("Release notes"). - AssertOptions([]string{"Write my own", "Write using generated notes as template", "Leave blank"}). - AnswerWith("Leave blank") - as.StubPrompt("Is this a prerelease?").AnswerWith(false) - as.StubPrompt("Submit?").AnswerWith("Publish release") + prompterStubs: func(t *testing.T, pm *prompter.PrompterMock) { + pm.SelectFunc = func(p, d string, opts []string) (int, error) { + switch p { + case "Choose a tag": + prompter.AssertOptions(t, []string{"v1.2.2", "v1.0.0", "v0.1.2", "Create a new tag"}, opts) + return prompter.IndexFor(opts, "Create a new tag") + case "Release notes": + prompter.AssertOptions(t, []string{"Write my own", "Write using generated notes as template", "Leave blank"}, opts) + return prompter.IndexFor(opts, "Leave blank") + case "Submit?": + prompter.AssertOptions(t, []string{"Publish release", "Save as draft", "Cancel"}, opts) + return prompter.IndexFor(opts, "Publish release") + default: + return -1, prompter.NoSuchPromptErr(p) + } + } + pm.InputFunc = func(p, d string) (string, error) { + switch p { + case "Tag name": + return "v1.2.3", nil + case "Title (optional)": + return d, nil + default: + return "", prompter.NoSuchPromptErr(p) + } + } + pm.ConfirmFunc = func(p string, d bool) (bool, error) { + switch p { + case "Is this a prerelease?": + return false, nil + default: + return false, prompter.NoSuchPromptErr(p) + } + } }, runStubs: func(rs *run.CommandStubber) { rs.Register(`git tag --list`, 1, "") @@ -910,13 +955,35 @@ func Test_createRun_interactive(t *testing.T) { opts: &CreateOptions{ TagName: "v1.2.3", }, - askStubs: func(as *prompt.AskStubber) { - as.StubPrompt("Title (optional)").AnswerDefault() - as.StubPrompt("Release notes"). - AssertOptions([]string{"Write my own", "Write using generated notes as template", "Leave blank"}). - AnswerWith("Write using generated notes as template") - as.StubPrompt("Is this a prerelease?").AnswerWith(false) - as.StubPrompt("Submit?").AnswerWith("Publish release") + prompterStubs: func(t *testing.T, pm *prompter.PrompterMock) { + pm.SelectFunc = func(p, d string, opts []string) (int, error) { + switch p { + case "Release notes": + prompter.AssertOptions(t, []string{"Write my own", "Write using generated notes as template", "Leave blank"}, opts) + return prompter.IndexFor(opts, "Write using generated notes as template") + case "Submit?": + prompter.AssertOptions(t, []string{"Publish release", "Save as draft", "Cancel"}, opts) + return prompter.IndexFor(opts, "Publish release") + default: + return -1, prompter.NoSuchPromptErr(p) + } + } + pm.InputFunc = func(p, d string) (string, error) { + switch p { + case "Title (optional)": + return d, nil + default: + return "", prompter.NoSuchPromptErr(p) + } + } + pm.ConfirmFunc = func(p string, d bool) (bool, error) { + switch p { + case "Is this a prerelease?": + return false, nil + default: + return false, prompter.NoSuchPromptErr(p) + } + } }, runStubs: func(rs *run.CommandStubber) { rs.Register(`git tag --list`, 1, "") @@ -948,13 +1015,35 @@ func Test_createRun_interactive(t *testing.T) { opts: &CreateOptions{ TagName: "v1.2.3", }, - askStubs: func(as *prompt.AskStubber) { - as.StubPrompt("Title (optional)").AnswerDefault() - as.StubPrompt("Release notes"). - AssertOptions([]string{"Write my own", "Write using commit log as template", "Leave blank"}). - AnswerWith("Write using commit log as template") - as.StubPrompt("Is this a prerelease?").AnswerWith(false) - as.StubPrompt("Submit?").AnswerWith("Publish release") + prompterStubs: func(t *testing.T, pm *prompter.PrompterMock) { + pm.SelectFunc = func(p, d string, opts []string) (int, error) { + switch p { + case "Release notes": + prompter.AssertOptions(t, []string{"Write my own", "Write using commit log as template", "Leave blank"}, opts) + return prompter.IndexFor(opts, "Write using commit log as template") + case "Submit?": + prompter.AssertOptions(t, []string{"Publish release", "Save as draft", "Cancel"}, opts) + return prompter.IndexFor(opts, "Publish release") + default: + return -1, prompter.NoSuchPromptErr(p) + } + } + pm.InputFunc = func(p, d string) (string, error) { + switch p { + case "Title (optional)": + return d, nil + default: + return "", prompter.NoSuchPromptErr(p) + } + } + pm.ConfirmFunc = func(p string, d bool) (bool, error) { + switch p { + case "Is this a prerelease?": + return false, nil + default: + return false, prompter.NoSuchPromptErr(p) + } + } }, runStubs: func(rs *run.CommandStubber) { rs.Register(`git tag --list`, 1, "") @@ -984,13 +1073,35 @@ func Test_createRun_interactive(t *testing.T) { opts: &CreateOptions{ TagName: "v1.2.3", }, - askStubs: func(as *prompt.AskStubber) { - as.StubPrompt("Title (optional)").AnswerDefault() - as.StubPrompt("Release notes"). - AssertOptions([]string{"Write my own", "Write using git tag message as template", "Leave blank"}). - AnswerWith("Write using git tag message as template") - as.StubPrompt("Is this a prerelease?").AnswerWith(false) - as.StubPrompt("Submit?").AnswerWith("Publish release") + prompterStubs: func(t *testing.T, pm *prompter.PrompterMock) { + pm.SelectFunc = func(p, d string, opts []string) (int, error) { + switch p { + case "Release notes": + prompter.AssertOptions(t, []string{"Write my own", "Write using git tag message as template", "Leave blank"}, opts) + return prompter.IndexFor(opts, "Write using git tag message as template") + case "Submit?": + prompter.AssertOptions(t, []string{"Publish release", "Save as draft", "Cancel"}, opts) + return prompter.IndexFor(opts, "Publish release") + default: + return -1, prompter.NoSuchPromptErr(p) + } + } + pm.InputFunc = func(p, d string) (string, error) { + switch p { + case "Title (optional)": + return d, nil + default: + return "", prompter.NoSuchPromptErr(p) + } + } + pm.ConfirmFunc = func(p string, d bool) (bool, error) { + switch p { + case "Is this a prerelease?": + return false, nil + default: + return false, prompter.NoSuchPromptErr(p) + } + } }, runStubs: func(rs *run.CommandStubber) { rs.Register(`git tag --list`, 0, "hello from annotated tag") @@ -1036,13 +1147,35 @@ func Test_createRun_interactive(t *testing.T) { TagName: "v1.2.3", Target: "main", }, - askStubs: func(as *prompt.AskStubber) { - as.StubPrompt("Title (optional)").AnswerWith("") - as.StubPrompt("Release notes"). - AssertOptions([]string{"Write my own", "Write using generated notes as template", "Write using git tag message as template", "Leave blank"}). - AnswerWith("Leave blank") - as.StubPrompt("Is this a prerelease?").AnswerWith(false) - as.StubPrompt("Submit?").AnswerWith("Publish release") + prompterStubs: func(t *testing.T, pm *prompter.PrompterMock) { + pm.SelectFunc = func(p, d string, opts []string) (int, error) { + switch p { + case "Release notes": + prompter.AssertOptions(t, []string{"Write my own", "Write using generated notes as template", "Write using git tag message as template", "Leave blank"}, opts) + return prompter.IndexFor(opts, "Leave blank") + case "Submit?": + prompter.AssertOptions(t, []string{"Publish release", "Save as draft", "Cancel"}, opts) + return prompter.IndexFor(opts, "Publish release") + default: + return -1, prompter.NoSuchPromptErr(p) + } + } + pm.InputFunc = func(p, d string) (string, error) { + switch p { + case "Title (optional)": + return d, nil + default: + return "", prompter.NoSuchPromptErr(p) + } + } + pm.ConfirmFunc = func(p string, d bool) (bool, error) { + switch p { + case "Is this a prerelease?": + return false, nil + default: + return false, prompter.NoSuchPromptErr(p) + } + } }, runStubs: func(rs *run.CommandStubber) { rs.Register(`git tag --list`, 0, "tag exists") @@ -1061,6 +1194,7 @@ func Test_createRun_interactive(t *testing.T) { }, wantParams: map[string]interface{}{ "draft": false, + "name": "generated name", "prerelease": false, "tag_name": "v1.2.3", "target_commitish": "main", @@ -1073,13 +1207,35 @@ func Test_createRun_interactive(t *testing.T) { TagName: "v1.2.3", NotesStartTag: "v1.1.0", }, - askStubs: func(as *prompt.AskStubber) { - as.StubPrompt("Title (optional)").AnswerDefault() - as.StubPrompt("Release notes"). - AssertOptions([]string{"Write my own", "Write using generated notes as template", "Leave blank"}). - AnswerWith("Write using generated notes as template") - as.StubPrompt("Is this a prerelease?").AnswerWith(false) - as.StubPrompt("Submit?").AnswerWith("Publish release") + prompterStubs: func(t *testing.T, pm *prompter.PrompterMock) { + pm.SelectFunc = func(p, d string, opts []string) (int, error) { + switch p { + case "Release notes": + prompter.AssertOptions(t, []string{"Write my own", "Write using generated notes as template", "Leave blank"}, opts) + return prompter.IndexFor(opts, "Write using generated notes as template") + case "Submit?": + prompter.AssertOptions(t, []string{"Publish release", "Save as draft", "Cancel"}, opts) + return prompter.IndexFor(opts, "Publish release") + default: + return -1, prompter.NoSuchPromptErr(p) + } + } + pm.InputFunc = func(p, d string) (string, error) { + switch p { + case "Title (optional)": + return d, nil + default: + return "", prompter.NoSuchPromptErr(p) + } + } + pm.ConfirmFunc = func(p string, d bool) (bool, error) { + switch p { + case "Is this a prerelease?": + return false, nil + default: + return false, prompter.NoSuchPromptErr(p) + } + } }, runStubs: func(rs *run.CommandStubber) { rs.Register(`git tag --list`, 1, "") @@ -1117,13 +1273,35 @@ func Test_createRun_interactive(t *testing.T) { TagName: "v1.2.3", NotesStartTag: "v1.1.0", }, - askStubs: func(as *prompt.AskStubber) { - as.StubPrompt("Title (optional)").AnswerDefault() - as.StubPrompt("Release notes"). - AssertOptions([]string{"Write my own", "Write using commit log as template", "Leave blank"}). - AnswerWith("Write using commit log as template") - as.StubPrompt("Is this a prerelease?").AnswerWith(false) - as.StubPrompt("Submit?").AnswerWith("Publish release") + prompterStubs: func(t *testing.T, pm *prompter.PrompterMock) { + pm.SelectFunc = func(p, d string, opts []string) (int, error) { + switch p { + case "Release notes": + prompter.AssertOptions(t, []string{"Write my own", "Write using commit log as template", "Leave blank"}, opts) + return prompter.IndexFor(opts, "Write using commit log as template") + case "Submit?": + prompter.AssertOptions(t, []string{"Publish release", "Save as draft", "Cancel"}, opts) + return prompter.IndexFor(opts, "Publish release") + default: + return -1, prompter.NoSuchPromptErr(p) + } + } + pm.InputFunc = func(p, d string) (string, error) { + switch p { + case "Title (optional)": + return d, nil + default: + return "", prompter.NoSuchPromptErr(p) + } + } + pm.ConfirmFunc = func(p string, d bool) (bool, error) { + switch p { + case "Is this a prerelease?": + return false, nil + default: + return false, prompter.NoSuchPromptErr(p) + } + } }, runStubs: func(rs *run.CommandStubber) { rs.Register(`git tag --list`, 1, "") @@ -1153,13 +1331,35 @@ func Test_createRun_interactive(t *testing.T) { TagName: "v1.2.3", VerifyTag: true, }, - askStubs: func(as *prompt.AskStubber) { - as.StubPrompt("Title (optional)").AnswerWith("") - as.StubPrompt("Release notes"). - AssertOptions([]string{"Write my own", "Write using generated notes as template", "Write using git tag message as template", "Leave blank"}). - AnswerWith("Leave blank") - as.StubPrompt("Is this a prerelease?").AnswerWith(false) - as.StubPrompt("Submit?").AnswerWith("Publish release") + prompterStubs: func(t *testing.T, pm *prompter.PrompterMock) { + pm.SelectFunc = func(p, d string, opts []string) (int, error) { + switch p { + case "Release notes": + prompter.AssertOptions(t, []string{"Write my own", "Write using generated notes as template", "Write using git tag message as template", "Leave blank"}, opts) + return prompter.IndexFor(opts, "Leave blank") + case "Submit?": + prompter.AssertOptions(t, []string{"Publish release", "Save as draft", "Cancel"}, opts) + return prompter.IndexFor(opts, "Publish release") + default: + return -1, prompter.NoSuchPromptErr(p) + } + } + pm.InputFunc = func(p, d string) (string, error) { + switch p { + case "Title (optional)": + return d, nil + default: + return "", prompter.NoSuchPromptErr(p) + } + } + pm.ConfirmFunc = func(p string, d bool) (bool, error) { + switch p { + case "Is this a prerelease?": + return false, nil + default: + return false, prompter.NoSuchPromptErr(p) + } + } }, runStubs: func(rs *run.CommandStubber) { rs.Register(`git tag --list`, 0, "tag exists") @@ -1180,6 +1380,7 @@ func Test_createRun_interactive(t *testing.T) { }, wantParams: map[string]interface{}{ "draft": false, + "name": "generated name", "prerelease": false, "tag_name": "v1.2.3", }, @@ -1227,11 +1428,11 @@ func Test_createRun_interactive(t *testing.T) { tt.opts.GitClient = &git.Client{GitPath: "some/path/git"} t.Run(tt.name, func(t *testing.T) { - //nolint:staticcheck // SA1019: prompt.NewAskStubber is deprecated: use PrompterMock - as := prompt.NewAskStubber(t) - if tt.askStubs != nil { - tt.askStubs(as) + pm := &prompter.PrompterMock{} + if tt.prompterStubs != nil { + tt.prompterStubs(t, pm) } + tt.opts.Prompter = pm rs, teardown := run.Stub() defer teardown(t) diff --git a/pkg/cmd/release/delete-asset/delete_asset.go b/pkg/cmd/release/delete-asset/delete_asset.go index 1309729d5..126397321 100644 --- a/pkg/cmd/release/delete-asset/delete_asset.go +++ b/pkg/cmd/release/delete-asset/delete_asset.go @@ -4,20 +4,23 @@ import ( "fmt" "net/http" - "github.com/AlecAivazis/survey/v2" "github.com/cli/cli/v2/api" "github.com/cli/cli/v2/internal/ghrepo" "github.com/cli/cli/v2/pkg/cmd/release/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 { + Confirm(string, bool) (bool, error) +} + type DeleteAssetOptions struct { HttpClient func() (*http.Client, error) IO *iostreams.IOStreams BaseRepo func() (ghrepo.Interface, error) + Prompter iprompter TagName string SkipConfirm bool @@ -28,6 +31,7 @@ func NewCmdDeleteAsset(f *cmdutil.Factory, runF func(*DeleteAssetOptions) error) opts := &DeleteAssetOptions{ IO: f.IOStreams, HttpClient: f.HttpClient, + Prompter: f.Prompter, } cmd := &cobra.Command{ @@ -68,12 +72,9 @@ func deleteAssetRun(opts *DeleteAssetOptions) error { } if !opts.SkipConfirm && opts.IO.CanPrompt() { - var confirmed bool - //nolint:staticcheck // SA1019: prompt.SurveyAskOne is deprecated: use Prompter - err := prompt.SurveyAskOne(&survey.Confirm{ - Message: fmt.Sprintf("Delete asset %s in release %s in %s?", opts.AssetName, release.TagName, ghrepo.FullName(baseRepo)), - Default: true, - }, &confirmed) + confirmed, err := opts.Prompter.Confirm( + fmt.Sprintf("Delete asset %s in release %s in %s?", opts.AssetName, release.TagName, ghrepo.FullName(baseRepo)), + true) if err != nil { return err } diff --git a/pkg/cmd/release/delete-asset/delete_asset_test.go b/pkg/cmd/release/delete-asset/delete_asset_test.go index 6d102f375..11f7fbe84 100644 --- a/pkg/cmd/release/delete-asset/delete_asset_test.go +++ b/pkg/cmd/release/delete-asset/delete_asset_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/cli/cli/v2/internal/ghrepo" + "github.com/cli/cli/v2/internal/prompter" "github.com/cli/cli/v2/pkg/cmdutil" "github.com/cli/cli/v2/pkg/httpmock" "github.com/cli/cli/v2/pkg/iostreams" @@ -98,13 +99,32 @@ func Test_NewCmdDeleteAsset(t *testing.T) { func Test_deleteAssetRun(t *testing.T) { tests := []struct { - name string - isTTY bool - opts DeleteAssetOptions - wantErr string - wantStdout string - wantStderr string + name string + isTTY bool + opts DeleteAssetOptions + prompterStubs func(*prompter.PrompterMock) + wantErr string + wantStdout string + wantStderr string }{ + { + name: "interactive confirm", + isTTY: true, + opts: DeleteAssetOptions{ + TagName: "v1.2.3", + AssetName: "test-asset", + }, + prompterStubs: func(pm *prompter.PrompterMock) { + pm.ConfirmFunc = func(p string, d bool) (bool, error) { + if p == "Delete asset test-asset in release v1.2.3 in OWNER/REPO?" { + return true, nil + } + return false, prompter.NoSuchPromptErr(p) + } + }, + wantStdout: ``, + wantStderr: "✓ Deleted asset test-asset from release v1.2.3\n", + }, { name: "skipping confirmation", isTTY: true, @@ -150,7 +170,13 @@ func Test_deleteAssetRun(t *testing.T) { }`)) fakeHTTP.Register(httpmock.REST("DELETE", "repos/OWNER/REPO/releases/assets/1"), httpmock.StatusStringResponse(204, "")) + pm := &prompter.PrompterMock{} + if tt.prompterStubs != nil { + tt.prompterStubs(pm) + } + tt.opts.IO = ios + tt.opts.Prompter = pm tt.opts.HttpClient = func() (*http.Client, error) { return &http.Client{Transport: fakeHTTP}, nil } diff --git a/pkg/cmd/release/delete/delete.go b/pkg/cmd/release/delete/delete.go index 059305be7..3a89c6c2a 100644 --- a/pkg/cmd/release/delete/delete.go +++ b/pkg/cmd/release/delete/delete.go @@ -4,21 +4,24 @@ import ( "fmt" "net/http" - "github.com/AlecAivazis/survey/v2" "github.com/cli/cli/v2/api" "github.com/cli/cli/v2/internal/ghinstance" "github.com/cli/cli/v2/internal/ghrepo" "github.com/cli/cli/v2/pkg/cmd/release/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 { + Confirm(string, bool) (bool, error) +} + type DeleteOptions struct { HttpClient func() (*http.Client, error) IO *iostreams.IOStreams BaseRepo func() (ghrepo.Interface, error) + Prompter iprompter TagName string SkipConfirm bool @@ -29,6 +32,7 @@ func NewCmdDelete(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Co opts := &DeleteOptions{ IO: f.IOStreams, HttpClient: f.HttpClient, + Prompter: f.Prompter, } cmd := &cobra.Command{ @@ -71,12 +75,8 @@ func deleteRun(opts *DeleteOptions) error { } if !opts.SkipConfirm && opts.IO.CanPrompt() { - var confirmed bool - //nolint:staticcheck // SA1019: prompt.SurveyAskOne is deprecated: use Prompter - err := prompt.SurveyAskOne(&survey.Confirm{ - Message: fmt.Sprintf("Delete release %s in %s?", release.TagName, ghrepo.FullName(baseRepo)), - Default: true, - }, &confirmed) + confirmed, err := opts.Prompter.Confirm( + fmt.Sprintf("Delete release %s in %s?", release.TagName, ghrepo.FullName(baseRepo)), true) if err != nil { return err } diff --git a/pkg/cmd/release/delete/delete_test.go b/pkg/cmd/release/delete/delete_test.go index 34ebb03db..906cb057a 100644 --- a/pkg/cmd/release/delete/delete_test.go +++ b/pkg/cmd/release/delete/delete_test.go @@ -8,6 +8,7 @@ import ( "github.com/MakeNowJust/heredoc" "github.com/cli/cli/v2/internal/ghrepo" + "github.com/cli/cli/v2/internal/prompter" "github.com/cli/cli/v2/pkg/cmdutil" "github.com/cli/cli/v2/pkg/httpmock" "github.com/cli/cli/v2/pkg/iostreams" @@ -104,13 +105,31 @@ func Test_NewCmdDelete(t *testing.T) { func Test_deleteRun(t *testing.T) { tests := []struct { - name string - isTTY bool - opts DeleteOptions - wantErr string - wantStdout string - wantStderr string + name string + isTTY bool + opts DeleteOptions + prompterStubs func(*prompter.PrompterMock) + wantErr string + wantStdout string + wantStderr string }{ + { + name: "interactive confirm", + isTTY: true, + opts: DeleteOptions{ + TagName: "v1.2.3", + }, + wantStdout: "", + wantStderr: "✓ Deleted release v1.2.3\n! Note that the v1.2.3 git tag still remains in the repository\n", + prompterStubs: func(pm *prompter.PrompterMock) { + pm.ConfirmFunc = func(p string, d bool) (bool, error) { + if p == "Delete release v1.2.3 in OWNER/REPO?" { + return true, nil + } + return false, prompter.NoSuchPromptErr(p) + } + }, + }, { name: "skipping confirmation", isTTY: true, @@ -168,6 +187,11 @@ func Test_deleteRun(t *testing.T) { ios.SetStdinTTY(tt.isTTY) ios.SetStderrTTY(tt.isTTY) + pm := &prompter.PrompterMock{} + if tt.prompterStubs != nil { + tt.prompterStubs(pm) + } + fakeHTTP := &httpmock.Registry{} fakeHTTP.Register(httpmock.REST("GET", "repos/OWNER/REPO/releases/tags/v1.2.3"), httpmock.StringResponse(`{ "tag_name": "v1.2.3", @@ -179,6 +203,7 @@ func Test_deleteRun(t *testing.T) { fakeHTTP.Register(httpmock.REST("DELETE", "repos/OWNER/REPO/git/refs/tags/v1.2.3"), httpmock.StatusStringResponse(204, "")) tt.opts.IO = ios + tt.opts.Prompter = pm tt.opts.HttpClient = func() (*http.Client, error) { return &http.Client{Transport: fakeHTTP}, nil }