This repository has been archived by the owner on Apr 19, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 354
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Added multiline input * Test for multiline input * Use of existing API to clear lines instead of escape sequence * Multiline input example added to README * multiline-input: change message to finish * multiline-input: show the response in a new line * MultilineInput renamed to Multiline * README updated
- Loading branch information
1 parent
1fa814d
commit fb519f8
Showing
3 changed files
with
270 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
package survey | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
|
||
"gopkg.in/AlecAivazis/survey.v1/core" | ||
"gopkg.in/AlecAivazis/survey.v1/terminal" | ||
) | ||
|
||
type Multiline struct { | ||
core.Renderer | ||
Message string | ||
Default string | ||
Help string | ||
} | ||
|
||
// data available to the templates when processing | ||
type MultilineTemplateData struct { | ||
Multiline | ||
Answer string | ||
ShowAnswer bool | ||
ShowHelp bool | ||
} | ||
|
||
// Templates with Color formatting. See Documentation: https://github.com/mgutz/ansi#style-format | ||
var MultilineQuestionTemplate = ` | ||
{{- if .ShowHelp }}{{- color "cyan"}}{{ HelpIcon }} {{ .Help }}{{color "reset"}}{{"\n"}}{{end}} | ||
{{- color "green+hb"}}{{ QuestionIcon }} {{color "reset"}} | ||
{{- color "default+hb"}}{{ .Message }} {{color "reset"}} | ||
{{- if .ShowAnswer}} | ||
{{- "\n"}}{{color "cyan"}}{{.Answer}}{{color "reset"}}{{"\n"}} | ||
{{- else }} | ||
{{- if .Default}}{{color "white"}}({{.Default}}) {{color "reset"}}{{end}} | ||
{{- color "cyan"}}[Enter 2 empty lines to finish]{{color "reset"}} | ||
{{- end}}` | ||
|
||
func (i *Multiline) Prompt() (interface{}, error) { | ||
// render the template | ||
err := i.Render( | ||
MultilineQuestionTemplate, | ||
MultilineTemplateData{Multiline: *i}, | ||
) | ||
if err != nil { | ||
return "", err | ||
} | ||
fmt.Println() | ||
|
||
// start reading runes from the standard in | ||
rr := i.NewRuneReader() | ||
rr.SetTermMode() | ||
defer rr.RestoreTermMode() | ||
|
||
cursor := i.NewCursor() | ||
|
||
multiline := make([]string, 0) | ||
|
||
emptyOnce := false | ||
// get the next line | ||
for { | ||
line := []rune{} | ||
line, err = rr.ReadLine(0) | ||
if err != nil { | ||
return string(line), err | ||
} | ||
|
||
if string(line) == "" { | ||
if emptyOnce { | ||
numLines := len(multiline) + 2 | ||
cursor.PreviousLine(numLines) | ||
for j := 0; j < numLines; j++ { | ||
terminal.EraseLine(i.Stdio().Out, terminal.ERASE_LINE_ALL) | ||
cursor.NextLine(1) | ||
} | ||
cursor.PreviousLine(numLines) | ||
break | ||
} | ||
emptyOnce = true | ||
} else { | ||
emptyOnce = false | ||
} | ||
multiline = append(multiline, string(line)) | ||
} | ||
|
||
val := strings.Join(multiline, "\n") | ||
val = strings.TrimSpace(val) | ||
|
||
// if the line is empty | ||
if len(val) == 0 { | ||
// use the default value | ||
return i.Default, err | ||
} | ||
|
||
// we're done | ||
return val, err | ||
} | ||
|
||
func (i *Multiline) Cleanup(val interface{}) error { | ||
return i.Render( | ||
MultilineQuestionTemplate, | ||
MultilineTemplateData{Multiline: *i, Answer: val.(string), ShowAnswer: true}, | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
package survey | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"io" | ||
"os" | ||
"testing" | ||
|
||
expect "github.com/Netflix/go-expect" | ||
"github.com/stretchr/testify/assert" | ||
"gopkg.in/AlecAivazis/survey.v1/core" | ||
"gopkg.in/AlecAivazis/survey.v1/terminal" | ||
) | ||
|
||
func init() { | ||
// disable color output for all prompts to simplify testing | ||
core.DisableColor = true | ||
} | ||
|
||
func TestMultilineRender(t *testing.T) { | ||
|
||
tests := []struct { | ||
title string | ||
prompt Multiline | ||
data MultilineTemplateData | ||
expected string | ||
}{ | ||
{ | ||
"Test Multiline question output without default", | ||
Multiline{Message: "What is your favorite month:"}, | ||
MultilineTemplateData{}, | ||
fmt.Sprintf("%s What is your favorite month: [Enter 2 empty lines to finish]", core.QuestionIcon), | ||
}, | ||
{ | ||
"Test Multiline question output with default", | ||
Multiline{Message: "What is your favorite month:", Default: "April"}, | ||
MultilineTemplateData{}, | ||
fmt.Sprintf("%s What is your favorite month: (April) [Enter 2 empty lines to finish]", core.QuestionIcon), | ||
}, | ||
{ | ||
"Test Multiline answer output", | ||
Multiline{Message: "What is your favorite month:"}, | ||
MultilineTemplateData{Answer: "October", ShowAnswer: true}, | ||
fmt.Sprintf("%s What is your favorite month: \nOctober\n", core.QuestionIcon), | ||
}, | ||
{ | ||
"Test Multiline question output without default but with help hidden", | ||
Multiline{Message: "What is your favorite month:", Help: "This is helpful"}, | ||
MultilineTemplateData{}, | ||
fmt.Sprintf("%s What is your favorite month: [Enter 2 empty lines to finish]", string(core.HelpInputRune)), | ||
}, | ||
{ | ||
"Test Multiline question output with default and with help hidden", | ||
Multiline{Message: "What is your favorite month:", Default: "April", Help: "This is helpful"}, | ||
MultilineTemplateData{}, | ||
fmt.Sprintf("%s What is your favorite month: (April) [Enter 2 empty lines to finish]", string(core.HelpInputRune)), | ||
}, | ||
{ | ||
"Test Multiline question output without default but with help shown", | ||
Multiline{Message: "What is your favorite month:", Help: "This is helpful"}, | ||
MultilineTemplateData{ShowHelp: true}, | ||
fmt.Sprintf("%s This is helpful\n%s What is your favorite month: [Enter 2 empty lines to finish]", core.HelpIcon, core.QuestionIcon), | ||
}, | ||
{ | ||
"Test Multiline question output with default and with help shown", | ||
Multiline{Message: "What is your favorite month:", Default: "April", Help: "This is helpful"}, | ||
MultilineTemplateData{ShowHelp: true}, | ||
fmt.Sprintf("%s This is helpful\n%s What is your favorite month: (April) [Enter 2 empty lines to finish]", core.HelpIcon, core.QuestionIcon), | ||
}, | ||
} | ||
|
||
for _, test := range tests { | ||
r, w, err := os.Pipe() | ||
assert.Nil(t, err, test.title) | ||
|
||
test.prompt.WithStdio(terminal.Stdio{Out: w}) | ||
test.data.Multiline = test.prompt | ||
err = test.prompt.Render( | ||
MultilineQuestionTemplate, | ||
test.data, | ||
) | ||
assert.Nil(t, err, test.title) | ||
|
||
w.Close() | ||
var buf bytes.Buffer | ||
io.Copy(&buf, r) | ||
|
||
assert.Contains(t, buf.String(), test.expected, test.title) | ||
} | ||
} | ||
|
||
func TestMultilinePrompt(t *testing.T) { | ||
tests := []PromptTest{ | ||
{ | ||
"Test Multiline prompt interaction", | ||
&Multiline{ | ||
Message: "What is your name?", | ||
}, | ||
func(c *expect.Console) { | ||
c.ExpectString("What is your name?") | ||
c.SendLine("Larry Bird\nI guess...\nnot sure\n\n") | ||
c.ExpectEOF() | ||
}, | ||
"Larry Bird\nI guess...\nnot sure", | ||
}, | ||
{ | ||
"Test Multiline prompt interaction with default", | ||
&Multiline{ | ||
Message: "What is your name?", | ||
Default: "Johnny Appleseed", | ||
}, | ||
func(c *expect.Console) { | ||
c.ExpectString("What is your name?") | ||
c.SendLine("\n\n") | ||
c.ExpectEOF() | ||
}, | ||
"Johnny Appleseed", | ||
}, | ||
{ | ||
"Test Multiline prompt interaction overriding default", | ||
&Multiline{ | ||
Message: "What is your name?", | ||
Default: "Johnny Appleseed", | ||
}, | ||
func(c *expect.Console) { | ||
c.ExpectString("What is your name?") | ||
c.SendLine("Larry Bird\n\n") | ||
c.ExpectEOF() | ||
}, | ||
"Larry Bird", | ||
}, | ||
{ | ||
"Test Multiline does not implement help interaction", | ||
&Multiline{ | ||
Message: "What is your name?", | ||
Help: "It might be Satoshi Nakamoto", | ||
}, | ||
func(c *expect.Console) { | ||
c.ExpectString("What is your name?") | ||
c.SendLine("?") | ||
c.SendLine("Satoshi Nakamoto\n\n") | ||
c.ExpectEOF() | ||
}, | ||
"?\nSatoshi Nakamoto", | ||
}, | ||
} | ||
|
||
for _, test := range tests { | ||
t.Run(test.name, func(t *testing.T) { | ||
RunPromptTest(t, test) | ||
}) | ||
} | ||
} |