package generator_test import ( "fmt" "regexp" "strings" "testing" "github.com/mattn/go-colorable" "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" "github.com/onsi/gomega/gbytes" "github.com/nicholas-fedor/shoutrrr/pkg/util/generator" ) func TestGenerator(t *testing.T) { gomega.RegisterFailHandler(ginkgo.Fail) ginkgo.RunSpecs(t, "Generator Suite") } var ( client *generator.UserDialog userOut *gbytes.Buffer userIn *gbytes.Buffer ) func mockTyped(a ...any) { _, _ = fmt.Fprint(userOut, a...) _, _ = fmt.Fprint(userOut, "\n") } func dumpBuffers() { for _, line := range strings.Split(string(userIn.Contents()), "\n") { _, _ = fmt.Fprint(ginkgo.GinkgoWriter, "> ", line, "\n") } for _, line := range strings.Split(string(userOut.Contents()), "\n") { _, _ = fmt.Fprint(ginkgo.GinkgoWriter, "< ", line, "\n") } } var _ = ginkgo.Describe("GeneratorCommon", func() { ginkgo.BeforeEach(func() { userOut = gbytes.NewBuffer() userIn = gbytes.NewBuffer() userInMono := colorable.NewNonColorable(userIn) client = generator.NewUserDialog( userOut, userInMono, map[string]string{"propKey": "propVal"}, ) }) ginkgo.It("reprompt upon invalid answers", func() { defer dumpBuffers() answer := make(chan string) go func() { answer <- client.QueryString("name:", generator.Required, "") }() mockTyped("") mockTyped("Normal Human Name") gomega.Eventually(userIn).Should(gbytes.Say(`name: `)) gomega.Eventually(userIn).Should(gbytes.Say(`field is required`)) gomega.Eventually(userIn).Should(gbytes.Say(`name: `)) gomega.Eventually(answer).Should(gomega.Receive(gomega.Equal("Normal Human Name"))) }) ginkgo.It("should accept any input when validator is nil", func() { defer dumpBuffers() answer := make(chan string) go func() { answer <- client.QueryString("name:", nil, "") }() mockTyped("") gomega.Eventually(answer).Should(gomega.Receive(gomega.BeEmpty())) }) ginkgo.It("should use predefined prop value if key is present", func() { defer dumpBuffers() answer := make(chan string) go func() { answer <- client.QueryString("name:", generator.Required, "propKey") }() gomega.Eventually(answer).Should(gomega.Receive(gomega.Equal("propVal"))) }) ginkgo.Describe("Query", func() { ginkgo.It("should prompt until a valid answer is provided", func() { defer dumpBuffers() answer := make(chan []string) query := "pick foo or bar:" go func() { answer <- client.Query(query, regexp.MustCompile("(foo|bar)"), "") }() mockTyped("") mockTyped("foo") gomega.Eventually(userIn).Should(gbytes.Say(query)) gomega.Eventually(userIn).Should(gbytes.Say(`invalid format`)) gomega.Eventually(userIn).Should(gbytes.Say(query)) gomega.Eventually(answer).Should(gomega.Receive(gomega.ContainElement("foo"))) }) }) ginkgo.Describe("QueryAll", func() { ginkgo.It("should prompt until a valid answer is provided", func() { defer dumpBuffers() answer := make(chan [][]string) query := "pick foo or bar:" go func() { answer <- client.QueryAll(query, regexp.MustCompile(`foo(ba[rz])`), "", -1) }() mockTyped("foobar foobaz") gomega.Eventually(userIn).Should(gbytes.Say(query)) var matches [][]string gomega.Eventually(answer).Should(gomega.Receive(&matches)) gomega.Expect(matches).To(gomega.ContainElement([]string{"foobar", "bar"})) gomega.Expect(matches).To(gomega.ContainElement([]string{"foobaz", "baz"})) }) }) ginkgo.Describe("QueryStringPattern", func() { ginkgo.It("should prompt until a valid answer is provided", func() { defer dumpBuffers() answer := make(chan string) query := "type of bar:" go func() { answer <- client.QueryStringPattern(query, regexp.MustCompile(".*bar"), "") }() mockTyped("foo") mockTyped("foobar") gomega.Eventually(userIn).Should(gbytes.Say(query)) gomega.Eventually(userIn).Should(gbytes.Say(`invalid format`)) gomega.Eventually(userIn).Should(gbytes.Say(query)) gomega.Eventually(answer).Should(gomega.Receive(gomega.Equal("foobar"))) }) }) ginkgo.Describe("QueryInt", func() { ginkgo.It("should prompt until a valid answer is provided", func() { defer dumpBuffers() answer := make(chan int64) query := "number:" go func() { answer <- client.QueryInt(query, "", 64) }() mockTyped("x") mockTyped("0x20") gomega.Eventually(userIn).Should(gbytes.Say(query)) gomega.Eventually(userIn).Should(gbytes.Say(`not a number`)) gomega.Eventually(userIn).Should(gbytes.Say(query)) gomega.Eventually(answer).Should(gomega.Receive(gomega.Equal(int64(32)))) }) }) ginkgo.Describe("QueryBool", func() { ginkgo.It("should prompt until a valid answer is provided", func() { defer dumpBuffers() answer := make(chan bool) query := "cool?" go func() { answer <- client.QueryBool(query, "") }() mockTyped("maybe") mockTyped("y") gomega.Eventually(userIn).Should(gbytes.Say(query)) gomega.Eventually(userIn).Should(gbytes.Say(`answer must be yes or no`)) gomega.Eventually(userIn).Should(gbytes.Say(query)) gomega.Eventually(answer).Should(gomega.Receive(gomega.BeTrue())) }) }) })