Repository: AlecAivazis/survey
Pull request #446 (2022-08-20 08:39:26)

feat: Add Description function to multi select
Adopting the Description function from select to multi select as proposed in #432

AlecAivazis: 👋 Thanks for submitting this! Please bear with us while we catch up on a lot of PRs that are piling up. We appreciate your patience 
Konkrad: Hello,
I added some tests. If you have ideas for more, please let me know.

Patch:
diff --git a/README.md b/README.md
index ac2c6fc..ba75b32 100644
--- a/README.md
+++ b/README.md
@@ -357,6 +357,39 @@ All of the prompts have a `Help` field which can be defined to provide more info
 }
 ```
 
+## Removing the "Select All" and "Select None" options
+
+By default, users can select all of the multi-select options using the right arrow key. To prevent users from being able to do this (and remove the `<right> to all` message from the prompt), use the option `WithRemoveSelectAll`:
+
+```golang
+import (
+    "github.com/AlecAivazis/survey/v2"
+)
+
+number := ""
+prompt := &survey.Input{
+    Message: "This question has the select all option removed",
+}
+
+survey.AskOne(prompt, &number, survey.WithRemoveSelectAll())
+```
+
+Also by default, users can use the left arrow key to unselect all of the options. To prevent users from being able to do this (and remove the `<left> to none` message from the prompt), use the option `WithRemoveSelectNone`:
+
+```golang
+import (
+    "github.com/AlecAivazis/survey/v2"
+)
+
+number := ""
+prompt := &survey.Input{
+    Message: "This question has the select all option removed",
+}
+
+survey.AskOne(prompt, &number, survey.WithRemoveSelectNone())
+```
+
+
 ### Changing the input rune
 
 In some situations, `?` is a perfectly valid response. To handle this, you can change the rune that survey
diff --git a/input.go b/input.go
index dbc7c08..2fddff5 100644
--- a/input.go
+++ b/input.go
@@ -207,20 +207,13 @@ func (i *Input) Prompt(config *PromptConfig) (interface{}, error) {
 }
 
 func (i *Input) Cleanup(config *PromptConfig, val interface{}) error {
-	// use the default answer when cleaning up the prompt if necessary
-	ans := i.answer
-	if ans == "" && i.Default != "" {
-		ans = i.Default
-	}
-
-	// render the cleanup
 	return i.Render(
 		InputQuestionTemplate,
 		InputTemplateData{
 			Input:      *i,
 			ShowAnswer: true,
 			Config:     config,
-			Answer:     ans,
+			Answer:     val.(string),
 		},
 	)
 }
diff --git a/multiselect.go b/multiselect.go
index 3251ea1..396169f 100644
--- a/multiselect.go
+++ b/multiselect.go
@@ -29,6 +29,7 @@ type MultiSelect struct {
 	VimMode       bool
 	FilterMessage string
 	Filter        func(filter string, value string, index int) bool
+	Description   func(value string, index int) string
 	filter        string
 	selectedIndex int
 	checked       map[int]bool
@@ -43,6 +44,7 @@ type MultiSelectTemplateData struct {
 	Checked       map[int]bool
 	SelectedIndex int
 	ShowHelp      bool
+	Description   func(value string, index int) string
 	PageEntries   []core.OptionAnswer
 	Config        *PromptConfig
 
@@ -59,19 +61,26 @@ func (m MultiSelectTemplateData) IterateOption(ix int, opt core.OptionAnswer) in
 	return copy
 }
 
+func (m MultiSelectTemplateData) GetDescription(opt core.OptionAnswer) string {
+	if m.Description == nil {
+		return ""
+	}
+	return m.Description(opt.Value, opt.Index)
+}
+
 var MultiSelectQuestionTemplate = `
 {{- define "option"}}
     {{- if eq .SelectedIndex .CurrentIndex }}{{color .Config.Icons.SelectFocus.Format }}{{ .Config.Icons.SelectFocus.Text }}{{color "reset"}}{{else}} {{end}}
     {{- if index .Checked .CurrentOpt.Index }}{{color .Config.Icons.MarkedOption.Format }} {{ .Config.Icons.MarkedOption.Text }} {{else}}{{color .Config.Icons.UnmarkedOption.Format }} {{ .Config.Icons.UnmarkedOption.Text }} {{end}}
     {{- color "reset"}}
