"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "pkg/surveyext/editor_test.go" between
gh-cli-1.11.0.tar.gz and gh-cli-1.12.0.tar.gz

About: GitHub CLI is GitHub’s official command line tool.

editor_test.go  (gh-cli-1.11.0):editor_test.go  (gh-cli-1.12.0)
skipping to change at line 17 skipping to change at line 17
"os" "os"
"strings" "strings"
"sync" "sync"
"testing" "testing"
"time" "time"
"github.com/AlecAivazis/survey/v2" "github.com/AlecAivazis/survey/v2"
"github.com/AlecAivazis/survey/v2/terminal" "github.com/AlecAivazis/survey/v2/terminal"
pseudotty "github.com/creack/pty" pseudotty "github.com/creack/pty"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
func Test_GhEditor_Prompt(t *testing.T) { func testLookPath(s string) ([]string, []string, error) {
return []string{os.Args[0], "-test.run=TestHelperProcess", "--", s}, []st
ring{"GH_WANT_HELPER_PROCESS=1"}, nil
}
func TestHelperProcess(t *testing.T) {
if os.Getenv("GH_WANT_HELPER_PROCESS") != "1" {
return
}
if err := func(args []string) error {
switch args[0] {
// "vim" appends a message to the file
case "vim":
f, err := os.OpenFile(args[1], os.O_APPEND|os.O_WRONLY, 0
)
if err != nil {
return err
}
defer f.Close()
_, err = f.WriteString(" - added by vim")
return err
// "nano" truncates the contents of the file
case "nano":
f, err := os.OpenFile(args[1], os.O_TRUNC|os.O_WRONLY, 0)
if err != nil {
return err
}
return f.Close()
default:
return fmt.Errorf("unrecognized arguments: %#v\n", args)
}
}(os.Args[3:]); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
os.Exit(0)
}
func Test_GhEditor_Prompt_skip(t *testing.T) {
pty := newTerminal(t)
e := &GhEditor{ e := &GhEditor{
BlankAllowed: true, BlankAllowed: true,
EditorCommand: "false", EditorCommand: "vim",
Editor: &survey.Editor{ Editor: &survey.Editor{
Message: "Body", Message: "Body",
FileName: "*.md", FileName: "*.md",
Default: "initial value", Default: "initial value",
HideDefault: true, HideDefault: true,
AppendDefault: true, AppendDefault: true,
}, },
lookPath: func(s string) ([]string, []string, error) {
return nil, nil, errors.New("no editor allowed")
},
} }
e.WithStdio(pty.Stdio())
pty, tty, err := pseudotty.Open() // wait until the prompt is rendered and send the Enter key
if errors.Is(err, pseudotty.ErrUnsupported) { go func() {
return pty.WaitForOutput("Body")
} assert.Equal(t, "\x1b[0G\x1b[2K\x1b[0;1;92m? \x1b[0m\x1b[0;1;99mB
require.NoError(t, err) ody \x1b[0m\x1b[0;36m[(e) to launch vim, enter to skip] \x1b[0m", normalizeANSI(
defer pty.Close() pty.Output()))
defer tty.Close() pty.ResetOutput()
assert.NoError(t, pty.SendKey('\n'))
err = pseudotty.Setsize(tty, &pseudotty.Winsize{Cols: 72, Rows: 30}) }()
require.NoError(t, err)
out := teeWriter{File: tty}
e.WithStdio(terminal.Stdio{
In: tty,
Out: &out,
Err: tty,
})
var res string res, err := e.Prompt(defaultPromptConfig())
errc := make(chan error) assert.NoError(t, err)
assert.Equal(t, "initial value", res)
assert.Equal(t, "", normalizeANSI(pty.Output()))
}
func Test_GhEditor_Prompt_editorAppend(t *testing.T) {
pty := newTerminal(t)
e := &GhEditor{
BlankAllowed: true,
EditorCommand: "vim",
Editor: &survey.Editor{
Message: "Body",
FileName: "*.md",
Default: "initial value",
HideDefault: true,
AppendDefault: true,
},
lookPath: testLookPath,
}
e.WithStdio(pty.Stdio())
// wait until the prompt is rendered and send the 'e' key
go func() { go func() {
r, err := e.Prompt(defaultPromptConfig()) pty.WaitForOutput("Body")
if r != nil { assert.Equal(t, "\x1b[0G\x1b[2K\x1b[0;1;92m? \x1b[0m\x1b[0;1;99mB
res = r.(string) ody \x1b[0m\x1b[0;36m[(e) to launch vim, enter to skip] \x1b[0m", normalizeANSI(
} pty.Output()))
errc <- err pty.ResetOutput()
assert.NoError(t, pty.SendKey('e'))
}() }()
time.Sleep(5 * time.Millisecond) res, err := e.Prompt(defaultPromptConfig())
assert.Equal(t, "\x1b[0G\x1b[2K\x1b[0;1;92m? \x1b[0m\x1b[0;1;99mBody \x1b assert.NoError(t, err)
[0m\x1b[0;36m[(e) to launch false, enter to skip] \x1b[0m\x1b[?25l", out.String( assert.Equal(t, "initial value - added by vim", res)
)) assert.Equal(t, "", normalizeANSI(pty.Output()))
fmt.Fprint(pty, "\n") // send Enter key }
func Test_GhEditor_Prompt_editorTruncate(t *testing.T) {
pty := newTerminal(t)
err = <-errc e := &GhEditor{
BlankAllowed: true,
EditorCommand: "nano",
Editor: &survey.Editor{
Message: "Body",
FileName: "*.md",
Default: "initial value",
HideDefault: true,
AppendDefault: true,
},
lookPath: testLookPath,
}
e.WithStdio(pty.Stdio())
// wait until the prompt is rendered and send the 'e' key
go func() {
pty.WaitForOutput("Body")
assert.Equal(t, "\x1b[0G\x1b[2K\x1b[0;1;92m? \x1b[0m\x1b[0;1;99mB
ody \x1b[0m\x1b[0;36m[(e) to launch nano, enter to skip] \x1b[0m", normalizeANSI
(pty.Output()))
pty.ResetOutput()
assert.NoError(t, pty.SendKey('e'))
}()
res, err := e.Prompt(defaultPromptConfig())
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, "initial value", res) assert.Equal(t, "", res)
assert.Equal(t, "\x1b[?25h", out.String()) assert.Equal(t, "", normalizeANSI(pty.Output()))
} }
// survey doesn't expose this // survey doesn't expose this
func defaultPromptConfig() *survey.PromptConfig { func defaultPromptConfig() *survey.PromptConfig {
return &survey.PromptConfig{ return &survey.PromptConfig{
PageSize: 7, PageSize: 7,
HelpInput: "?", HelpInput: "?",
SuggestInput: "tab", SuggestInput: "tab",
Icons: survey.IconSet{ Icons: survey.IconSet{
Error: survey.Icon{ Error: survey.Icon{
skipping to change at line 112 skipping to change at line 190
}, },
}, },
Filter: func(filter string, value string, index int) (include boo l) { Filter: func(filter string, value string, index int) (include boo l) {
filter = strings.ToLower(filter) filter = strings.ToLower(filter)
return strings.Contains(strings.ToLower(value), filter) return strings.Contains(strings.ToLower(value), filter)
}, },
KeepFilter: false, KeepFilter: false,
} }
} }
type testTerminal struct {
pty *os.File
tty *os.File
stdout *teeWriter
stderr *teeWriter
}
func newTerminal(t *testing.T) *testTerminal {
t.Helper()
pty, tty, err := pseudotty.Open()
if errors.Is(err, pseudotty.ErrUnsupported) {
t.SkipNow()
return nil
}
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
pty.Close()
tty.Close()
})
if err := pseudotty.Setsize(tty, &pseudotty.Winsize{Cols: 72, Rows: 30});
err != nil {
t.Fatal(err)
}
return &testTerminal{pty: pty, tty: tty}
}
func (t *testTerminal) SendKey(c rune) error {
_, err := t.pty.WriteString(string(c))
return err
}
func (t *testTerminal) WaitForOutput(s string) {
for {
time.Sleep(time.Millisecond)
if strings.Contains(t.stdout.String(), s) {
return
}
}
}
func (t *testTerminal) Output() string {
return t.stdout.String()
}
func (t *testTerminal) ResetOutput() {
t.stdout.Reset()
}
func (t *testTerminal) Stdio() terminal.Stdio {
t.stdout = &teeWriter{File: t.tty}
t.stderr = t.stdout
return terminal.Stdio{
In: t.tty,
Out: t.stdout,
Err: t.stderr,
}
}
// teeWriter is a writer that duplicates all writes to a file into a buffer // teeWriter is a writer that duplicates all writes to a file into a buffer
type teeWriter struct { type teeWriter struct {
*os.File *os.File
buf bytes.Buffer buf bytes.Buffer
mu sync.Mutex mu sync.Mutex
} }
func (f *teeWriter) Write(p []byte) (n int, err error) { func (f *teeWriter) Write(p []byte) (n int, err error) {
f.mu.Lock() f.mu.Lock()
defer f.mu.Unlock() defer f.mu.Unlock()
_, _ = f.buf.Write(p) _, _ = f.buf.Write(p)
return f.File.Write(p) return f.File.Write(p)
} }
func (f *teeWriter) String() string { func (f *teeWriter) String() string {
f.mu.Lock() f.mu.Lock()
s := f.buf.String() s := f.buf.String()
f.buf.Reset()
f.mu.Unlock() f.mu.Unlock()
return s return s
} }
func (f *teeWriter) Reset() {
f.mu.Lock()
f.buf.Reset()
f.mu.Unlock()
}
// strips some ANSI escape sequences that we do not want tests to be concerned w
ith
func normalizeANSI(t string) string {
t = strings.ReplaceAll(t, "\x1b[?25h", "") // strip sequence that shows c
ursor
t = strings.ReplaceAll(t, "\x1b[?25l", "") // strip sequence that hides c
ursor
return t
}
 End of changes. 15 change blocks. 
36 lines changed or deleted 182 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)