Files
odo/tests/interactive/cmd_init_test.go
Charlie Drage 9c491b16fe Updates odo init output, fixes colorized output for tests. (#5613)
* Update vendoring for coloring to add NO_COLOR

* Updates odo init output / adds logo

<!--
Thank you for opening a PR! Here are some things you need to know before submitting:

1. Please read our developer guideline: https://github.com/redhat-developer/odo/wiki/Dev:-odo-Dev-Guidelines
2. Label this PR accordingly with the '/kind' line
3. Ensure you have written and ran the appropriate tests: https://github.com/redhat-developer/odo/wiki/Dev:-Writing-and-running-tests
4. Read how we approve and LGTM each PR: https://github.com/redhat-developer/odo/wiki/Pull-Requests:-Review-guideline

Documentation:

If you are pushing a change to documentation, please read: https://github.com/redhat-developer/odo/wiki/Documentation:-Contributing
-->

**What type of PR is this:**

<!--
Add one of the following kinds:
/kind bug
/kind cleanup
/kind tests
/kind documentation

Feel free to use other [labels](https://github.com/redhat-developer/odo/labels) as needed. However one of the above labels must be present or the PR will not be reviewed. This instruction is for reviewers as well.
-->

/kind feature

**What does this PR do / why we need it:**

We have the logo appearing in `odo dev` and `odo deploy`

Those 3 commands are the most used with `odo`, and the ones that are
most used. All three should have similar and consistent output that
contains:
  * What the component is doing (first line)
  * What's detected (in odo init, it's the files, in odo deploy and dev
    it's the component name)
  * odo version that is being used / outputted.

```sh
$ odo init
  __
 /  \__     Initializing a new component
 \__/  \    Files: No source code detected, a starter project will be created in the current directory
 /  \__/    odo version: v2.5.0
 \__/

Interactive mode enabled, please answer the following questions:
? Select language: javascript
? Select project type: Next.js
 ✓  Downloading devfile "nodejs-nextjs" from registry "DefaultDevfileRegistry" [450ms]
? Which starter project do you want to use? nodejs-nextjs-starter
? Enter component name: my-nodejs-nextjs-app
 ✓  Downloading starter project "nodejs-nextjs-starter" [516ms]

Your new component 'my-nodejs-nextjs-app' is ready in the current directory.
To start editing your component, use 'odo dev' and open this folder in your favorite IDE.
Changes will be directly reflected on the cluster.
```

**Which issue(s) this PR fixes:**
<!--
Specifying the issue will automatically close it when this PR is merged
-->

N/A

**PR acceptance criteria:**

- [X] Unit test

- [X] Integration test

- [X] Documentation

**How to test changes / Special notes to the reviewer:**

N/A

Signed-off-by: Charlie Drage <charlie@charliedrage.com>

* Update based on reviews

Signed-off-by: Charlie Drage <charlie@charliedrage.com>

* Update based on review

Signed-off-by: Charlie Drage <charlie@charliedrage.com>
2022-04-12 09:41:45 -04:00

255 lines
8.9 KiB
Go

//go:build linux || darwin || dragonfly || solaris || openbsd || netbsd || freebsd
// +build linux darwin dragonfly solaris openbsd netbsd freebsd
package interactive
import (
"fmt"
"log"
"os"
"path/filepath"
"strings"
odolog "github.com/redhat-developer/odo/pkg/log"
"github.com/redhat-developer/odo/pkg/odo/cli/messages"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/redhat-developer/odo/pkg/version"
"github.com/redhat-developer/odo/tests/helper"
)
var _ = Describe("odo init interactive command tests", func() {
var commonVar helper.CommonVar
// This is run before every Spec (It)
var _ = BeforeEach(func() {
commonVar = helper.CommonBeforeEach()
helper.Chdir(commonVar.Context)
// We make EXPLICITLY sure that we are outputting with NO COLOR
// this is because in some cases we are comparing the output with a colorized one
os.Setenv("NO_COLOR", "true")
})
// Clean up after the test
// This is run after every Spec (It)
var _ = AfterEach(func() {
helper.CommonAfterEach(commonVar)
})
It("should not fail when using -v flag", func() {
command := []string{"odo", "init", "-v", "4"}
output, err := helper.RunInteractive(command, nil, func(ctx helper.InteractiveContext) {
helper.ExpectString(ctx, "Select language")
helper.SendLine(ctx, "go")
helper.ExpectString(ctx, "Select project type")
helper.SendLine(ctx, "\n")
helper.ExpectString(ctx, "Which starter project do you want to use")
helper.SendLine(ctx, "\n")
helper.ExpectString(ctx, "Enter component name")
helper.SendLine(ctx, "my-go-app")
helper.ExpectString(ctx, "Your new component 'my-go-app' is ready in the current directory")
})
Expect(err).To(BeNil())
Expect(output).To(ContainSubstring("Your new component 'my-go-app' is ready in the current directory"))
Expect(helper.ListFilesInDir(commonVar.Context)).To(ContainElements("devfile.yaml"))
})
It("should download correct devfile", func() {
command := []string{"odo", "init"}
output, err := helper.RunInteractive(command, nil, func(ctx helper.InteractiveContext) {
helper.ExpectString(ctx, "Select language")
helper.SendLine(ctx, "go")
helper.ExpectString(ctx, "Select project type")
helper.SendLine(ctx, "\n")
helper.ExpectString(ctx, "Which starter project do you want to use")
helper.SendLine(ctx, "\n")
helper.ExpectString(ctx, "Enter component name")
helper.SendLine(ctx, "my-go-app")
helper.ExpectString(ctx, "Your new component 'my-go-app' is ready in the current directory")
})
Expect(err).To(BeNil())
Expect(output).To(ContainSubstring("Your new component 'my-go-app' is ready in the current directory"))
Expect(helper.ListFilesInDir(commonVar.Context)).To(ContainElements("devfile.yaml"))
})
Describe("displaying welcoming messages", func() {
// testFunc is a function that returns a `Tester` function (intended to be used via `helper.RunInteractive`),
// which first expects all messages in `welcomingMsgs` to be read from the console,
// then runs an `additionalTester` and finally expects the asking of a component name
// (based on the `language` specified)
testFunc := func(language string, welcomingMsgs []string, additionalTester helper.Tester) helper.Tester {
return func(ctx helper.InteractiveContext) {
for _, msg := range welcomingMsgs {
helper.ExpectString(ctx, msg)
}
if additionalTester != nil {
additionalTester(ctx)
}
helper.ExpectString(ctx, "Enter component name")
helper.SendLine(ctx, fmt.Sprintf("my-%s-app", language))
helper.ExpectString(ctx,
fmt.Sprintf("Your new component 'my-%s-app' is ready in the current directory", language))
}
}
assertBehavior := func(language string, output string, err error, msgs []string, additionalAsserter func()) {
Expect(err).To(BeNil())
lines, err := helper.ExtractLines(output)
if err != nil {
log.Fatal(err)
}
Expect(len(lines)).To(BeNumerically(">", len(msgs)))
Expect(lines[0:len(msgs)]).To(Equal(msgs))
Expect(lines).To(
ContainElement(fmt.Sprintf("Your new component 'my-%s-app' is ready in the current directory", language)))
Expect(helper.ListFilesInDir(commonVar.Context)).To(ContainElements("devfile.yaml"))
if additionalAsserter != nil {
additionalAsserter()
}
}
testRunner := func(language string, welcomingMsgs []string, tester helper.Tester) (string, error) {
command := []string{"odo", "init"}
return helper.RunInteractive(command,
// Setting verbosity level to 0, because we would be asserting the welcoming message is the first
// message displayed to the end user. So we do not want any potential debug lines to be printed first.
// Using envvars here (and not via the -v flag), because of https://github.com/redhat-developer/odo/issues/5513
[]string{"ODO_LOG_LEVEL=0"},
testFunc(language, welcomingMsgs, tester))
}
When("directory is empty", func() {
BeforeEach(func() {
Expect(helper.ListFilesInDir(commonVar.Context)).To(HaveLen(0))
})
It("should display appropriate welcoming messages", func() {
language := "java"
// The first output is welcoming message / paragraph / banner output
welcomingMsgs := strings.Split(odolog.Stitle(messages.InitializingNewComponent, messages.NoSourceCodeDetected, "odo version: "+version.VERSION), "\n")
output, err := testRunner(language, welcomingMsgs, func(ctx helper.InteractiveContext) {
helper.ExpectString(ctx, "Select language")
helper.SendLine(ctx, language)
helper.ExpectString(ctx, "Select project type")
helper.SendLine(ctx, "\n")
helper.ExpectString(ctx, "Which starter project do you want to use")
helper.SendLine(ctx, "\n")
})
assertBehavior(language, output, err, welcomingMsgs, nil)
})
})
When("directory is not empty", func() {
BeforeEach(func() {
helper.CopyExample(filepath.Join("source", "python"), commonVar.Context)
Expect(helper.ListFilesInDir(commonVar.Context)).To(
SatisfyAll(
HaveLen(2),
ContainElements("requirements.txt", "wsgi.py")))
})
It("should display appropriate welcoming messages", func() {
language := "python"
welcomingMsgs := strings.Split(odolog.Stitle(messages.InitializingNewComponent, messages.SourceCodeDetected, "odo version: "+version.VERSION), "\n")
output, err := testRunner(language, welcomingMsgs, func(ctx helper.InteractiveContext) {
helper.ExpectString(ctx, "Based on the files in the current directory odo detected")
helper.ExpectString(ctx, fmt.Sprintf("Language: %s", language))
helper.ExpectString(ctx, fmt.Sprintf("Project type: %s", language))
helper.ExpectString(ctx,
fmt.Sprintf("The devfile \"%s\" from the registry \"DefaultDevfileRegistry\" will be downloaded.", language))
helper.ExpectString(ctx, "Is this correct")
helper.SendLine(ctx, "\n")
helper.ExpectString(ctx, "Select container for which you want to change configuration")
helper.SendLine(ctx, "\n")
})
assertBehavior(language, output, err, welcomingMsgs, func() {
// Make sure the original source code files are still present
Expect(helper.ListFilesInDir(commonVar.Context)).To(
SatisfyAll(
HaveLen(3),
ContainElements("devfile.yaml", "requirements.txt", "wsgi.py")))
})
})
})
})
It("should start downloading starter project only after all interactive questions have been asked", func() {
output, err := helper.RunInteractive([]string{"odo", "init"}, nil, func(ctx helper.InteractiveContext) {
helper.ExpectString(ctx, "Select language")
helper.SendLine(ctx, "dotnet")
helper.ExpectString(ctx, "Select project type")
helper.SendLine(ctx, "\n")
helper.ExpectString(ctx, "Which starter project do you want to use")
helper.SendLine(ctx, "\n")
helper.ExpectString(ctx, "Enter component name")
helper.SendLine(ctx, "my-dotnet-app")
helper.ExpectString(ctx, "Your new component 'my-dotnet-app' is ready in the current directory")
})
Expect(err).To(BeNil())
lines, err := helper.ExtractLines(output)
Expect(err).To(BeNil())
Expect(len(lines)).To(BeNumerically(">", 2))
Expect(lines[len(lines)-1]).To(Equal("Your new component 'my-dotnet-app' is ready in the current directory"))
componentNameQuestionIdx, ok := helper.FindFirstElementIndexMatchingRegExp(lines, ".*Enter component name:.*")
Expect(ok).To(BeTrue())
starterProjectDownloadActionIdx, found := helper.FindFirstElementIndexMatchingRegExp(lines,
".*Downloading starter project \"([^\\s]+)\" \\[.*")
Expect(found).To(BeTrue())
Expect(starterProjectDownloadActionIdx).To(SatisfyAll(
Not(BeZero()),
// #5495: component name question should be displayed before starter project is actually downloaded
BeNumerically(">", componentNameQuestionIdx),
), "Action 'Downloading starter project' should have been displayed after the last interactive question ('Enter component name')")
Expect(helper.ListFilesInDir(commonVar.Context)).To(ContainElements("devfile.yaml"))
})
})