-    {{- " "}}{{- .CurrentOpt.Value}}
+    {{- " "}}{{- .CurrentOpt.Value}}{{ if ne ($.GetDescription .CurrentOpt) "" }} - {{color "cyan"}}{{ $.GetDescription .CurrentOpt }}{{color "reset"}}{{end}}
 {{end}}
 {{- if .ShowHelp }}{{- color .Config.Icons.Help.Format }}{{ .Config.Icons.Help.Text }} {{ .Help }}{{color "reset"}}{{"\n"}}{{end}}
 {{- color .Config.Icons.Question.Format }}{{ .Config.Icons.Question.Text }} {{color "reset"}}
 {{- color "default+hb"}}{{ .Message }}{{ .FilterMessage }}{{color "reset"}}
 {{- if .ShowAnswer}}{{color "cyan"}} {{.Answer}}{{color "reset"}}{{"\n"}}
 {{- else }}
-	{{- "  "}}{{- color "cyan"}}[Use arrows to move, space to select, <right> to all, <left> to none, type to filter{{- if and .Help (not .ShowHelp)}}, {{ .Config.HelpInput }} for more help{{end}}]{{color "reset"}}
+	{{- "  "}}{{- color "cyan"}}[Use arrows to move, space to select,{{- if not .Config.RemoveSelectAll }} <right> to all,{{end}}{{- if not .Config.RemoveSelectNone }} <left> to none,{{end}} type to filter{{- if and .Help (not .ShowHelp)}}, {{ .Config.HelpInput }} for more help{{end}}]{{color "reset"}}
   {{- "\n"}}
   {{- range $ix, $option := .PageEntries}}
     {{- template "option" $.IterateOption $ix $option}}
