fix(prompter): pass io to huh and refactor tests
This commit is contained in:
parent
2f5e896535
commit
fab0de5583
2 changed files with 84 additions and 142 deletions
|
|
@ -5,9 +5,7 @@ package prompter_test
|
|||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
|
@ -20,54 +18,11 @@ import (
|
|||
)
|
||||
|
||||
func TestAccessiblePrompter(t *testing.T) {
|
||||
// Create a PTY and hook up a virtual terminal emulator
|
||||
ptm, pts, err := pty.Open()
|
||||
require.NoError(t, err)
|
||||
|
||||
term := vt10x.New(vt10x.WithWriter(pts))
|
||||
|
||||
// Create a console via Expect that allows scripting against the terminal
|
||||
consoleOpts := []expect.ConsoleOpt{
|
||||
expect.WithStdin(ptm),
|
||||
expect.WithStdout(term),
|
||||
expect.WithCloser(ptm, pts),
|
||||
failOnExpectError(t),
|
||||
failOnSendError(t),
|
||||
expect.WithDefaultTimeout(time.Second),
|
||||
}
|
||||
|
||||
console, err := expect.NewConsole(consoleOpts...)
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() { testCloser(t, console) })
|
||||
|
||||
// Using OS here because huh currently ignores configured iostreams
|
||||
// See https://github.com/charmbracelet/huh/issues/612
|
||||
stdIn := os.Stdin
|
||||
stdOut := os.Stdout
|
||||
stdErr := os.Stderr
|
||||
|
||||
t.Cleanup(func() {
|
||||
os.Stdin = stdIn
|
||||
os.Stdout = stdOut
|
||||
os.Stderr = stdErr
|
||||
})
|
||||
|
||||
os.Stdin = console.Tty()
|
||||
os.Stdout = console.Tty()
|
||||
os.Stderr = console.Tty()
|
||||
|
||||
t.Setenv("GH_ACCESSIBLE_PROMPTER", "true")
|
||||
// Using echo as the editor command here because it will immediately exit
|
||||
// and return no input.
|
||||
p := prompter.New("echo", nil, nil, nil)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
t.Run("Select", func(t *testing.T) {
|
||||
wg.Add(1)
|
||||
console := newTestVirtualTerminal(t)
|
||||
p := newTestAcessiblePrompter(t, console)
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
// Wait for prompt to appear
|
||||
_, err := console.ExpectString("Choose:")
|
||||
require.NoError(t, err)
|
||||
|
|
@ -80,15 +35,13 @@ func TestAccessiblePrompter(t *testing.T) {
|
|||
selectValue, err := p.Select("Select a number", "", []string{"1", "2", "3"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 0, selectValue)
|
||||
|
||||
wg.Wait()
|
||||
})
|
||||
|
||||
t.Run("MultiSelect", func(t *testing.T) {
|
||||
wg.Add(1)
|
||||
console := newTestVirtualTerminal(t)
|
||||
p := newTestAcessiblePrompter(t, console)
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
// Wait for prompt to appear
|
||||
_, err := console.ExpectString("Select a number")
|
||||
require.NoError(t, err)
|
||||
|
|
@ -107,16 +60,14 @@ func TestAccessiblePrompter(t *testing.T) {
|
|||
multiSelectValue, err := p.MultiSelect("Select a number", []string{}, []string{"1", "2", "3"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []int{0, 1}, multiSelectValue)
|
||||
|
||||
wg.Wait()
|
||||
})
|
||||
|
||||
t.Run("Input", func(t *testing.T) {
|
||||
wg.Add(1)
|
||||
|
||||
console := newTestVirtualTerminal(t)
|
||||
p := newTestAcessiblePrompter(t, console)
|
||||
dummyText := "12345abcdefg"
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
// Wait for prompt to appear
|
||||
_, err := console.ExpectString("Enter some characters")
|
||||
require.NoError(t, err)
|
||||
|
|
@ -129,16 +80,14 @@ func TestAccessiblePrompter(t *testing.T) {
|
|||
inputValue, err := p.Input("Enter some characters", "")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, dummyText, inputValue)
|
||||
|
||||
wg.Wait()
|
||||
})
|
||||
|
||||
t.Run("Input - blank input returns default value", func(t *testing.T) {
|
||||
wg.Add(1)
|
||||
|
||||
console := newTestVirtualTerminal(t)
|
||||
p := newTestAcessiblePrompter(t, console)
|
||||
dummyDefaultValue := "12345abcdefg"
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
// Wait for prompt to appear
|
||||
_, err := console.ExpectString("Enter some characters")
|
||||
require.NoError(t, err)
|
||||
|
|
@ -155,16 +104,14 @@ func TestAccessiblePrompter(t *testing.T) {
|
|||
inputValue, err := p.Input("Enter some characters", dummyDefaultValue)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, dummyDefaultValue, inputValue)
|
||||
|
||||
wg.Wait()
|
||||
})
|
||||
|
||||
t.Run("Password", func(t *testing.T) {
|
||||
wg.Add(1)
|
||||
|
||||
console := newTestVirtualTerminal(t)
|
||||
p := newTestAcessiblePrompter(t, console)
|
||||
dummyPassword := "12345abcdefg"
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
// Wait for prompt to appear
|
||||
_, err := console.ExpectString("Enter password")
|
||||
require.NoError(t, err)
|
||||
|
|
@ -177,15 +124,13 @@ func TestAccessiblePrompter(t *testing.T) {
|
|||
passwordValue, err := p.Password("Enter password")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, dummyPassword, passwordValue)
|
||||
|
||||
wg.Wait()
|
||||
})
|
||||
|
||||
t.Run("Confirm", func(t *testing.T) {
|
||||
wg.Add(1)
|
||||
console := newTestVirtualTerminal(t)
|
||||
p := newTestAcessiblePrompter(t, console)
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
// Wait for prompt to appear
|
||||
_, err := console.ExpectString("Are you sure")
|
||||
require.NoError(t, err)
|
||||
|
|
@ -198,11 +143,12 @@ func TestAccessiblePrompter(t *testing.T) {
|
|||
confirmValue, err := p.Confirm("Are you sure", false)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, confirmValue)
|
||||
|
||||
wg.Wait()
|
||||
})
|
||||
|
||||
t.Run("Confirm - blank input returns default", func(t *testing.T) {
|
||||
console := newTestVirtualTerminal(t)
|
||||
p := newTestAcessiblePrompter(t, console)
|
||||
|
||||
go func() {
|
||||
// Wait for prompt to appear
|
||||
_, err := console.ExpectString("Are you sure")
|
||||
|
|
@ -219,11 +165,11 @@ func TestAccessiblePrompter(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("AuthToken", func(t *testing.T) {
|
||||
wg.Add(1)
|
||||
|
||||
console := newTestVirtualTerminal(t)
|
||||
p := newTestAcessiblePrompter(t, console)
|
||||
dummyAuthToken := "12345abcdefg"
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
// Wait for prompt to appear
|
||||
_, err := console.ExpectString("Paste your authentication token:")
|
||||
require.NoError(t, err)
|
||||
|
|
@ -236,16 +182,14 @@ func TestAccessiblePrompter(t *testing.T) {
|
|||
authValue, err := p.AuthToken()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, dummyAuthToken, authValue)
|
||||
|
||||
wg.Wait()
|
||||
})
|
||||
|
||||
t.Run("AuthToken - blank input returns error", func(t *testing.T) {
|
||||
wg.Add(1)
|
||||
|
||||
console := newTestVirtualTerminal(t)
|
||||
p := newTestAcessiblePrompter(t, console)
|
||||
dummyAuthTokenForAfterFailure := "12345abcdefg"
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
// Wait for prompt to appear
|
||||
_, err := console.ExpectString("Paste your authentication token:")
|
||||
require.NoError(t, err)
|
||||
|
|
@ -266,16 +210,14 @@ func TestAccessiblePrompter(t *testing.T) {
|
|||
authValue, err := p.AuthToken()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, dummyAuthTokenForAfterFailure, authValue)
|
||||
|
||||
wg.Wait()
|
||||
})
|
||||
|
||||
t.Run("ConfirmDeletion", func(t *testing.T) {
|
||||
wg.Add(1)
|
||||
console := newTestVirtualTerminal(t)
|
||||
p := newTestAcessiblePrompter(t, console)
|
||||
|
||||
requiredValue := "test"
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
// Wait for prompt to appear
|
||||
_, err := console.ExpectString(fmt.Sprintf("Type %q to confirm deletion", requiredValue))
|
||||
require.NoError(t, err)
|
||||
|
|
@ -288,17 +230,15 @@ func TestAccessiblePrompter(t *testing.T) {
|
|||
// An err indicates that the confirmation text sent did not match
|
||||
err := p.ConfirmDeletion(requiredValue)
|
||||
require.NoError(t, err)
|
||||
|
||||
wg.Wait()
|
||||
})
|
||||
|
||||
t.Run("ConfirmDeletion - bad input", func(t *testing.T) {
|
||||
wg.Add(1)
|
||||
|
||||
console := newTestVirtualTerminal(t)
|
||||
p := newTestAcessiblePrompter(t, console)
|
||||
requiredValue := "test"
|
||||
badInputValue := "garbage"
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
// Wait for prompt to appear
|
||||
_, err := console.ExpectString(fmt.Sprintf("Type %q to confirm deletion", requiredValue))
|
||||
require.NoError(t, err)
|
||||
|
|
@ -319,16 +259,14 @@ func TestAccessiblePrompter(t *testing.T) {
|
|||
// An err indicates that the confirmation text sent did not match
|
||||
err := p.ConfirmDeletion(requiredValue)
|
||||
require.NoError(t, err)
|
||||
|
||||
wg.Wait()
|
||||
})
|
||||
|
||||
t.Run("InputHostname", func(t *testing.T) {
|
||||
wg.Add(1)
|
||||
|
||||
console := newTestVirtualTerminal(t)
|
||||
p := newTestAcessiblePrompter(t, console)
|
||||
hostname := "example.com"
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
// Wait for prompt to appear
|
||||
_, err := console.ExpectString("Hostname:")
|
||||
require.NoError(t, err)
|
||||
|
|
@ -341,15 +279,13 @@ func TestAccessiblePrompter(t *testing.T) {
|
|||
inputValue, err := p.InputHostname()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, hostname, inputValue)
|
||||
|
||||
wg.Wait()
|
||||
})
|
||||
|
||||
t.Run("MarkdownEditor - blank allowed with blank input returns blank", func(t *testing.T) {
|
||||
wg.Add(1)
|
||||
console := newTestVirtualTerminal(t)
|
||||
p := newTestAcessiblePrompter(t, console)
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
// Wait for prompt to appear
|
||||
_, err := console.ExpectString("How to edit?")
|
||||
require.NoError(t, err)
|
||||
|
|
@ -362,16 +298,14 @@ func TestAccessiblePrompter(t *testing.T) {
|
|||
inputValue, err := p.MarkdownEditor("How to edit?", "", true)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "", inputValue)
|
||||
|
||||
wg.Wait()
|
||||
})
|
||||
|
||||
t.Run("MarkdownEditor - blank disallowed with default value returns default value", func(t *testing.T) {
|
||||
wg.Add(1)
|
||||
|
||||
console := newTestVirtualTerminal(t)
|
||||
p := newTestAcessiblePrompter(t, console)
|
||||
defaultValue := "12345abcdefg"
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
// Wait for prompt to appear
|
||||
_, err := console.ExpectString("How to edit?")
|
||||
require.NoError(t, err)
|
||||
|
|
@ -392,15 +326,13 @@ func TestAccessiblePrompter(t *testing.T) {
|
|||
inputValue, err := p.MarkdownEditor("How to edit?", defaultValue, false)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, defaultValue, inputValue)
|
||||
|
||||
wg.Wait()
|
||||
})
|
||||
|
||||
t.Run("MarkdownEditor - blank disallowed no default value returns error", func(t *testing.T) {
|
||||
wg.Add(1)
|
||||
console := newTestVirtualTerminal(t)
|
||||
p := newTestAcessiblePrompter(t, console)
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
// Wait for prompt to appear
|
||||
_, err := console.ExpectString("How to edit?")
|
||||
require.NoError(t, err)
|
||||
|
|
@ -422,46 +354,18 @@ func TestAccessiblePrompter(t *testing.T) {
|
|||
inputValue, err := p.MarkdownEditor("How to edit?", "", false)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "", inputValue)
|
||||
|
||||
wg.Wait()
|
||||
})
|
||||
}
|
||||
|
||||
func TestSurveyPrompter(t *testing.T) {
|
||||
// Create a PTY and hook up a virtual terminal emulator
|
||||
ptm, pts, err := pty.Open()
|
||||
require.NoError(t, err)
|
||||
|
||||
term := vt10x.New(vt10x.WithWriter(pts))
|
||||
|
||||
// Create a console via Expect that allows scripting against the terminal
|
||||
consoleOpts := []expect.ConsoleOpt{
|
||||
expect.WithStdin(ptm),
|
||||
expect.WithStdout(term),
|
||||
expect.WithCloser(ptm, pts),
|
||||
failOnExpectError(t),
|
||||
failOnSendError(t),
|
||||
expect.WithDefaultTimeout(time.Second * 600),
|
||||
}
|
||||
|
||||
console, err := expect.NewConsole(consoleOpts...)
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() { testCloser(t, console) })
|
||||
|
||||
// Using echo as the editor command here because it will immediately exit
|
||||
// and return no input.
|
||||
p := prompter.New("echo", console.Tty(), console.Tty(), console.Tty())
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
// This not a comprehensive test of the survey prompter, but it does
|
||||
// demonstrate that the survey prompter is used when the
|
||||
// accessible prompter is disabled.
|
||||
t.Run("Select uses survey prompter when accessible prompter is disabled", func(t *testing.T) {
|
||||
wg.Add(1)
|
||||
console := newTestVirtualTerminal(t)
|
||||
p := newTestSurveyPrompter(t, console)
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
// Wait for prompt to appear
|
||||
_, err := console.ExpectString("Select a number")
|
||||
require.NoError(t, err)
|
||||
|
|
@ -477,11 +381,49 @@ func TestSurveyPrompter(t *testing.T) {
|
|||
selectValue, err := p.Select("Select a number", "", []string{"1", "2", "3"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 0, selectValue)
|
||||
|
||||
wg.Wait()
|
||||
})
|
||||
}
|
||||
|
||||
func newTestVirtualTerminal(t testing.TB) *expect.Console {
|
||||
t.Helper()
|
||||
|
||||
// Create a PTY and hook up a virtual terminal emulator
|
||||
ptm, pts, err := pty.Open()
|
||||
require.NoError(t, err)
|
||||
|
||||
term := vt10x.New(vt10x.WithWriter(pts))
|
||||
|
||||
// Create a console via Expect that allows scripting against the terminal
|
||||
consoleOpts := []expect.ConsoleOpt{
|
||||
expect.WithStdin(ptm),
|
||||
expect.WithStdout(term),
|
||||
expect.WithCloser(ptm, pts),
|
||||
failOnExpectError(t),
|
||||
failOnSendError(t),
|
||||
expect.WithDefaultTimeout(time.Second),
|
||||
}
|
||||
|
||||
console, err := expect.NewConsole(consoleOpts...)
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() { testCloser(t, console) })
|
||||
|
||||
return console
|
||||
}
|
||||
|
||||
func newTestAcessiblePrompter(t testing.TB, console *expect.Console) prompter.Prompter {
|
||||
t.Helper()
|
||||
|
||||
t.Setenv("GH_ACCESSIBLE_PROMPTER", "true")
|
||||
return prompter.New("echo", console.Tty(), console.Tty(), console.Tty())
|
||||
}
|
||||
|
||||
func newTestSurveyPrompter(t testing.TB, console *expect.Console) prompter.Prompter {
|
||||
t.Helper()
|
||||
|
||||
t.Setenv("GH_ACCESSIBLE_PROMPTER", "false")
|
||||
return prompter.New("echo", console.Tty(), console.Tty(), console.Tty())
|
||||
}
|
||||
|
||||
// failOnExpectError adds an observer that will fail the test in a standardised way
|
||||
// if any expectation on the command output fails, without requiring an explicit
|
||||
// assertion.
|
||||
|
|
|
|||
|
|
@ -75,9 +75,9 @@ type accessiblePrompter struct {
|
|||
func (p *accessiblePrompter) newForm(groups ...*huh.Group) *huh.Form {
|
||||
return huh.NewForm(groups...).
|
||||
WithTheme(huh.ThemeBase16()).
|
||||
WithAccessible(true)
|
||||
// Commented out because https://github.com/charmbracelet/huh/issues/612
|
||||
// WithProgramOptions(tea.WithOutput(p.stdout), tea.WithInput(p.stdin))
|
||||
WithAccessible(true).
|
||||
WithInput(p.stdin).
|
||||
WithOutput(p.stdout)
|
||||
}
|
||||
|
||||
func (p *accessiblePrompter) Select(prompt, _ string, options []string) (int, error) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue