Doc Automation: Command Reference: odo init (#6442)

* Pilot work

Signed-off-by: Parthvi Vala <pvala@redhat.com>

* Add more tests

* CompareDocOutput returns strings missing from cmdout and file

* Finish automating docs for odo init command reference

Signed-off-by: Parthvi Vala <pvala@redhat.com>

* Cleanup

Signed-off-by: Parthvi Vala <pvala@redhat.com>

* Add make target for doc automation tests; create a separate folder for doc automation tests; add documentation for helper functions

Signed-off-by: Parthvi Vala <pvala@redhat.com>

* Attempt at fixing validation test

Signed-off-by: Parthvi Vala <pvala@redhat.com>

* Fix validation failures

* Rename ReplaceTimeInString to ReplaceAllTimeInString

Signed-off-by: Parthvi Vala <pvala@redhat.com>

* Replace all time strings with a static value for both cmd and mdx output

Signed-off-by: Parthvi Vala <pvala@redhat.com>

* Add test suite for command reference tests

Signed-off-by: Parthvi Vala <pvala@redhat.com>

* Fix failing test and skip empty directory interactive test; add comment
for tests with specific check

Signed-off-by: Parthvi Vala <pvala@redhat.com>

* Fix make target

Signed-off-by: Parthvi Vala <pvala@redhat.com>

* StripSpinner relies on statement to decide if it should strip the line or let it be

Signed-off-by: Parthvi Vala <pvala@redhat.com>

* Add check for unicode spinner frames just in case it is present; it is flaky

Signed-off-by: Parthvi Vala <pvala@redhat.com>

* Make interactive tests work

* Make it pretty

* Update odo version

Signed-off-by: Parthvi Vala <pvala@redhat.com>
This commit is contained in:
Parthvi Vala
2023-01-06 00:33:26 +05:30
committed by GitHub
parent 9e3d554d0a
commit 7c563b5de3
29 changed files with 540 additions and 140 deletions

View File

@@ -111,7 +111,7 @@ vet:
.PHONY: sec
sec:
go run $(COMMON_GOFLAGS) github.com/securego/gosec/v2/cmd/gosec -severity medium -confidence medium -exclude G304,G204,G107 -quiet ./tests/integration/... ./tests/helper... ./tests/e2escenarios/...
go run $(COMMON_GOFLAGS) github.com/securego/gosec/v2/cmd/gosec -severity medium -confidence medium -exclude G304,G204,G107 -quiet ./tests/integration/... ./tests/helper... ./tests/e2escenarios/... ./tests/documentation/...
go run $(COMMON_GOFLAGS) github.com/securego/gosec/v2/cmd/gosec -severity medium -confidence medium -exclude G304,G204 -quiet ./cmd/... ./pkg/...
.PHONY: clean
@@ -211,4 +211,9 @@ test-integration: test-integration-no-cluster test-integration-cluster
.PHONY: test-e2e
test-e2e:
$(RUN_GINKGO) $(GINKGO_FLAGS) --junit-report="test-e2e.xml" tests/e2escenarios
$(RUN_GINKGO) $(GINKGO_FLAGS) --junit-report="test-e2e.xml" tests/e2escenarios
.PHONY: test-doc-automation
test-doc-automation:
$(RUN_GINKGO) $(GINKGO_FLAGS) --junit-report="test-doc-automation.xml" tests/documentation/...

View File

@@ -1,2 +0,0 @@
` / \__/ odo version: v3.0.0~beta3
\__/`

View File

@@ -0,0 +1,15 @@
```console
$ odo init --devfile nodejs-react --name my-nr-app
__
/ \__ Initializing a new component
\__/ \
/ \__/ odo version: v3.5.0
\__/
✓ Downloading devfile "nodejs-react" [3s]
Your new component 'my-nr-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.
```

View File

@@ -0,0 +1,16 @@
```console
$ odo init --name my-spring-app --devfile java-springboot --devfile-registry DefaultDevfileRegistry --starter springbootproject
__
/ \__ Initializing a new component
\__/ \
/ \__/ odo version: v3.5.0
\__/
✓ Downloading devfile "java-springboot" from registry "DefaultDevfileRegistry" [4s]
✓ Downloading starter project "springbootproject" [874ms]
Your new component 'my-spring-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.
```

View File

@@ -0,0 +1,16 @@
```console
$ odo init --devfile-path https://registry.devfile.io/devfiles/nodejs-angular --name my-nodejs-app --starter nodejs-angular-starter
__
/ \__ Initializing a new component
\__/ \
/ \__/ odo version: v3.5.0
\__/
✓ Downloading devfile from "https://registry.devfile.io/devfiles/nodejs-angular" [1s]
✓ Downloading starter project "nodejs-angular-starter" [958ms]
Your new component 'my-nodejs-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.
```

View File

@@ -0,0 +1,34 @@
```console
$ odo init
__
/ \__ Initializing a new component
\__/ \ Files: Source code detected, a Devfile will be determined based upon source code autodetection
/ \__/ odo version: v3.5.0
\__/
Interactive mode enabled, please answer the following questions:
Based on the files in the current directory odo detected
Language: JavaScript
Project type: Node.js
Application ports: 8080
The devfile "nodejs:2.1.1" from the registry "DefaultDevfileRegistry" will be downloaded.
? Is this correct? Yes
✓ Downloading devfile "nodejs:2.1.1" from registry "DefaultDevfileRegistry" [3s]
↪ Container Configuration "runtime":
OPEN PORTS:
- 8080
- 5858
ENVIRONMENT VARIABLES:
- DEBUG_PORT = 5858
? Select container for which you want to change configuration? NONE - configuration is correct
? Enter component name: node-echo
You can automate this command by executing:
odo init --name node-echo --devfile nodejs --devfile-registry DefaultDevfileRegistry --devfile-version 2.1.1
Your new component 'node-echo' 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.
```

View File

@@ -0,0 +1,32 @@
```console
$ odo init
__
/ \__ Initializing a new component
\__/ \ Files: No source code detected, a starter project will be created in the current directory
/ \__/ odo version: v3.5.0
\__/
Interactive mode enabled, please answer the following questions:
? Select language: Java
? Select project type: Maven Java
✓ Downloading devfile "java-maven" from registry "DefaultDevfileRegistry" [4s]
↪ Container Configuration "tools":
OPEN PORTS:
- 8080
- 5858
ENVIRONMENT VARIABLES:
- DEBUG_PORT = 5858
? Select container for which you want to change configuration? NONE - configuration is correct
? Which starter project do you want to use? springbootproject
? Enter component name: my-java-maven-app
✓ Downloading starter project "springbootproject" [1s]
You can automate this command by executing:
odo init --name my-java-maven-app --devfile java-maven --devfile-registry DefaultDevfileRegistry --starter springbootproject
Your new component 'my-java-maven-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.
```

View File

@@ -0,0 +1,16 @@
```console
$ odo init --devfile go --name my-go-app --devfile-version latest
__
/ \__ Initializing a new component
\__/ \
/ \__/ odo version: v3.5.0
\__/
✓ Downloading devfile "go:latest" [4s]
Your new component 'my-go-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.
To deploy your component to a cluster use "odo deploy".
```

View File

@@ -0,0 +1,7 @@
```console
$ odo registry --devfile nodejs-react
NAME REGISTRY DESCRIPTION VERSIONS
nodejs-react StagingRegistry React is a free and open-source front-en... 2.0.2
nodejs-react DefaultDevfileRegistry React is a free and open-source front-en... 2.0.2
```

View File

@@ -0,0 +1,10 @@
```console
$ odo preference view
[...]
Devfile registries:
NAME URL SECURE
StagingRegistry https://registry.stage.devfile.io No
DefaultDevfileRegistry https://registry.devfile.io No
```

View File

@@ -0,0 +1,16 @@
```console
$ odo init --devfile go --name my-go-app --devfile-version 2.0.0
__
/ \__ Initializing a new component
\__/ \
/ \__/ odo version: v3.5.0
\__/
✓ Downloading devfile "go:2.0.0" [3s]
Your new component 'my-go-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.
To deploy your component to a cluster use "odo deploy".
```

View File

@@ -29,20 +29,10 @@ odo init
<details>
<summary>Example</summary>
```console
$ odo init
? Select language: java
? Select project type: Maven Java (java-maven, registry: DefaultDevfileRegistry)
? Which starter project do you want to use? springbootproject
? Enter component name: my-java-maven-app
✓ Downloading devfile "java-maven" from registry "DefaultDevfileRegistry" [949ms]
✓ Downloading starter project "springbootproject" [430ms]
import EmptyDirOutput from './docs-mdx/init/interactive_mode_empty_directory_output.mdx';
<EmptyDirOutput />
Your new component "my-java-maven-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.
To deploy your component to a cluster use "odo deploy".
```
</details>
#### Directory with sources
@@ -67,40 +57,9 @@ odo init
<details>
<summary>Example</summary>
```console
$ odo init
__
/ \__ Initializing a new component
\__/ \ Files: Source code detected, a Devfile will be determined based upon source code autodetection
/ \__/ odo version: v3.3.0
\__/
Interactive mode enabled, please answer the following questions:
Based on the files in the current directory odo detected
Language: JavaScript
Project type: Node.js
Application ports: 3000
The devfile "nodejs" from the registry "DefaultDevfileRegistry" will be downloaded.
? Is this correct? Yes
✓ Downloading devfile "nodejs" from registry "DefaultDevfileRegistry" [1s]
↪ Container Configuration "runtime":
OPEN PORTS:
- 5858
- 3000
ENVIRONMENT VARIABLES:
- DEBUG_PORT = 5858
? Select container for which you want to change configuration? NONE - configuration is correct
? Enter component name: nodejs
You can automate this command by executing:
odo init --name nodejs --devfile nodejs --devfile-registry DefaultDevfileRegistry
import NonEmptyDirectoryOutput from './docs-mdx/init/interactive_mode_directory_with_sources_output.mdx'
Your new component 'nodejs' 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.
```
<NonEmptyDirectoryOutput />
</details>
### Non-interactive mode
@@ -119,41 +78,23 @@ The required `--name` flag indicates how the component initialized by this comma
#### Fetch Devfile from any registry of the list
In this example, the devfile will be downloaded from the **Staging** registry, which is the first one in the list containing the `nodejs-react` devfile.
In this example, the devfile will be downloaded from the **StagingRegistry** registry, which is the first one in the list containing the `nodejs-react` devfile.
```shell
odo init --name <component-name> --devfile <devfile> [--starter STARTER]
```
<details>
<summary>Example</summary>
```console
$ odo preference view
[...]
<RegistryOutput />
Devfile registries:
NAME URL SECURE
Staging https://registry.stage.devfile.io No
DefaultDevfileRegistry https://registry.devfile.io No
import RegistryListOutput from './docs-mdx/init/registry_list_output.mdx'
$ odo registry --devfile nodejs-react
NAME REGISTRY DESCRIPTION VERSIONS
nodejs-react StagingRegistry React is a free and open-source front-en... 2.0.2
nodejs-react DefaultDevfileRegistry React is a free and open-source front-en... 2.0.2
<RegistryListOutput />
$ odo init --devfile nodejs-react --name my-nr-app
__
/ \__ Initializing a new component
\__/ \
/ \__/ odo version: v3.4.0
\__/
import DevfileFromAnyRegistryOutput from './docs-mdx/init/devfile_from_any_registry_output.mdx'
✓ Downloading devfile "nodejs-react" [3s]
<DevfileFromAnyRegistryOutput />
Your new component 'my-nr-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.
```
</details>
@@ -163,24 +104,14 @@ In this example, the devfile will be downloaded from the **DefaultDevfileRegistr
<details>
<summary>Example</summary>
```console
$ odo preference view
[...]
import RegistryOutput from './docs-mdx/init/registry_output.mdx'
Devfile registries:
NAME URL SECURE
Staging https://registry.stage.devfile.io No
DefaultDevfileRegistry https://registry.devfile.io No
<RegistryOutput />
$ odo init --name my-spring-app --devfile java-springboot --devfile-registry DefaultDevfileRegistry --starter springbootproject
✓ Downloading devfile "java-springboot" from registry "DefaultDevfileRegistry" [980ms]
✓ Downloading starter project "springbootproject" [399ms]
import DevfileFromSpecificRegistryOutput from './docs-mdx/init/devfile_from_specific_registry_output.mdx';
<DevfileFromSpecificRegistryOutput />
Your new component "my-spring-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.
To deploy your component to a cluster use "odo deploy".
```
</details>
@@ -192,16 +123,10 @@ odo init --devfile-path <URL> --name <component-name> [--starter STARTER]
<details>
<summary>Example</summary>
```console
$ odo init --devfile-path https://registry.devfile.io/devfiles/nodejs-angular --name my-nodejs-app --starter nodejs-angular-starter
✓ Downloading devfile from "https://registry.devfile.io/devfiles/nodejs-angular" [415ms]
✓ Downloading starter project "nodejs-angular-starter" [484ms]
import DevfileFromURLOutput from './docs-mdx/init/devfile_from_url_output.mdx';
<DevfileFromURLOutput />
Your new component "my-nodejs-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.
To deploy your component to a cluster use "odo deploy".
```
</details>
#### Fetch Devfile of a specific version
@@ -213,44 +138,21 @@ odo init --devfile <devfile-name> --devfile-version <devfile-version> --name <co
<details>
<summary>Examples</summary>
```console
$ odo init --devfile go --name my-go-app --devfile-version 2.0.0
__
/ \__ Initializing a new component
\__/ \
/ \__/ odo version: v3.4.0
\__/
import VersionedOutput from './docs-mdx/init/versioned_devfile_output.mdx';
✓ Downloading devfile "go:2.0.0" [4s]
Your new component 'my-go-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.
To deploy your component to a cluster use "odo deploy".
```
<VersionedOutput />
</details>
import LatestVersionedOutput from './docs-mdx/init/latest_versioned_devfile_output.mdx';
:::note
Use "latest" as the version name to fetch the latest version of a given Devfile.
<details>
<summary>Example</summary>
```console
$ odo init --devfile go --name my-go-app --devfile-version latest
__
/ \__ Initializing a new component
\__/ \
/ \__/ odo version: v3.4.0
\__/
<LatestVersionedOutput />
✓ Downloading devfile "go:latest" [4s]
Your new component 'my-go-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.
To deploy your component to a cluster use "odo deploy".
```
</details>
:::
:::

View File

@@ -3,7 +3,7 @@ $ odo deploy
__
/ \__ Deploying the application using my-dotnet-app Devfile
\__/ \ Namespace: odo-dev
/ \__/ odo version: v3.4.0
/ \__/ odo version: v3.5.0
\__/
↪ Building & Pushing Container: quay.io/MYUSERNAME/dotnet-odo-example

View File

@@ -3,7 +3,7 @@ $ odo deploy
__
/ \__ Deploying the application using my-go-app Devfile
\__/ \ Namespace: odo-dev
/ \__/ odo version: v3.4.0
/ \__/ odo version: v3.5.0
\__/
↪ Building & Pushing Container: quay.io/MYUSERNAME/go-odo-example

View File

@@ -3,7 +3,7 @@ $ odo deploy
__
/ \__ Deploying the application using my-java-app Devfile
\__/ \ Namespace: odo-dev
/ \__/ odo version: v3.4.0
/ \__/ odo version: v3.5.0
\__/
↪ Building & Pushing Container: quay.io/MYUSERNAME/java-odo-example

View File

@@ -3,7 +3,7 @@ $ odo deploy
__
/ \__ Deploying the application using my-nodejs-app Devfile
\__/ \ Namespace: odo-dev
/ \__/ odo version: v3.4.0
/ \__/ odo version: v3.5.0
\__/
↪ Building & Pushing Container: quay.io/MYUSERNAME/nodejs-odo-example

View File

@@ -3,7 +3,7 @@ $ odo dev
__
/ \__ Developing using the "my-dotnet-app" Devfile
\__/ \ Namespace: odo-dev
/ \__/ odo version: v3.4.0
/ \__/ odo version: v3.5.0
\__/
↪ Deploying to the cluster in developer mode

View File

@@ -3,7 +3,7 @@ $ odo init
__
/ \__ Initializing a new component
\__/ \ Files: Source code detected, a Devfile will be determined based upon source code autodetection
/ \__/ odo version: v3.4.0
/ \__/ odo version: v3.5.0
\__/
Interactive mode enabled, please answer the following questions:

View File

@@ -3,7 +3,7 @@ $ odo dev
__
/ \__ Developing using the "my-go-app" Devfile
\__/ \ Namespace: odo-dev
/ \__/ odo version: v3.4.0
/ \__/ odo version: v3.5.0
\__/
↪ Deploying to the cluster in developer mode

View File

@@ -3,7 +3,7 @@ $ odo init
__
/ \__ Initializing a new component
\__/ \ Files: Source code detected, a Devfile will be determined based upon source code autodetection
/ \__/ odo version: v3.4.0
/ \__/ odo version: v3.5.0
\__/
Interactive mode enabled, please answer the following questions:

View File

@@ -3,7 +3,7 @@ $ odo dev
__
/ \__ Developing using the "my-java-app" Devfile
\__/ \ Namespace: odo-dev
/ \__/ odo version: v3.4.0
/ \__/ odo version: v3.5.0
\__/
↪ Deploying to the cluster in developer mode

View File

@@ -3,7 +3,7 @@ $ odo init
__
/ \__ Initializing a new component
\__/ \ Files: Source code detected, a Devfile will be determined based upon source code autodetection
/ \__/ odo version: v3.4.0
/ \__/ odo version: v3.5.0
\__/
Interactive mode enabled, please answer the following questions:

View File

@@ -3,7 +3,7 @@ $ odo dev
__
/ \__ Developing using the "my-nodejs-app" Devfile
\__/ \ Namespace: odo-dev
/ \__/ odo version: v3.4.0
/ \__/ odo version: v3.5.0
\__/
↪ Deploying to the cluster in developer mode

View File

@@ -3,7 +3,7 @@ $ odo init
__
/ \__ Initializing a new component
\__/ \ Files: Source code detected, a Devfile will be determined based upon source code autodetection
/ \__/ odo version: v3.4.0
/ \__/ odo version: v3.5.0
\__/
Interactive mode enabled, please answer the following questions:

View File

@@ -134,6 +134,13 @@ module.exports = {
breadcrumbs: true,
sidebarCollapsible: true,
lastVersion: 'current',
exclude: [
'**/docs-mdx/**',
'**/_*.{js,jsx,ts,tsx,md,mdx}',
'**/_*/**',
'**/*.test.{js,jsx,ts,tsx}',
'**/__tests__/**'
],
versions: {
current: {
label: 'v3',

View File

@@ -0,0 +1,207 @@
package docautomation
import (
"fmt"
"path/filepath"
"strings"
"github.com/google/go-cmp/cmp"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/redhat-developer/odo/tests/helper"
)
var _ = Describe("doc command reference odo init", Label(helper.LabelNoCluster), func() {
var commonVar helper.CommonVar
var commonPath = filepath.Join("command-reference", "docs-mdx", "init")
var outputStringFormat = "```console\n$ odo %s\n%s```\n"
BeforeEach(func() {
commonVar = helper.CommonBeforeEach()
helper.Chdir(commonVar.Context)
Expect(helper.VerifyFileExists(".odo/env/env.yaml")).To(BeFalse())
})
AfterEach(func() {
helper.CommonAfterEach(commonVar)
})
// interactive tests do not provide the same output every time,
// so we'll skip these tests until we have more coverage and then investigate a better way to test this
Context("Interactive Mode", func() {
It("Empty directory", func() {
args := []string{"odo", "init"}
out, err := helper.RunInteractive(args, []string{"ODO_LOG_LEVEL=0"}, func(ctx helper.InteractiveContext) {
helper.ExpectString(ctx, "Select language")
helper.SendLine(ctx, "Java")
helper.ExpectString(ctx, "Select project type")
helper.SendLine(ctx, "")
helper.ExpectString(ctx, "Select container for which you want to change configuration?")
helper.SendLine(ctx, "")
helper.ExpectString(ctx, "Which starter project do you want to use")
helper.SendLine(ctx, "")
helper.ExpectString(ctx, "Enter component name")
helper.SendLine(ctx, "my-java-maven-app")
helper.ExpectString(ctx, "Changes will be directly reflected on the cluster.")
})
Expect(err).To(BeNil())
got := helper.StripAnsi(out)
got = helper.StripInteractiveQuestion(got)
got = fmt.Sprintf(outputStringFormat, args[1], helper.StripSpinner(got))
want := helper.GetMDXContent(filepath.Join(commonPath, "interactive_mode_empty_directory_output.mdx"))
diff := cmp.Diff(want, got)
Expect(diff).To(BeEmpty())
})
When("the directory is not empty", func() {
BeforeEach(func() {
helper.CopyExample(filepath.Join("source", "nodejs"), commonVar.Context)
})
It("Directory with sources", func() {
args := []string{"odo", "init"}
out, err := helper.RunInteractive(args, []string{"ODO_LOG_LEVEL=0"}, func(ctx helper.InteractiveContext) {
helper.ExpectString(ctx, "Is this correct?")
helper.SendLine(ctx, "")
helper.ExpectString(ctx, "✓ Downloading devfile \"nodejs:2.1.1\" from registry \"DefaultDevfileRegistry\"")
helper.ExpectString(ctx, "Select container for which you want to change configuration?")
helper.SendLine(ctx, "")
helper.ExpectString(ctx, "Enter component name")
helper.SendLine(ctx, "")
helper.ExpectString(ctx, "Changes will be directly reflected on the cluster.")
})
Expect(err).To(BeNil())
got := helper.StripAnsi(out)
got = helper.StripInteractiveQuestion(got)
got = fmt.Sprintf(outputStringFormat, args[1], helper.StripSpinner(got))
want := helper.GetMDXContent(filepath.Join(commonPath, "interactive_mode_directory_with_sources_output.mdx"))
diff := cmp.Diff(want, got)
Expect(diff).To(BeEmpty())
})
})
})
Context("Non Interactive Mode", func() {
It("Fetch Devfile of a specific version", func() {
args := []string{"init", "--devfile", "go", "--name", "my-go-app", "--devfile-version", "2.0.0"}
out := helper.Cmd("odo", args...).ShouldPass().Out()
got := fmt.Sprintf(outputStringFormat, strings.Join(args, " "), helper.StripSpinner(out))
want := helper.GetMDXContent(filepath.Join(commonPath, "versioned_devfile_output.mdx"))
diff := cmp.Diff(want, got)
Expect(diff).To(BeEmpty())
})
It("Fetch Devfile of the latest version", func() {
args := []string{"init", "--devfile", "go", "--name", "my-go-app", "--devfile-version", "latest"}
out := helper.Cmd("odo", args...).ShouldPass().Out()
got := fmt.Sprintf(outputStringFormat, strings.Join(args, " "), helper.StripSpinner(out))
want := helper.GetMDXContent(filepath.Join(commonPath, "latest_versioned_devfile_output.mdx"))
diff := cmp.Diff(want, got)
Expect(diff).To(BeEmpty())
})
It("Fetch Devfile from a URL", func() {
args := []string{"init", "--devfile-path", "https://registry.devfile.io/devfiles/nodejs-angular", "--name", "my-nodejs-app", "--starter", "nodejs-angular-starter"}
out := helper.Cmd("odo", args...).ShouldPass().Out()
got := fmt.Sprintf(outputStringFormat, strings.Join(args, " "), helper.StripSpinner(out))
want := helper.GetMDXContent(filepath.Join(commonPath, "devfile_from_url_output.mdx"))
diff := cmp.Diff(want, got)
Expect(diff).To(BeEmpty())
})
Context("fetching devfile from a registry", func() {
When("setting up the registry", func() {
const (
defaultReg = "DefaultDevfileRegistry"
defaultRegURL = "https://registry.devfile.io"
stagingReg = "StagingRegistry"
stagingRegURL = "https://registry.stage.devfile.io"
)
BeforeEach(func() {
helper.Cmd("odo", "preference", "remove", "registry", defaultReg, "-f").ShouldPass()
helper.Cmd("odo", "preference", "add", "registry", defaultReg, defaultRegURL).ShouldPass()
helper.Cmd("odo", "preference", "add", "registry", stagingReg, stagingRegURL).ShouldPass()
})
AfterEach(func() {
helper.Cmd("odo", "preference", "remove", "registry", stagingReg, "-f").ShouldPass()
helper.SetDefaultDevfileRegistryAsStaging()
})
removePreferenceKeys := func(docString string) string {
return "[...]\n\n" + docString[strings.Index(docString, "Devfile registries"):]
}
checkRegistriesOutput := func() {
args := []string{"preference", "view"}
out := helper.Cmd("odo", args...).ShouldPass().Out()
got := helper.StripAnsi(out)
got = removePreferenceKeys(got)
got = fmt.Sprintf(outputStringFormat, strings.Join(args, " "), helper.StripSpinner(got))
want := helper.GetMDXContent(filepath.Join(commonPath, "registry_output.mdx"))
diff := cmp.Diff(want, got)
Expect(diff).To(BeEmpty())
}
It("Fetch Devfile from a specific registry of the list", func() {
By("checking for required registries", func() {
checkRegistriesOutput()
})
By("checking for the init output", func() {
args := []string{"init", "--name", "my-spring-app", "--devfile", "java-springboot", "--devfile-registry", "DefaultDevfileRegistry", "--starter", "springbootproject"}
out := helper.Cmd("odo", args...).ShouldPass().Out()
got := fmt.Sprintf(outputStringFormat, strings.Join(args, " "), helper.StripSpinner(out))
want := helper.GetMDXContent(filepath.Join(commonPath, "devfile_from_specific_registry_output.mdx"))
diff := cmp.Diff(want, got)
Expect(diff).To(BeEmpty())
})
})
It("Fetch Devfile from any registry of the list", func() {
By("checking for required registries", func() {
checkRegistriesOutput()
})
By("checking for the registry list output", func() {
args := []string{"registry", "--devfile", "nodejs-react"}
out := helper.Cmd("odo", args...).ShouldPass().Out()
got := helper.StripAnsi(out)
got = fmt.Sprintf(outputStringFormat, strings.Join(args, " "), helper.StripSpinner(got))
want := helper.GetMDXContent(filepath.Join(commonPath, "registry_list_output.mdx"))
diff := cmp.Diff(want, got)
Expect(diff).To(BeEmpty())
})
By("checking for the init output", func() {
args := []string{"init", "--devfile", "nodejs-react", "--name", "my-nr-app"}
out := helper.Cmd("odo", args...).ShouldPass().Out()
got := fmt.Sprintf(outputStringFormat, strings.Join(args, " "), helper.StripSpinner(out))
want := helper.GetMDXContent(filepath.Join(commonPath, "devfile_from_any_registry_output.mdx"))
diff := cmp.Diff(want, got)
Expect(diff).To(BeEmpty())
})
})
})
})
It("Fetch Devfile from a URL", func() {
args := []string{"init", "--devfile-path", "https://registry.devfile.io/devfiles/nodejs-angular", "--name", "my-nodejs-app", "--starter", "nodejs-angular-starter"}
out := helper.Cmd("odo", args...).ShouldPass().Out()
got := fmt.Sprintf(outputStringFormat, strings.Join(args, " "), helper.StripSpinner(out))
want := helper.GetMDXContent(filepath.Join(commonPath, "devfile_from_url_output.mdx"))
diff := cmp.Diff(want, got)
Expect(diff).To(BeEmpty())
})
})
})

View File

@@ -0,0 +1,11 @@
package docautomation
import (
"testing"
"github.com/redhat-developer/odo/tests/helper"
)
func TestDocCommandReferenceAutomation(t *testing.T) {
helper.RunTestSpecs(t, "Doc Command Reference Automation Suite")
}

View File

@@ -0,0 +1,108 @@
package helper
import (
"bufio"
"os"
"path/filepath"
"regexp"
"runtime"
"strings"
"unicode"
. "github.com/onsi/gomega"
)
const (
timePatternInOdo = `(\[[0-9smh]+\])` // e.g. [4s], [1m], [3ms]
staticTimeValue = "[1s]"
// Credit: https://github.com/acarl005/stripansi/blob/master/stripansi.go
ansiPattern = "[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))"
unicodeSpinnerFrames = "◓◐◑◒"
)
// ReplaceAllTimeInString replaces the time taken to download a Devfile or a starter project for an odo command with a custom value;
// this function is helpful because the time value is variable and replacing it with the value in mdx content helps in comparing.
func ReplaceAllTimeInString(docString string, timeString string) string {
reg := regexp.MustCompile(timePatternInOdo)
return reg.ReplaceAllString(docString, timeString)
}
// StripSpinner strips the cmd out string of spaces, spinner statements and spinner frames
func StripSpinner(docString string) (returnString string) {
for _, line := range strings.Split(docString, "\n") {
// trim any special character present in the line
line = strings.TrimFunc(line, unicode.IsSpace)
// This check is to avoid spinner statements in the cmd output
// currently it does so for init and dev
// e.g. " • Syncing file changes ..."
if (strings.HasPrefix(line, "• Downloading") || strings.HasPrefix(line, "• Syncing") || strings.HasPrefix(line, "• Building")) && strings.HasSuffix(line, "...") {
continue
}
// for some reason, splitting the docString by \n does not split the spinner frames,
// so we perform a side operation to remove the extra spinner frames that are not present in the final output
// e.g. "◐ Downloading devfile "java-maven" from registry "DefaultDevfileReg... \n ◓ Downloading devfile "java-maven" from registry "DefaultDevfileReg...\n\n ✓ Downloading devfile "java-maven" from registry "DefaultDevfileRegistry" [2s]"
if strings.ContainsAny(line, unicodeSpinnerFrames) {
line = "✓" + strings.SplitAfter(line, "✓")[1]
}
returnString += line + "\n"
}
// replace all instances of time to [1s], this is also done for mdx out
returnString = ReplaceAllTimeInString(returnString, staticTimeValue)
return
}
// GetMDXContent reads the content of MDX files, strips it of extra spaces and returns the string
// it strips the extra space for an easy comparison
func GetMDXContent(filePath string) (mdxContent string) {
// filename of this file
_, filename, _, _ := runtime.Caller(0)
// path to the docs directory
mdxDir := filepath.Join(filepath.Dir(filename), "..", "..", "docs", "website", "docs")
readFile, err := os.Open(filepath.Join(mdxDir, filePath))
defer func(err error) {
if err == nil {
readFile.Close()
}
}(err)
Expect(err).ToNot(HaveOccurred())
fileScanner := bufio.NewScanner(readFile)
fileScanner.Split(bufio.ScanLines)
for fileScanner.Scan() {
line := fileScanner.Text()
line = strings.TrimFunc(line, unicode.IsSpace)
mdxContent += line + "\n"
}
// replace all instances of time to [1s], this is also done for cmd out
mdxContent = ReplaceAllTimeInString(mdxContent, staticTimeValue)
return
}
// StripAnsi strips the cmd out of ansi values used for fomatting(underline, colored line, etc.) the cmd out;
// this function should be called before StripSpinner for better results
// and is essential because mdx content does not support ansi
// The regex used by this function is copied from https://github.com/acarl005/stripansi/
func StripAnsi(docString string) (returnString string) {
reg, err := regexp.Compile(ansiPattern)
Expect(err).To(BeNil())
returnString = reg.ReplaceAllString(docString, "")
return
}
// StripInteractiveQuestion strips the extra output from interactive questions, leaving the final output
// e.g. "? Is this correct? (Y/n) No? Is this correct? No"
func StripInteractiveQuestion(docString string) (returnString string) {
returnString = docString
for _, question := range []string{"? Select language:", "? Select project type:", "? Select container for which you want to change configuration?", "? Is this correct?", "? Enter component name:", "? Which starter project do you want to use?"} {
if strings.Count(returnString, question) > 1 {
returnString = returnString[:strings.Index(returnString, question)] + returnString[strings.LastIndex(returnString, question):]
}
}
return
}

View File

@@ -16,7 +16,7 @@ import (
// InteractiveContext represents the context of an interactive command to be run.
type InteractiveContext struct {
//Command represents the original command ran
// Command represents the original command ran
Command []string
// cp is the internal interface used by the interactive command