@@ -134,14 +143,14 @@ func (m *MultiSelect) OnChange(key rune, config *PromptConfig) {
 	} else if key >= terminal.KeySpace {
 		m.filter += string(key)
 		m.VimMode = false
-	} else if key == terminal.KeyArrowRight {
+	} else if !config.RemoveSelectAll && key == terminal.KeyArrowRight {
 		for _, v := range options {
 			m.checked[v.Index] = true
 		}
 		if !config.KeepFilter {
 			m.filter = ""
 		}
-	} else if key == terminal.KeyArrowLeft {
+	} else if !config.RemoveSelectNone && key == terminal.KeyArrowLeft {
 		for _, v := range options {
 			m.checked[v.Index] = false
 		}
@@ -179,6 +188,7 @@ func (m *MultiSelect) OnChange(key rune, config *PromptConfig) {
 		SelectedIndex: idx,
 		Checked:       m.checked,
 		ShowHelp:      m.showingHelp,
+		Description:   m.Description,
 		PageEntries:   opts,
 		Config:        config,
 	}
@@ -272,6 +282,7 @@ func (m *MultiSelect) Prompt(config *PromptConfig) (interface{}, error) {
 	tmplData := MultiSelectTemplateData{
 		MultiSelect:   *m,
 		SelectedIndex: idx,
+		Description:   m.Description,
 		Checked:       m.checked,
 		PageEntries:   opts,
 		Config:        config,
@@ -342,6 +353,7 @@ func (m *MultiSelect) Cleanup(config *PromptConfig, val interface{}) error {
 			Checked:       m.checked,
 			Answer:        answer,
 			ShowAnswer:    true,
+			Description:   m.Description,
 			Config:        config,
 		},
 	)
diff --git a/select.go b/select.go
index 494f15d..1210122 100644
--- a/select.go
+++ b/select.go
@@ -2,6 +2,7 @@ package survey
 
 import (
 	"errors"
+	"fmt"
 
 	"github.com/AlecAivazis/survey/v2/core"
 	"github.com/AlecAivazis/survey/v2/terminal"
@@ -31,7 +32,6 @@ type Select struct {
 	Description   func(value string, index int) string
 	filter        string
 	selectedIndex int
-	useDefault    bool
 	showingHelp   bool
 }
 
@@ -103,8 +103,6 @@ func (s *Select) OnChange(key rune, config *PromptConfig) bool {
 
 		// if the user pressed the up arrow or 'k' to emulate vim
 	} else if (key == terminal.KeyArrowUp || (s.VimMode && key == 'k')) && len(options) > 0 {
-		s.useDefault = false
-
 		// if we are at the top of the list
 		if s.selectedIndex == 0 {
 			// start from the button
@@ -116,7 +114,6 @@ func (s *Select) OnChange(key rune, config *PromptConfig) bool {
 
 		// if the user pressed down or 'j' to emulate vim
 	} else if (key == terminal.KeyTab || key == terminal.KeyArrowDown || (s.VimMode && key == 'j')) && len(options) > 0 {
-		s.useDefault = false
 		// if we are at the bottom of the list
 		if s.selectedIndex == len(options)-1 {
 			// start from the top
@@ -147,8 +144,6 @@ func (s *Select) OnChange(key rune, config *PromptConfig) bool {
 		s.filter += string(key)
 		// make sure vim mode is disabled
 		s.VimMode = false
-		// make sure that we use the current value in the filtered list
-		s.useDefault = false
 	}
 
 	s.FilterMessage = ""
@@ -207,7 +202,6 @@ func (s *Select) filterOptions(config *PromptConfig) []core.OptionAnswer {
 		filter = config.Filter
 	}
 
-	//
 	for i, opt := range s.Options {
 		// i the filter says to include the option
 		if filter(s.filter, opt, i) {
@@ -229,23 +223,29 @@ func (s *Select) Prompt(config *PromptConfig) (interface{}, error) {
 		return "", errors.New("please provide options to select from")
 	}
 
-	// start off with the first option selected
-	sel := 0
-	// if there is a default
-	if s.Default != "" {
-		// find the choice
-		for i, opt := range s.Options {
-			// if the option corresponds to the default
-			if opt == s.Default {
-				// we found our initial value
-				sel = i
-				// stop looking
-				break
+	s.selectedIndex = 0
+	if s.Default != nil {
+		switch defaultValue := s.Default.(type) {
+		case string:
+			var found bool
+			for i, opt := range s.Options {
+				if opt == defaultValue {
+					s.selectedIndex = i
+					found = true
+				}
+			}
+			if !found {
+				return "", fmt.Errorf("default value %q not found in options", defaultValue)
+			}
+		case int:
+			if defaultValue >= len(s.Options) {
+				return "", fmt.Errorf("default index %d exceeds the number of options", defaultValue)
 			}
+			s.selectedIndex = defaultValue
+		default:
+			return "", errors.New("default value of select must be an int or string")
 		}
 	}
-	// save the selected index
-	s.selectedIndex = sel
 
 	// figure out the page size
 	pageSize := s.PageSize
@@ -256,7 +256,7 @@ func (s *Select) Prompt(config *PromptConfig) (interface{}, error) {
 	}
 
 	// figure out the options and index to render
-	opts, idx := paginate(pageSize, core.OptionAnswerList(s.Options), sel)
+	opts, idx := paginate(pageSize, core.OptionAnswerList(s.Options), s.selectedIndex)
 
 	cursor := s.NewCursor()
 	cursor.Save()          // for proper cursor placement during selection
@@ -279,9 +279,6 @@ func (s *Select) Prompt(config *PromptConfig) (interface{}, error) {
 		return "", err
 	}
 
-	// by default, use the default value
-	s.useDefault = true
-
 	rr := s.NewRuneReader()
 	_ = rr.SetTermMode()
 	defer func() {
@@ -304,45 +301,16 @@ func (s *Select) Prompt(config *PromptConfig) (interface{}, error) {
 			break
 		}
 	}
+
 	options := s.filterOptions(config)
 	s.filter = ""
 	s.FilterMessage = ""
 
-	// the index to report
-	var val string
-	// if we are supposed to use the default value
-	if s.useDefault || s.selectedIndex >= len(options) {
-		// if there is a default value
-		if s.Default != nil {
-			// if the default is a string
-			if defaultString, ok := s.Default.(string); ok {
-				// use the default value
-				val = defaultString
-				// the default value could also be an interpret which is interpretted as the index
-			} else if defaultIndex, ok := s.Default.(int); ok {
-				val = s.Options[defaultIndex]
-			} else {
-				return val, errors.New("default value of select must be an int or string")
-			}
-		} else if len(options) > 0 {
-			// there is no default value so use the first
-			val = options[0].Value
-		}
-		// otherwise the selected index points to the value
-	} else if s.selectedIndex < len(options) {
-		// the
-		val = options[s.selectedIndex].Value
-	}
-
-	// now that we have the value lets go hunt down the right index to return
-	idx = -1
-	for i, optionValue := range s.Options {
-		if optionValue == val {
-			idx = i
-		}
+	if s.selectedIndex < len(options) {
+		return options[s.selectedIndex], err
 	}
 
-	return core.OptionAnswer{Value: val, Index: idx}, err
+	return options[0], err
 }
 
 func (s *Select) Cleanup(config *PromptConfig, val interface{}) error {
diff --git a/survey.go b/survey.go
index 95136eb..3e03b69 100644
--- a/survey.go
+++ b/survey.go
@@ -56,8 +56,10 @@ func defaultAskOptions() *AskOptions {
 				// include this option if it matches
 				return strings.Contains(strings.ToLower(value), filter)
 			},
-			KeepFilter: false,
-			ShowCursor: false,
+			KeepFilter:       false,
+			ShowCursor:       false,
+			RemoveSelectAll:  false,
+			RemoveSelectNone: false,
 		},
 	}
 }
@@ -111,13 +113,15 @@ type Question struct {
 
 // PromptConfig holds the global configuration for a prompt
 type PromptConfig struct {
-	PageSize     int
-	Icons        IconSet
-	HelpInput    string
-	SuggestInput string
-	Filter       func(filter string, option string, index int) bool
-	KeepFilter   bool
-	ShowCursor   bool
+	PageSize         int
+	Icons            IconSet
+	HelpInput        string
+	SuggestInput     string
+	Filter           func(filter string, option string, index int) bool
+	KeepFilter       bool
+	ShowCursor       bool
+	RemoveSelectAll  bool
+	RemoveSelectNone bool
 }
 
 // Prompt is the primary interface for the objects that can take user input
@@ -175,6 +179,22 @@ func WithKeepFilter(KeepFilter bool) AskOpt {
 	}
 }
 
+// WithRemoveSelectAll remove the select all option in Multiselect
+func WithRemoveSelectAll() AskOpt {
+	return func(options *AskOptions) error {
+		options.PromptConfig.RemoveSelectAll = true
+		return nil
+	}
+}
+
+// WithRemoveSelectNone remove the select none/unselect all in Multiselect
+func WithRemoveSelectNone() AskOpt {
+	return func(options *AskOptions) error {
+		options.PromptConfig.RemoveSelectNone = true
+		return nil
+	}
+}
+
 // WithValidator specifies a validator to use while prompting the user
 func WithValidator(v Validator) AskOpt {
 	return func(options *AskOptions) error {


Test patch:
diff --git a/multiselect_test.go b/multiselect_test.go
index 41e8c2b..f0bb23b 100644
--- a/multiselect_test.go
+++ b/multiselect_test.go
@@ -27,6 +27,8 @@ func TestMultiSelectRender(t *testing.T) {
 		Default: []string{"bar", "buz"},
 	}
 
+	descriptions := []string{"oof", "rab", "zab", "zub"}
+
 	helpfulPrompt := prompt
 	helpfulPrompt.Help = "This is helpful"
 
@@ -127,6 +129,121 @@ func TestMultiSelectRender(t *testing.T) {
 				"\n",
 			),
 		},
+		{
+			"description all",
+			prompt,
+			MultiSelectTemplateData{
+				SelectedIndex: 2,
+				PageEntries:   core.OptionAnswerList(prompt.Options),
+				Checked:       map[int]bool{1: true, 3: true},
+				Description: func(value string, index int) string {
+					return descriptions[index]
+				},
+			},
+			strings.Join(
+				[]string{
+					fmt.Sprintf("%s Pick your words:  [Use arrows to move, space to select, <right> to all, <left> to none, type to filter]", defaultIcons().Question.Text),
+					fmt.Sprintf("  %s  foo - oof", defaultIcons().UnmarkedOption.Text),
+					fmt.Sprintf("  %s  bar - rab", defaultIcons().MarkedOption.Text),
+					fmt.Sprintf("%s %s  baz - zab", defaultIcons().SelectFocus.Text, defaultIcons().UnmarkedOption.Text),
+					fmt.Sprintf("  %s  buz - zub\n", defaultIcons().MarkedOption.Text),
+				},
+				"\n",
+			),
+		},
+		{
+			"description even",
+			prompt,
+			MultiSelectTemplateData{
+				SelectedIndex: 2,
+				PageEntries:   core.OptionAnswerList(prompt.Options),
+				Checked:       map[int]bool{1: true, 3: true},
+				Description: func(value string, index int) string {
+
+					if index%2 == 0 {
+						return descriptions[index]
+					}
+
+					return ""
+				},
+			},
+			strings.Join(
+				[]string{
+					fmt.Sprintf("%s Pick your words:  [Use arrows to move, space to select, <right> to all, <left> to none, type to filter]", defaultIcons().Question.Text),
+					fmt.Sprintf("  %s  foo - oof", defaultIcons().UnmarkedOption.Text),
+					fmt.Sprintf("  %s  bar", defaultIcons().MarkedOption.Text),
+					fmt.Sprintf("%s %s  baz - zab", defaultIcons().SelectFocus.Text, defaultIcons().UnmarkedOption.Text),
+					fmt.Sprintf("  %s  buz\n", defaultIcons().MarkedOption.Text),
+				},
+				"\n",
+			),
+		},
+		{
+			"description never",
+			prompt,
+			MultiSelectTemplateData{
+				SelectedIndex: 2,
+				PageEntries:   core.OptionAnswerList(prompt.Options),
+				Checked:       map[int]bool{1: true, 3: true},
+				Description: func(value string, index int) string {
+					return ""
+				},
+			},
+			strings.Join(
+				[]string{
+					fmt.Sprintf("%s Pick your words:  [Use arrows to move, space to select, <right> to all, <left> to none, type to filter]", defaultIcons().Question.Text),
+					fmt.Sprintf("  %s  foo", defaultIcons().UnmarkedOption.Text),
+					fmt.Sprintf("  %s  bar", defaultIcons().MarkedOption.Text),
+					fmt.Sprintf("%s %s  baz", defaultIcons().SelectFocus.Text, defaultIcons().UnmarkedOption.Text),
+					fmt.Sprintf("  %s  buz\n", defaultIcons().MarkedOption.Text),
+				},
+				"\n",
+			),
+		},
+		{
+			"description repeat value",
+			prompt,
+			MultiSelectTemplateData{
+				SelectedIndex: 2,
+				PageEntries:   core.OptionAnswerList(prompt.Options),
+				Checked:       map[int]bool{1: true, 3: true},
+				Description: func(value string, index int) string {
+					return value
+				},
+			},
+			strings.Join(
+				[]string{
+					fmt.Sprintf("%s Pick your words:  [Use arrows to move, space to select, <right> to all, <left> to none, type to filter]", defaultIcons().Question.Text),
+					fmt.Sprintf("  %s  foo - foo", defaultIcons().UnmarkedOption.Text),
+					fmt.Sprintf("  %s  bar - bar", defaultIcons().MarkedOption.Text),
+					fmt.Sprintf("%s %s  baz - baz", defaultIcons().SelectFocus.Text, defaultIcons().UnmarkedOption.Text),
+					fmt.Sprintf("  %s  buz - buz\n", defaultIcons().MarkedOption.Text),
+				},
+				"\n",
+			),
+		},
+		{
+			"description print index",
+			prompt,
+			MultiSelectTemplateData{
+				SelectedIndex: 2,
+				PageEntries:   core.OptionAnswerList(prompt.Options),
+				Checked:       map[int]bool{1: true, 3: true},
+				Description: func(value string, index int) string {
+					return fmt.Sprint(index)
+				},
+			},
+			strings.Join(
+				[]string{
+					fmt.Sprintf("%s Pick your words:  [Use arrows to move, space to select, <right> to all, <left> to none, type to filter]", defaultIcons().Question.Text),
+					fmt.Sprintf("  %s  foo - 0", defaultIcons().UnmarkedOption.Text),
+					fmt.Sprintf("  %s  bar - 1", defaultIcons().MarkedOption.Text),
+					fmt.Sprintf("%s %s  baz - 2", defaultIcons().SelectFocus.Text, defaultIcons().UnmarkedOption.Text),
+					fmt.Sprintf("  %s  buz - 3\n", defaultIcons().MarkedOption.Text),
+				},
+				"\n",
+			),
+		},
 	}
 
 	for _, test := range tests {
@@ -579,3 +696,69 @@ func TestMultiSelectPromptKeepFilter(t *testing.T) {
 		})
 	}
 }
+
+func TestMultiSelectPromptRemoveSelectAll(t *testing.T) {
+	tests := []PromptTest{
+		{
+			"multi select with remove select all option",
+			&MultiSelect{
+				Message: "What color do you prefer:",
+				Options: []string{"green", "red", "light-green", "blue", "black", "yellow", "purple"},
+			},
+			func(c expectConsole) {
+				c.ExpectString("What color do you prefer:  [Use arrows to move, space to select, <left> to none, type to filter]")
+				// Select the first option "green"
+				c.Send(" ")
+
+				// attempt to select all (this shouldn't do anything)
+				c.Send(string(terminal.KeyArrowRight))
+
+				// end the session
+				c.SendLine("")
+				c.ExpectEOF()
+			},
+			[]core.OptionAnswer{ // we should only have one option selected, not all of them
+				{Value: "green", Index: 0},
+			},
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			RunPromptTestRemoveSelectAll(t, test)
+		})
+	}
+}
+
+func TestMultiSelectPromptRemoveSelectNone(t *testing.T) {
+	tests := []PromptTest{
+		{
+			"multi select with remove select none option",
+			&MultiSelect{
+				Message: "What color do you prefer:",
+				Options: []string{"green", "red", "light-green", "blue", "black", "yellow", "purple"},
+			},
+			func(c expectConsole) {
+				c.ExpectString("What color do you prefer:  [Use arrows to move, space to select, <right> to all, type to filter]")
+				// Select the first option "green"
+				c.Send(" ")
+
+				// attempt to unselect all (this shouldn't do anything)
+				c.Send(string(terminal.KeyArrowLeft))
+
+				// end the session
+				c.SendLine("")
+				c.ExpectEOF()
+			},
+			[]core.OptionAnswer{ // we should only have one option selected, not all of them
+				{Value: "green", Index: 0},
+			},
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			RunPromptTestRemoveSelectNone(t, test)
+		})
+	}
+}
diff --git a/survey_test.go b/survey_test.go
index d0d6230..1b1ea41 100644
--- a/survey_test.go
+++ b/survey_test.go
@@ -97,6 +97,38 @@ func RunPromptTestKeepFilter(t *testing.T, test PromptTest) {
 	require.Equal(t, test.expected, answer)
 }
 
+func RunPromptTestRemoveSelectAll(t *testing.T, test PromptTest) {
+	t.Helper()
+	var answer interface{}
+	RunTest(t, test.procedure, func(stdio terminal.Stdio) error {
+		var err error
+		if p, ok := test.prompt.(wantsStdio); ok {
+			p.WithStdio(stdio)
+		}
+		config := defaultPromptConfig()
+		config.RemoveSelectAll = true
+		answer, err = test.prompt.Prompt(config)
+		return err
+	})
+	require.Equal(t, test.expected, answer)
+}
+
+func RunPromptTestRemoveSelectNone(t *testing.T, test PromptTest) {
+	t.Helper()
+	var answer interface{}
+	RunTest(t, test.procedure, func(stdio terminal.Stdio) error {
+		var err error
+		if p, ok := test.prompt.(wantsStdio); ok {
+			p.WithStdio(stdio)
+		}
+		config := defaultPromptConfig()
+		config.RemoveSelectNone = true
+		answer, err = test.prompt.Prompt(config)
+		return err
+	})
+	require.Equal(t, test.expected, answer)
+}
+
 func TestPagination_tooFew(t *testing.T) {
 	// a small list of options
 	choices := core.OptionAnswerList([]string{"choice1", "choice2", "choice3"})
@@ -348,6 +380,7 @@ func TestAsk(t *testing.T) {
 			func(c expectConsole) {
 				c.ExpectString("What is your name?")
 				c.SendLine("Johnny Appleseed")
+				c.ExpectString("What is your name? johnny appleseed")
 				c.ExpectEOF()
 			},
 			map[string]interface{}{
diff --git a/tests/multiselect.go b/tests/multiselect.go
index c1eff2d..cbed00c 100644
--- a/tests/multiselect.go
+++ b/tests/multiselect.go
@@ -1,8 +1,10 @@
 package main
 
 import (
+	"fmt"
+
 	"github.com/AlecAivazis/survey/v2"
-	"github.com/AlecAivazis/survey/v2/tests/util"
+	TestUtil "github.com/AlecAivazis/survey/v2/tests/util"
 )
 
 var answer = []string{}
@@ -42,6 +44,16 @@ var table = []TestUtil.TestTableEntry{
 			Default: []string{"Sundayaa"},
 		}, &answer, nil,
 	},
+	{
+		"descriptions", &survey.MultiSelect{
+			Message: "What days do you prefer:",
+			Options: []string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"},
+			Description: func(value string, index int) string {
+				return value + fmt.Sprint(index)
+
+			},
+		}, &answer, nil,
+	},
 }
 
 func main() {