AlecAivazis / survey

A golang library for building interactive and accessible prompts with full support for windows and posix terminals.
MIT License
4.08k stars 351 forks source link

How to get input from another reader (or *file) instead of os.Stdin? #287

Closed Prnyself closed 4 years ago

Prnyself commented 4 years ago

I want to test my code and inject the input from another file for mocking input in os.Stdin, and I tried to replace os.Stdin with a tempfile or use survey.WithStdio to inject the tempfile, but neither worked. Looks like this buffer turned into all-zero-bytes-slice, so that rr.ReadRune() cannot read rune from stdio.In and just return an EOF error. So how can I get input from another file?

viniciusramosdefaria commented 4 years ago

We are facing the same problem, seems like when trying to read runes something unexpected happened. @Prnyself this problem was solved with a turnaround implementation?

Prnyself commented 4 years ago

We are facing the same problem, seems like when trying to read runes something unexpected happened. @Prnyself this problem was solved with a turnaround implementation?

Still not figure out. sigh

viniciusramosdefaria commented 4 years ago

@Prnyself i figured it out. The following snippet may help you:

func terminalRunner() {
    bufOut := new(bytes.Buffer)
    c, err := expect.NewConsole(expect.WithStdout(bufOut))
    if err != nil {
        fmt.Println(err)
    }
    defer c.Close()

    cmd := exec.Command("rit", "scaffold", "generate", "coffee-go")

    cmd.Stdin = c.Tty()
    cmd.Stdout = c.Tty()

    go func() {
        c.ExpectEOF()
    }()

    testCases := []testCase{
        {input: "test", expectOut: ".*Type your name.*"},
        {input: "es", expectOut: ".*Pick your coffee.*"},
        {input: "", expectOut: ".*Delivery.*"},
    }

    go func() {
        for i := 0; i < len(testCases); i++ {
            for {
                matched, _ := regexp.MatchString(testCases[i].expectOut, bufOut.String())
                if matched {
                    c.SendLine(testCases[i].input)
                    break
                }
                time.Sleep(1 * time.Second)
            }
        }
    }()

    cmd.Start()
    cmd.Wait()

    fmt.Println(bufOut.String())
}
Prnyself commented 4 years ago

cool, that looks similar with input manually. By the way, the package expect indicates go-expect ?

viniciusramosdefaria commented 4 years ago

Yes, it is. Ops, I missed the import section, here it goes:

import (
    "bytes"
    "fmt"
    "github.com/Netflix/go-expect"
    "os/exec"
    "regexp"
    "time"
)
Prnyself commented 4 years ago

Thank you for your help! Let's just leave this open, and see whether the maintainer would raise any native-project features for this.

Prnyself commented 4 years ago

I just found that tips about testing are already in Readme and I missed it. So anyone has the same issue about testing, just try to use go-expect with virtual terminal as mentioned :).