Skip to content

Commit

Permalink
Add validators functionality to prompts
Browse files Browse the repository at this point in the history
  • Loading branch information
mpanchajanya committed Apr 25, 2023
1 parent 3fa0bda commit ee5d4f7
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 0 deletions.
19 changes: 19 additions & 0 deletions component/prompt.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ type PromptOptions struct {
// Standard in/out/error
Stdio terminal.Stdio
Icons survey.IconSet

// Validators on user inputs
Validators []survey.Validator
}

// PromptOpt is an option for prompts
Expand All @@ -127,6 +130,11 @@ func translatePromptOpts(options *PromptOptions) (surveyOpts []survey.AskOpt) {
icons.SelectFocus = options.Icons.SelectFocus
}))

// Add validators
for _, v := range options.Validators {
surveyOpts = append(surveyOpts, survey.WithValidator(v))
}

return
}

Expand All @@ -140,3 +148,14 @@ func WithStdio(in terminal.FileReader, out terminal.FileWriter, err io.Writer) P
return nil
}
}

// WithValidator specifies a validator to use while prompting the user
func WithValidator(v survey.Validator) PromptOpt {
return func(options *PromptOptions) error {
// add the provided validator to the list
options.Validators = append(options.Validators, v)

// nothing went wrong
return nil
}
}
83 changes: 83 additions & 0 deletions component/prompt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
package component

import (
"errors"
"strings"
"testing"

"github.com/AlecAivazis/survey/v2"
Expand Down Expand Up @@ -79,3 +81,84 @@ func Test_PromptOptions(t *testing.T) {
assert.Equal("cyan+b", options.Icons.Question.Format)
assert.Equal(2, len(opts))
}

func TestAskPrompt_Validation(t *testing.T) {
// Setup mock prompts
p := &mockPrompt{
answers: []string{"", "very-very-long-name", "ALL_CAPS_NAME", "Test", "test"},
}

var res string

// Setup PromptOpts validators
var promptOpts []PromptOpt
noUpperCase := func(v interface{}) error {
s := v.(string)
if strings.ToLower(s) != s {
return errors.New("value contains uppercase characters")
}
return nil
}
promptOpts = append(promptOpts, WithValidator(noUpperCase), WithValidator(survey.Required), WithValidator(survey.MaxLength(4)))

// Prepare the surveyOpts
var options = &PromptOptions{}
for _, opt := range promptOpts {
err := opt(options)
assert.Nil(t, err)
}
surveyOpts := translatePromptOpts(options)

// Trigger the Prompt
err := survey.Ask([]*survey.Question{
{
Prompt: p,
},
}, &res, surveyOpts...)

if err != nil {
t.Fatalf("Ask() = %v", err)
}

if res != "test" {
t.Errorf("answer: %q, want %q", res, "test")
}
if p.cleanups != 1 {
t.Errorf("cleanups: %d, want %d", p.cleanups, 1)
}
if err1 := p.printedErrors[0].Error(); err1 != "Value is required" {
t.Errorf("printed error 1: %q, want %q", err1, "Value is required")
}
if err2 := p.printedErrors[1].Error(); err2 != "value is too long. Max length is 4" {
t.Errorf("printed error 2: %q, want %q", err2, "value is too long. Max length is 4")
}
if err3 := p.printedErrors[2].Error(); err3 != "value contains uppercase characters" {
t.Errorf("printed error 2: %q, want %q", err3, "value contains uppercase characters")
}
}

type mockPrompt struct {
index int
answers []string
cleanups int
printedErrors []error
}

func (p *mockPrompt) Prompt(*survey.PromptConfig) (interface{}, error) {
if p.index >= len(p.answers) {
return nil, errors.New("no valid answers provided")
}
val := p.answers[p.index]
p.index++
return val, nil
}

func (p *mockPrompt) Cleanup(*survey.PromptConfig, interface{}) error {
p.cleanups++
return nil
}

func (p *mockPrompt) Error(_ *survey.PromptConfig, err error) error {
p.printedErrors = append(p.printedErrors, err)
return nil
}

0 comments on commit ee5d4f7

Please sign in to comment.