1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2024-12-12 10:55:20 +02:00

feat(gauge): migrate gaugeExecuteTests to go implementation (#2775)

* gaugeExecuteTests converted to golang

* rewrited gaugeExecuteTests to cross-platform implementation. Now gauge uses npm

* regenerated

* groovy file import fix

Co-authored-by: Oliver Nocon <33484802+OliverNocon@users.noreply.github.com>
This commit is contained in:
Mikalai Dzemidzenka 2021-06-01 14:15:10 +03:00 committed by GitHub
parent 3b2e7dc53d
commit c38d231820
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 997 additions and 323 deletions

118
cmd/gaugeExecuteTests.go Normal file
View File

@ -0,0 +1,118 @@
package cmd
import (
"fmt"
"io"
"os"
"path/filepath"
"strings"
"github.com/SAP/jenkins-library/pkg/command"
"github.com/SAP/jenkins-library/pkg/log"
"github.com/SAP/jenkins-library/pkg/piperutils"
"github.com/SAP/jenkins-library/pkg/telemetry"
"github.com/pkg/errors"
)
var ErrorGaugeInstall error = errors.New("error installing gauge")
var ErrorGaugeRunnerInstall error = errors.New("error installing runner")
var ErrorGaugeRun error = errors.New("error running gauge")
type gaugeExecuteTestsUtils interface {
FileExists(filename string) (bool, error)
MkdirAll(path string, perm os.FileMode) error
SetEnv([]string)
RunExecutable(executable string, params ...string) error
Stdout(io.Writer)
Getenv(key string) string
}
type gaugeExecuteTestsUtilsBundle struct {
*command.Command
*piperutils.Files
}
func newGaugeExecuteTestsUtils() gaugeExecuteTestsUtils {
utils := gaugeExecuteTestsUtilsBundle{
Command: &command.Command{},
Files: &piperutils.Files{},
}
utils.Stdout(log.Writer())
utils.Stderr(log.Writer())
return &utils
}
func gaugeExecuteTests(config gaugeExecuteTestsOptions, telemetryData *telemetry.CustomData, influx *gaugeExecuteTestsInflux) {
utils := newGaugeExecuteTestsUtils()
influx.step_data.fields.gauge = false
err := runGaugeExecuteTests(&config, telemetryData, utils)
if err != nil {
log.Entry().WithError(err).Fatal("step execution failed")
}
influx.step_data.fields.gauge = true
}
func runGaugeExecuteTests(config *gaugeExecuteTestsOptions, telemetryData *telemetry.CustomData, utils gaugeExecuteTestsUtils) error {
if config.InstallCommand != "" {
err := installGauge(config.InstallCommand, utils)
if err != nil {
return err
}
}
if config.LanguageRunner != "" {
err := installLanguageRunner(config.LanguageRunner, utils)
if err != nil {
return err
}
}
err := runGauge(config, utils)
if err != nil {
return fmt.Errorf("failed to run gauge: %w", err)
}
return nil
}
func installGauge(gaugeInstallCommand string, utils gaugeExecuteTestsUtils) error {
installGaugeTokens := strings.Split(gaugeInstallCommand, " ")
installGaugeTokens = append(installGaugeTokens, "--prefix=~/.npm-global")
err := utils.RunExecutable(installGaugeTokens[0], installGaugeTokens[1:]...)
if err != nil {
log.SetErrorCategory(log.ErrorConfiguration)
return errors.Wrap(ErrorGaugeInstall, err.Error())
}
return nil
}
func installLanguageRunner(languageRunner string, utils gaugeExecuteTestsUtils) error {
installParams := []string{"install", languageRunner}
gaugePath := filepath.Join(utils.Getenv("HOME"), "/.npm-global/bin/gauge")
err := utils.RunExecutable(gaugePath, installParams...)
if err != nil {
log.SetErrorCategory(log.ErrorConfiguration)
return errors.Wrap(ErrorGaugeRunnerInstall, err.Error())
}
return nil
}
func runGauge(config *gaugeExecuteTestsOptions, utils gaugeExecuteTestsUtils) error {
runCommandTokens := strings.Split(config.RunCommand, " ")
if config.TestOptions != "" {
runCommandTokens = append(runCommandTokens, strings.Split(config.TestOptions, " ")...)
}
gaugePath := filepath.Join(utils.Getenv("HOME"), "/.npm-global/bin/gauge")
err := utils.RunExecutable(gaugePath, runCommandTokens...)
if err != nil {
return errors.Wrap(ErrorGaugeRun, err.Error())
}
return nil
}
func (utils gaugeExecuteTestsUtilsBundle) Getenv(key string) string {
return os.Getenv(key)
}

View File

@ -0,0 +1,221 @@
// Code generated by piper's step-generator. DO NOT EDIT.
package cmd
import (
"fmt"
"os"
"path/filepath"
"time"
"github.com/SAP/jenkins-library/pkg/config"
"github.com/SAP/jenkins-library/pkg/log"
"github.com/SAP/jenkins-library/pkg/piperenv"
"github.com/SAP/jenkins-library/pkg/splunk"
"github.com/SAP/jenkins-library/pkg/telemetry"
"github.com/spf13/cobra"
)
type gaugeExecuteTestsOptions struct {
InstallCommand string `json:"installCommand,omitempty"`
LanguageRunner string `json:"languageRunner,omitempty"`
RunCommand string `json:"runCommand,omitempty"`
TestOptions string `json:"testOptions,omitempty"`
}
type gaugeExecuteTestsInflux struct {
step_data struct {
fields struct {
gauge bool
}
tags struct {
}
}
}
func (i *gaugeExecuteTestsInflux) persist(path, resourceName string) {
measurementContent := []struct {
measurement string
valType string
name string
value interface{}
}{
{valType: config.InfluxField, measurement: "step_data", name: "gauge", value: i.step_data.fields.gauge},
}
errCount := 0
for _, metric := range measurementContent {
err := piperenv.SetResourceParameter(path, resourceName, filepath.Join(metric.measurement, fmt.Sprintf("%vs", metric.valType), metric.name), metric.value)
if err != nil {
log.Entry().WithError(err).Error("Error persisting influx environment.")
errCount++
}
}
if errCount > 0 {
log.Entry().Fatal("failed to persist Influx environment")
}
}
// GaugeExecuteTestsCommand Installs gauge and executes specified gauge tests.
func GaugeExecuteTestsCommand() *cobra.Command {
const STEP_NAME = "gaugeExecuteTests"
metadata := gaugeExecuteTestsMetadata()
var stepConfig gaugeExecuteTestsOptions
var startTime time.Time
var influx gaugeExecuteTestsInflux
var logCollector *log.CollectorHook
var createGaugeExecuteTestsCmd = &cobra.Command{
Use: STEP_NAME,
Short: "Installs gauge and executes specified gauge tests.",
Long: `In this step Gauge ([getgauge.io](https://getgauge.io)) acceptance tests are executed. Using Gauge it will be possible to have a three-tier test layout:
Acceptance Criteria
Test implemenation layer
Application driver layer
This layout is propagated by Jez Humble and Dave Farley in their book "Continuous Delivery" as a way to create maintainable acceptance test suites (see "Continuous Delivery", p. 190ff).
Using Gauge it is possible to write test specifications in [Markdown syntax](http://daringfireball.net/projects/markdown/syntax) and therefore allow e.g. product owners to write the relevant acceptance test specifications. At the same time it allows the developer to implement the steps described in the specification in her development environment.
You can use the [sample projects](https://github.com/getgauge/gauge-mvn-archetypes) of Gauge.`,
PreRunE: func(cmd *cobra.Command, _ []string) error {
startTime = time.Now()
log.SetStepName(STEP_NAME)
log.SetVerbose(GeneralConfig.Verbose)
path, _ := os.Getwd()
fatalHook := &log.FatalHook{CorrelationID: GeneralConfig.CorrelationID, Path: path}
log.RegisterHook(fatalHook)
err := PrepareConfig(cmd, &metadata, STEP_NAME, &stepConfig, config.OpenPiperFile)
if err != nil {
log.SetErrorCategory(log.ErrorConfiguration)
return err
}
if len(GeneralConfig.HookConfig.SentryConfig.Dsn) > 0 {
sentryHook := log.NewSentryHook(GeneralConfig.HookConfig.SentryConfig.Dsn, GeneralConfig.CorrelationID)
log.RegisterHook(&sentryHook)
}
if len(GeneralConfig.HookConfig.SplunkConfig.Dsn) > 0 {
logCollector = &log.CollectorHook{CorrelationID: GeneralConfig.CorrelationID}
log.RegisterHook(logCollector)
}
return nil
},
Run: func(_ *cobra.Command, _ []string) {
telemetryData := telemetry.CustomData{}
telemetryData.ErrorCode = "1"
handler := func() {
config.RemoveVaultSecretFiles()
influx.persist(GeneralConfig.EnvRootPath, "influx")
telemetryData.Duration = fmt.Sprintf("%v", time.Since(startTime).Milliseconds())
telemetryData.ErrorCategory = log.GetErrorCategory().String()
telemetry.Send(&telemetryData)
if len(GeneralConfig.HookConfig.SplunkConfig.Dsn) > 0 {
splunk.Send(&telemetryData, logCollector)
}
}
log.DeferExitHandler(handler)
defer handler()
telemetry.Initialize(GeneralConfig.NoTelemetry, STEP_NAME)
if len(GeneralConfig.HookConfig.SplunkConfig.Dsn) > 0 {
splunk.Initialize(GeneralConfig.CorrelationID,
GeneralConfig.HookConfig.SplunkConfig.Dsn,
GeneralConfig.HookConfig.SplunkConfig.Token,
GeneralConfig.HookConfig.SplunkConfig.Index,
GeneralConfig.HookConfig.SplunkConfig.SendLogs)
}
gaugeExecuteTests(stepConfig, &telemetryData, &influx)
telemetryData.ErrorCode = "0"
log.Entry().Info("SUCCESS")
},
}
addGaugeExecuteTestsFlags(createGaugeExecuteTestsCmd, &stepConfig)
return createGaugeExecuteTestsCmd
}
func addGaugeExecuteTestsFlags(cmd *cobra.Command, stepConfig *gaugeExecuteTestsOptions) {
cmd.Flags().StringVar(&stepConfig.InstallCommand, "installCommand", os.Getenv("PIPER_installCommand"), "Defines the command for installing Gauge. Gauge should be installed using npm. Example: npm install -g @getgauge/cli@1.2.1")
cmd.Flags().StringVar(&stepConfig.LanguageRunner, "languageRunner", os.Getenv("PIPER_languageRunner"), "Defines the Gauge language runner to be used. Example: java")
cmd.Flags().StringVar(&stepConfig.RunCommand, "runCommand", os.Getenv("PIPER_runCommand"), "Defines the command which is used for executing Gauge. Example: run -s -p specs/")
cmd.Flags().StringVar(&stepConfig.TestOptions, "testOptions", os.Getenv("PIPER_testOptions"), "Allows to set specific options for the Gauge execution. Details can be found for example [in the Gauge Maven plugin documentation](https://github.com/getgauge-contrib/gauge-maven-plugin#executing-specs)")
cmd.MarkFlagRequired("runCommand")
}
// retrieve step metadata
func gaugeExecuteTestsMetadata() config.StepData {
var theMetaData = config.StepData{
Metadata: config.StepMetadata{
Name: "gaugeExecuteTests",
Aliases: []config.Alias{},
Description: "Installs gauge and executes specified gauge tests.",
},
Spec: config.StepSpec{
Inputs: config.StepInputs{
Resources: []config.StepResources{
{Name: "buildDescriptor", Type: "stash"},
{Name: "tests", Type: "stash"},
},
Parameters: []config.StepParameters{
{
Name: "installCommand",
ResourceRef: []config.ResourceReference{},
Scope: []string{"STEPS", "STAGES", "PARAMETERS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "languageRunner",
ResourceRef: []config.ResourceReference{},
Scope: []string{"STEPS", "STAGES", "PARAMETERS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "runCommand",
ResourceRef: []config.ResourceReference{},
Scope: []string{"STEPS", "STAGES", "PARAMETERS"},
Type: "string",
Mandatory: true,
Aliases: []config.Alias{},
},
{
Name: "testOptions",
ResourceRef: []config.ResourceReference{},
Scope: []string{"STEPS", "STAGES", "PARAMETERS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
},
},
},
Containers: []config.Container{
{Name: "gauge", Image: "node:lts-stretch", EnvVars: []config.EnvVar{{Name: "no_proxy", Value: "localhost,selenium,$no_proxy"}, {Name: "NO_PROXY", Value: "localhost,selenium,$NO_PROXY"}}, WorkingDir: "/home/node"},
},
Sidecars: []config.Container{
{Name: "selenium", Image: "selenium/standalone-chrome", EnvVars: []config.EnvVar{{Name: "NO_PROXY", Value: "localhost,selenium,$NO_PROXY"}, {Name: "no_proxy", Value: "localhost,selenium,$no_proxy"}}},
},
Outputs: config.StepOutputs{
Resources: []config.StepResources{
{
Name: "influx",
Type: "influx",
Parameters: []map[string]interface{}{
{"Name": "step_data"}, {"fields": []map[string]string{{"name": "gauge"}}},
},
},
},
},
},
}
return theMetaData
}

View File

@ -0,0 +1,17 @@
package cmd
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestGaugeExecuteTestsCommand(t *testing.T) {
t.Parallel()
testCmd := GaugeExecuteTestsCommand()
// only high level testing performed - details are tested in step generation procedure
assert.Equal(t, "gaugeExecuteTests", testCmd.Use, "command name incorrect")
}

View File

@ -0,0 +1,112 @@
package cmd
import (
"errors"
"testing"
"github.com/SAP/jenkins-library/pkg/mock"
"github.com/stretchr/testify/assert"
)
type gaugeExecuteTestsMockUtils struct {
*mock.ExecMockRunner
*mock.FilesMock
}
func (utils gaugeExecuteTestsMockUtils) Getenv(key string) string {
if key == "HOME" {
return "/home/node"
}
return ""
}
func TestRunGaugeExecuteTests(t *testing.T) {
t.Parallel()
allFineConfig := gaugeExecuteTestsOptions{
InstallCommand: "npm install -g @getgauge/cli",
LanguageRunner: "java",
RunCommand: "run",
TestOptions: "specs",
}
gaugeBin := "home/node/.npm-global/bin/gauge"
t.Run("success case", func(t *testing.T) {
t.Parallel()
mockUtils := gaugeExecuteTestsMockUtils{
ExecMockRunner: &mock.ExecMockRunner{},
FilesMock: &mock.FilesMock{},
}
err := runGaugeExecuteTests(&allFineConfig, nil, &mockUtils)
assert.NoError(t, err)
assert.Equal(t, mockUtils.ExecMockRunner.Calls[0].Exec, "npm")
assert.Equal(t, mockUtils.ExecMockRunner.Calls[0].Params, []string{"install", "-g", "@getgauge/cli", "--prefix=~/.npm-global"})
assert.Equal(t, mockUtils.ExecMockRunner.Calls[1].Exec, "/home/node/.npm-global/bin/gauge")
assert.Equal(t, mockUtils.ExecMockRunner.Calls[1].Params, []string{"install", "java"})
assert.Equal(t, mockUtils.ExecMockRunner.Calls[2].Exec, "/home/node/.npm-global/bin/gauge")
assert.Equal(t, mockUtils.ExecMockRunner.Calls[2].Params, []string{"run", "specs"})
})
t.Run("fail on installation", func(t *testing.T) {
t.Parallel()
badInstallConfig := allFineConfig
badInstallConfig.InstallCommand = "npm install -g @wronggauge/cli"
mockUtils := gaugeExecuteTestsMockUtils{
ExecMockRunner: &mock.ExecMockRunner{ShouldFailOnCommand: map[string]error{"npm install -g @wronggauge/cli": errors.New("cannot find module")}},
FilesMock: &mock.FilesMock{},
}
err := runGaugeExecuteTests(&badInstallConfig, nil, &mockUtils)
assert.True(t, errors.Is(err, ErrorGaugeInstall))
assert.Equal(t, len(mockUtils.ExecMockRunner.Calls), 1)
assert.Equal(t, mockUtils.ExecMockRunner.Calls[0].Exec, "npm")
assert.Equal(t, mockUtils.ExecMockRunner.Calls[0].Params, []string{"install", "-g", "@wronggauge/cli", "--prefix=~/.npm-global"})
})
t.Run("fail on installing language runner", func(t *testing.T) {
t.Parallel()
badInstallConfig := allFineConfig
badInstallConfig.LanguageRunner = "wrong"
mockUtils := gaugeExecuteTestsMockUtils{
ExecMockRunner: &mock.ExecMockRunner{ShouldFailOnCommand: map[string]error{gaugeBin + " install wrong": errors.New("error installing runner")}},
FilesMock: &mock.FilesMock{},
}
err := runGaugeExecuteTests(&badInstallConfig, nil, &mockUtils)
assert.True(t, errors.Is(err, ErrorGaugeRunnerInstall))
assert.Equal(t, len(mockUtils.ExecMockRunner.Calls), 2)
assert.Equal(t, mockUtils.ExecMockRunner.Calls[0].Exec, "npm")
assert.Equal(t, mockUtils.ExecMockRunner.Calls[0].Params, []string{"install", "-g", "@getgauge/cli", "--prefix=~/.npm-global"})
assert.Equal(t, mockUtils.ExecMockRunner.Calls[1].Exec, "/home/node/.npm-global/bin/gauge")
assert.Equal(t, mockUtils.ExecMockRunner.Calls[1].Params, []string{"install", "wrong"})
})
t.Run("fail on gauge run", func(t *testing.T) {
t.Parallel()
mockUtils := gaugeExecuteTestsMockUtils{
ExecMockRunner: &mock.ExecMockRunner{ShouldFailOnCommand: map[string]error{gaugeBin + " run specs": errors.New("error running gauge")}},
FilesMock: &mock.FilesMock{},
}
err := runGaugeExecuteTests(&allFineConfig, nil, &mockUtils)
assert.True(t, errors.Is(err, ErrorGaugeRun))
assert.Equal(t, len(mockUtils.ExecMockRunner.Calls), 3)
assert.Equal(t, mockUtils.ExecMockRunner.Calls[0].Exec, "npm")
assert.Equal(t, mockUtils.ExecMockRunner.Calls[0].Params, []string{"install", "-g", "@getgauge/cli", "--prefix=~/.npm-global"})
assert.Equal(t, mockUtils.ExecMockRunner.Calls[1].Exec, "/home/node/.npm-global/bin/gauge")
assert.Equal(t, mockUtils.ExecMockRunner.Calls[1].Params, []string{"install", "java"})
assert.Equal(t, mockUtils.ExecMockRunner.Calls[2].Exec, "/home/node/.npm-global/bin/gauge")
assert.Equal(t, mockUtils.ExecMockRunner.Calls[2].Params, []string{"run", "specs"})
})
}

View File

@ -33,6 +33,7 @@ func GetAllStepMetadata() map[string]config.StepData {
"containerExecuteStructureTests": containerExecuteStructureTestsMetadata(),
"detectExecuteScan": detectExecuteScanMetadata(),
"fortifyExecuteScan": fortifyExecuteScanMetadata(),
"gaugeExecuteTests": gaugeExecuteTestsMetadata(),
"gctsCloneRepository": gctsCloneRepositoryMetadata(),
"gctsCreateRepository": gctsCreateRepositoryMetadata(),
"gctsDeploy": gctsDeployMetadata(),

View File

@ -147,6 +147,7 @@ func Execute() {
rootCmd.AddCommand(IntegrationArtifactUploadCommand())
rootCmd.AddCommand(TerraformExecuteCommand())
rootCmd.AddCommand(ContainerExecuteStructureTestsCommand())
rootCmd.AddCommand(GaugeExecuteTestsCommand())
rootCmd.AddCommand(BatsExecuteTestsCommand())
rootCmd.AddCommand(PipelineCreateScanSummaryCommand())
rootCmd.AddCommand(TransportRequestDocIDFromGitCommand())

View File

@ -0,0 +1,81 @@
// +build integration
// can be execute with go test -tags=integration ./integration/...
package main
import (
"context"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/testcontainers/testcontainers-go"
)
const (
installCommand string = "npm install -g @getgauge/cli --prefix=~/.npm-global --unsafe-perm" //option --unsafe-perm need to install gauge in docker container. See this issue: https://github.com/getgauge/gauge/issues/1470
)
func runTest(t *testing.T, languageRunner string) {
ctx := context.Background()
pwd, err := os.Getwd()
assert.NoError(t, err, "Getting current working directory failed.")
pwd = filepath.Dir(pwd)
// using custom createTmpDir function to avoid issues with symlinks on Docker for Mac
tempDir, err := createTmpDir("")
defer os.RemoveAll(tempDir) // clean up
assert.NoError(t, err, "Error when creating temp dir")
err = copyDir(filepath.Join(pwd, "integration", "testdata", "TestGaugeIntegration", "gauge-"+languageRunner), tempDir)
if err != nil {
t.Fatal("Failed to copy test project.")
}
//workaround to use test script util it is possible to set workdir for Exec call
testScript := fmt.Sprintf(`#!/bin/sh
cd /test
/piperbin/piper gaugeExecuteTests --installCommand="%v" --languageRunner=%v --runCommand="run" >test-log.txt 2>&1
`, installCommand, languageRunner)
ioutil.WriteFile(filepath.Join(tempDir, "runPiper.sh"), []byte(testScript), 0700)
reqNode := testcontainers.ContainerRequest{
Image: "getgauge/gocd-jdk-mvn-node",
Cmd: []string{"tail", "-f"},
BindMounts: map[string]string{
pwd: "/piperbin",
tempDir: "/test",
},
}
nodeContainer, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
ContainerRequest: reqNode,
Started: true,
})
code, err := nodeContainer.Exec(ctx, []string{"sh", "/test/runPiper.sh"})
assert.NoError(t, err)
assert.Equal(t, 0, code)
content, err := ioutil.ReadFile(filepath.Join(tempDir, "/test-log.txt"))
if err != nil {
t.Fatal("Could not read test-log.txt.", err)
}
output := string(content)
assert.Contains(t, output, "info gaugeExecuteTests - Scenarios: 2 executed 2 passed 0 failed 0 skipped")
assert.Contains(t, output, "info gaugeExecuteTests - SUCCESS")
}
func TestGaugeJava(t *testing.T) {
t.Parallel()
runTest(t, "java")
}
func TestGaugeJS(t *testing.T) {
t.Parallel()
runTest(t, "js")
}

View File

@ -0,0 +1,7 @@
logs
Dockerfile
reports
.gauge
README.md
gauge_bin
.gitignore

View File

@ -0,0 +1,11 @@
# Gauge - metadata dir
.gauge
# Gauge - log files dir
logs
# Gauge - java class output directory
gauge_bin
# Gauge - reports generated by reporting plugins
reports

View File

@ -0,0 +1,24 @@
# Building the image
# docker build -t gauge-java .
# Running the image
# docker run --rm -it -v ${PWD}/reports:/gauge/reports gauge-java
# This image uses the official openjdk base image.
FROM openjdk
# Install gauge
RUN microdnf install -y unzip \
&& curl -Ssl https://downloads.gauge.org/stable | sh
# Set working directory
WORKDIR /gauge
# Copy the local source folder
COPY . .
# Install gauge plugins
RUN gauge install \
&& gauge install screenshot
CMD ["gauge", "run", "specs"]

View File

@ -0,0 +1,28 @@
# default.properties
# properties set here will be available to the test execution as environment variables
# sample_key = sample_value
# The path to the gauge reports directory. Should be either relative to the project directory or an absolute path
gauge_reports_dir = reports
# Set as false if gauge reports should not be overwritten on each execution. A new time-stamped directory will be created on each execution.
overwrite_reports = true
# Set to false to disable screenshots on failure in reports.
screenshot_on_failure = true
# The path to the gauge logs directory. Should be either relative to the project directory or an absolute path
logs_directory = logs
# Set to true to use multithreading for parallel execution
enable_multithreading = false
# The path the gauge specifications directory. Takes a comma separated list of specification files/directories.
gauge_specs_dir = specs
# The default delimiter used read csv files.
csv_delimiter = ,
# Allows steps to be written in multiline
allow_multiline_step = false

View File

@ -0,0 +1,22 @@
# Specify an alternate Java home if you want to use a custom version
gauge_java_home =
# IntelliJ and Eclipse out directory will be usually autodetected
# Use the below property if you need to override the build path
gauge_custom_build_path =
# specify the directory where additional libs are kept
# you can specify multiple directory names separated with a comma (,)
gauge_additional_libs = libs/*
# JVM arguments passed to java while launching. Enter multiple values separated by comma (,) eg. Xmx1024m, Xms128m
gauge_jvm_args =
# specify the directory containing java files to be compiled
# you can specify multiple directory names separated with a comma (,)
gauge_custom_compile_dir =
# specify the level at which the objects should be cleared
# Possible values are suite, spec and scenario. Default value is scenario.
gauge_clear_state_level = scenario

View File

@ -0,0 +1,6 @@
{
"Language": "java",
"Plugins": [
"html-report"
]
}

View File

@ -0,0 +1,32 @@
# Specification Heading
This is an executable specification file. This file follows markdown syntax.
Every heading in this file denotes a scenario. Every bulleted point denotes a step.
To execute this specification, run
gauge run specs
* Vowels in English language are "aeiou".
## Vowel counts in single word
tags: single word
* The word "gauge" has "3" vowels.
## Vowel counts in multiple word
This is the second scenario in this specification
Here's a step that takes a table
* Almost all words have vowels
|Word |Vowel Count|
|------|-----------|
|Gauge |3 |
|Mingle|2 |
|Snap |1 |
|GoCD |1 |
|Rhythm|0 |

View File

@ -0,0 +1,47 @@
import com.thoughtworks.gauge.Step;
import com.thoughtworks.gauge.Table;
import com.thoughtworks.gauge.TableRow;
import java.util.HashSet;
import static org.assertj.core.api.Assertions.assertThat;
public class StepImplementation {
private HashSet<Character> vowels;
@Step("Vowels in English language are <vowelString>.")
public void setLanguageVowels(String vowelString) {
vowels = new HashSet<>();
for (char ch : vowelString.toCharArray()) {
vowels.add(ch);
}
}
@Step("The word <word> has <expectedCount> vowels.")
public void verifyVowelsCountInWord(String word, int expectedCount) {
int actualCount = countVowels(word);
assertThat(expectedCount).isEqualTo(actualCount);
}
@Step("Almost all words have vowels <wordsTable>")
public void verifyVowelsCountInMultipleWords(Table wordsTable) {
for (TableRow row : wordsTable.getTableRows()) {
String word = row.getCell("Word");
int expectedCount = Integer.parseInt(row.getCell("Vowel Count"));
int actualCount = countVowels(word);
assertThat(expectedCount).isEqualTo(actualCount);
}
}
private int countVowels(String word) {
int count = 0;
for (char ch : word.toCharArray()) {
if (vowels.contains(ch)) {
count++;
}
}
return count;
}
}

View File

@ -0,0 +1,5 @@
node_modules
logs
Dockerfile
reports
README.md

View File

@ -0,0 +1,36 @@
# Gauge - metadata dir
.gauge
# Gauge - log files dir
logs
# Gauge - reports dir
reports
# Gauge - JavaScript node dependencies
node_modules
# Gauge - metadata dir
.gauge
# Gauge - log files dir
logs
# Gauge - reports dir
reports
# Gauge - JavaScript node dependencies
node_modules
# Gauge - metadata dir
.gauge
# Gauge - log files dir
logs
# Gauge - reports dir
reports
# Gauge - JavaScript node dependencies
node_modules
.DS_Store

View File

@ -0,0 +1,22 @@
# default.properties
# properties set here will be available to the test execution as environment variables
# sample_key = sample_value
#The path to the gauge reports directory. Should be either relative to the project directory or an absolute path
gauge_reports_dir = reports
#Set as false if gauge reports should not be overwritten on each execution. A new time-stamped directory will be created on each execution.
overwrite_reports = true
# Set to false to disable screenshots on failure in reports.
screenshot_on_failure = true
# The path to the gauge logs directory. Should be either relative to the project directory or an absolute path
logs_directory = logs
# The path the gauge specifications directory. Takes a comma separated list of specification files/directories.
gauge_specs_dir = specs
# The default delimiter used read csv files.
csv_delimiter = ,

View File

@ -0,0 +1,10 @@
#js.properties
#settings related to gauge-js.
test_timeout = 10000
# Change this to true to enable debugging support
DEBUG = false
# Comma seperated list of dirs. path should be relative to project root.
STEP_IMPL_DIR = tests

View File

@ -0,0 +1,6 @@
{
"Language": "js",
"Plugins": [
"html-report"
]
}

View File

@ -0,0 +1,11 @@
{
"name": "gauge-js-template",
"version": "0.0.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "gauge-js-template"
}
}
}

View File

@ -0,0 +1,7 @@
{
"name": "gauge-js-template",
"description": "Starter template for writing JavaScript tests for Gauge",
"scripts": {
"test": "gauge run specs/"
}
}

View File

@ -0,0 +1,32 @@
# Specification Heading
This is an executable specification file. This file follows markdown syntax.
Every heading in this file denotes a scenario. Every bulleted point denotes a step.
To execute this specification, run
gauge run specs
* Vowels in English language are "aeiou".
## Vowel counts in single word
tags: single word
* The word "gauge" has "3" vowels.
## Vowel counts in multiple word
This is the second scenario in this specification
Here's a step that takes a table
* Almost all words have vowels
|Word |Vowel Count|
|------|-----------|
|Gauge |3 |
|Mingle|2 |
|Snap |1 |
|GoCD |1 |
|Rhythm|0 |

View File

@ -0,0 +1,42 @@
/* globals gauge*/
"use strict";
var assert = require("assert");
var vowels = ["a", "e", "i", "o", "u"];
var numberOfVowels = function (word) {
var vowelArr = word.split("").filter(function (elem) { return vowels.indexOf(elem) > -1; });
return vowelArr.length;
};
// --------------------------
// Gauge step implementations
// --------------------------
step("Vowels in English language are <vowels>.", function(vowelsGiven) {
assert.equal(vowelsGiven, vowels.join(""));
});
step("The word <word> has <number> vowels.", function(word, number) {
assert.equal(number, numberOfVowels(word));
});
step("Almost all words have vowels <table>", function(table) {
table.rows.forEach(function (row) {
assert.equal(numberOfVowels(row.cells[0]), parseInt(row.cells[1]));
});
});
// ---------------
// Execution Hooks
// ---------------
beforeScenario(function () {
assert.equal(vowels.join(""), "aeiou");
});
beforeScenario(function () {
assert.equal(vowels[0], "a");
}, { tags: [ "single word" ]});

View File

@ -0,0 +1,91 @@
metadata:
name: gaugeExecuteTests
description: Installs gauge and executes specified gauge tests.
longDescription: |
In this step Gauge ([getgauge.io](https://getgauge.io)) acceptance tests are executed. Using Gauge it will be possible to have a three-tier test layout:
Acceptance Criteria
Test implemenation layer
Application driver layer
This layout is propagated by Jez Humble and Dave Farley in their book "Continuous Delivery" as a way to create maintainable acceptance test suites (see "Continuous Delivery", p. 190ff).
Using Gauge it is possible to write test specifications in [Markdown syntax](http://daringfireball.net/projects/markdown/syntax) and therefore allow e.g. product owners to write the relevant acceptance test specifications. At the same time it allows the developer to implement the steps described in the specification in her development environment.
You can use the [sample projects](https://github.com/getgauge/gauge-mvn-archetypes) of Gauge.
spec:
inputs:
secrets:
- name: seleniumHubCredentialsId
type: jenkins
description: "Defines the id of the user/password credentials to be used to connect to a Selenium Hub. The credentials are provided in the environment variables `PIPER_SELENIUM_HUB_USER` and `PIPER_SELENIUM_HUB_PASSWORD`."
resources:
- name: buildDescriptor
type: stash
- name: tests
type: stash
params:
- name: installCommand
type: string
description: 'Defines the command for installing Gauge. Gauge should be installed using npm. Example: npm install -g @getgauge/cli@1.2.1'
scope:
- STEPS
- STAGES
- PARAMETERS
- name: languageRunner
type: string
description: 'Defines the Gauge language runner to be used. Example: java'
scope:
- STEPS
- STAGES
- PARAMETERS
- name: runCommand
type: string
description: 'Defines the command which is used for executing Gauge. Example: run -s -p specs/'
scope:
- STEPS
- STAGES
- PARAMETERS
mandatory: true
- name: testOptions
type: string
description: "Allows to set specific options for the Gauge execution. Details can be found for example [in the Gauge Maven plugin documentation](https://github.com/getgauge-contrib/gauge-maven-plugin#executing-specs)"
scope:
- STEPS
- STAGES
- PARAMETERS
outputs:
resources:
- name: influx
type: influx
params:
- name: step_data
fields:
- name: gauge
type: bool
containers:
- name: gauge
image: node:lts-stretch
env:
- name: no_proxy
value: localhost,selenium,$no_proxy
- name: NO_PROXY
value: localhost,selenium,$NO_PROXY
workingDir: /home/node
volumeMounts:
- mountPath: /dev/shm
name: dev-shm
sidecars:
- image: selenium/standalone-chrome
name: selenium
securityContext:
privileged: true
volumeMounts:
- mountPath: /dev/shm
name: dev-shm
env:
- name: "NO_PROXY"
value: "localhost,selenium,$NO_PROXY"
- name: "no_proxy"
value: "localhost,selenium,$no_proxy"

View File

@ -186,6 +186,7 @@ public class CommonStepsTest extends BasePiperTest{
'integrationArtifactUpload', //implementing new golang pattern without fields
'containerExecuteStructureTests', //implementing new golang pattern without fields
'transportRequestUploadSOLMAN', //implementing new golang pattern without fields
'gaugeExecuteTests', //implementing new golang pattern without fields
'batsExecuteTests', //implementing new golang pattern without fields
]

View File

@ -1,163 +0,0 @@
import hudson.AbortException
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.ExpectedException
import org.junit.rules.RuleChain
import util.*
import static org.hamcrest.Matchers.*
import static org.junit.Assert.assertThat
class GaugeExecuteTestsTest extends BasePiperTest {
private JenkinsStepRule stepRule = new JenkinsStepRule(this)
private JenkinsLoggingRule loggingRule = new JenkinsLoggingRule(this)
private JenkinsShellCallRule shellRule = new JenkinsShellCallRule(this)
private JenkinsEnvironmentRule environmentRule = new JenkinsEnvironmentRule(this)
private ExpectedException thrown = ExpectedException.none()
@Rule
public RuleChain rules = Rules
.getCommonRules(this)
.around(new JenkinsReadYamlRule(this))
.around(shellRule)
.around(loggingRule)
.around(environmentRule)
.around(stepRule)
.around(thrown)
def gitParams = [:]
def seleniumParams = [:]
@Before
void init() throws Exception {
helper.registerAllowedMethod("git", [Map.class], { map -> gitParams = map })
helper.registerAllowedMethod("unstash", [String.class], { s -> return [s]})
helper.registerAllowedMethod('seleniumExecuteTests', [Map.class, Closure.class], {map, body ->
seleniumParams = map
return body()
})
}
@Test
void testExecuteGaugeDefaultSuccess() throws Exception {
stepRule.step.gaugeExecuteTests(
script: nullScript,
juStabUtils: utils,
testServerUrl: 'http://test.url'
)
assertThat(shellRule.shell, hasItem(stringContainsInOrder([
'export HOME=${HOME:-$(pwd)}',
'if [ "$HOME" = "/" ]; then export HOME=$(pwd); fi',
'export PATH=$HOME/bin/gauge:$PATH',
'mkdir -p $HOME/bin/gauge',
'curl -SsL https://downloads.gauge.org/stable | sh -s -- --location=$HOME/bin/gauge',
'gauge telemetry off',
'gauge install java',
'gauge install html-report',
'gauge install xml-report',
'mvn test-compile gauge:execute -DspecsDir=specs'
])))
assertThat(seleniumParams.dockerImage, is('maven:3.5-jdk-8'))
assertThat(seleniumParams.dockerEnvVars, hasEntry('TARGET_SERVER_URL', 'http://test.url'))
assertThat(seleniumParams.dockerName, is('maven'))
assertThat(seleniumParams.dockerWorkspace, is(''))
assertThat(seleniumParams.stashContent, hasSize(2))
assertThat(seleniumParams.stashContent, allOf(hasItem('buildDescriptor'), hasItem('tests')))
assertJobStatusSuccess()
}
@Test
void testDockerFromCustomStepConfiguration() {
def expectedImage = 'image:test'
def expectedEnvVars = ['HUB':'', 'HUB_URL':'', 'env1': 'value1', 'env2': 'value2']
def expectedOptions = '--opt1=val1 --opt2=val2 --opt3'
def expectedWorkspace = '/path/to/workspace'
nullScript.commonPipelineEnvironment.configuration = [steps:[gaugeExecuteTests:[
dockerImage: expectedImage,
dockerOptions: expectedOptions,
dockerEnvVars: expectedEnvVars,
dockerWorkspace: expectedWorkspace
]]]
stepRule.step.gaugeExecuteTests(
script: nullScript,
juStabUtils: utils
)
assert expectedImage == seleniumParams.dockerImage
assert expectedOptions == seleniumParams.dockerOptions
assert expectedEnvVars.equals(seleniumParams.dockerEnvVars)
assert expectedWorkspace == seleniumParams.dockerWorkspace
}
@Test
void testExecuteGaugeNode() throws Exception {
stepRule.step.gaugeExecuteTests(
script: nullScript,
buildTool: 'npm',
dockerEnvVars: ['TARGET_SERVER_URL':'http://custom.url'],
juStabUtils: utils,
testOptions: 'testSpec'
)
assertThat(shellRule.shell, hasItem(stringContainsInOrder([
'gauge install js',
'gauge run testSpec'
])))
assertThat(seleniumParams.dockerImage, is('node:lts-stretch'))
assertThat(seleniumParams.dockerEnvVars, hasEntry('TARGET_SERVER_URL', 'http://custom.url'))
assertThat(seleniumParams.dockerName, is('npm'))
assertThat(seleniumParams.dockerWorkspace, is('/home/node'))
assertJobStatusSuccess()
}
@Test
void testExecuteCustomWithError() throws Exception {
helper.registerAllowedMethod("sh", [String.class], { s ->
throw new RuntimeException('Test Error')
})
thrown.expect(AbortException)
thrown.expectMessage('ERROR: The execution of the gauge tests failed, see the log for details.')
try {
stepRule.step.gaugeExecuteTests(
script: nullScript,
juStabUtils: utils,
dockerImage: 'testImage',
dockerName: 'testImageName',
dockerWorkspace: '/home/test',
failOnError: true,
stashContent: ['testStash'],
)
} finally{
assertThat(seleniumParams.dockerImage, is('testImage'))
assertThat(seleniumParams.dockerName, is('testImageName'))
assertThat(seleniumParams.dockerWorkspace, is('/home/test'))
assertThat(seleniumParams.stashContent, hasSize(1))
assertThat(loggingRule.log, containsString('[gaugeExecuteTests] One or more tests failed'))
assertThat(nullScript.currentBuild.result, is('UNSTABLE'))
}
}
@Test
void testExecuteGaugeCustomRepo() throws Exception {
helper.registerAllowedMethod('git', [String.class], null)
helper.registerAllowedMethod('stash', [String.class], null)
stepRule.step.gaugeExecuteTests(
script: nullScript,
juStabUtils: utils,
testRepository: 'myTestRepo',
failOnError: true
)
// nested matchers do not work correctly
assertThat(seleniumParams.stashContent, hasItem(startsWith('testContent-')))
assertJobStatusSuccess()
}
}

View File

@ -1,167 +1,13 @@
import static com.sap.piper.Prerequisites.checkScript
import com.sap.piper.GenerateDocumentation
import com.sap.piper.Utils
import com.sap.piper.ConfigurationHelper
import com.sap.piper.GitUtils
import com.sap.piper.analytics.InfluxData
import groovy.text.GStringTemplateEngine
import groovy.transform.Field
@Field String STEP_NAME = getClass().getName()
@Field String METADATA_FILE = 'metadata/gauge.yaml'
@Field Set GENERAL_CONFIG_KEYS = []
@Field Set STEP_CONFIG_KEYS = [
/**
* Defines the build tool to be used for the test execution.
* @possibleValues `maven`, `npm`, `bundler`
*/
'buildTool',
/** @see dockerExecute*/
'dockerEnvVars',
/** @see dockerExecute*/
'dockerImage',
/** @see dockerExecute*/
'dockerName',
/** @see dockerExecute */
'dockerOptions',
/** @see dockerExecute*/
'dockerWorkspace',
/**
* Defines the behavior in case tests fail. When this is set to `true` test results cannot be recorded using the `publishTestResults` step afterwards.
* @possibleValues `true`, `false`
*/
'failOnError',
/** Defines the command for installing Gauge. In case the `dockerImage` already contains Gauge it can be set to empty: ``.*/
'installCommand',
/** Defines the Gauge language runner to be used.*/
'languageRunner',
/** Defines the command which is used for executing Gauge.*/
'runCommand',
/** Defines if specific stashes should be considered for the tests.*/
'stashContent',
/** Allows to set specific options for the Gauge execution. Details can be found for example [in the Gauge Maven plugin documentation](https://github.com/getgauge/gauge-maven-plugin#executing-specs)*/
'testOptions',
/** Defines the repository containing the tests, in case the test implementation is stored in a different repository than the code itself.*/
'testRepository',
/** Defines the branch containing the tests, in case the test implementation is stored in a different repository and a different branch than master.*/
'gitBranch',
/**
* Defines the credentials for the repository containing the tests, in case the test implementation is stored in a different and protected repository than the code itself.
* For protected repositories the `testRepository` needs to contain the ssh git url.
*/
'gitSshKeyCredentialsId',
/** It is passed as environment variable `TARGET_SERVER_URL` to the test execution. Tests running against the system should read the host information from this environment variable in order to be infrastructure agnostic.*/
'testServerUrl'
]
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS
/**
* In this step Gauge ([getgauge.io](http:getgauge.io)) acceptance tests are executed.
* Using Gauge it will be possible to have a three-tier test layout:
*
* * Acceptance Criteria
* * Test implemenation layer
* * Application driver layer
*
* This layout is propagated by Jez Humble and Dave Farley in their book "Continuous Delivery" as a way to create maintainable acceptance test suites (see "Continuous Delivery", p. 190ff).
*
* Using Gauge it is possible to write test specifications in [Markdown syntax](http://daringfireball.net/projects/markdown/syntax) and therefore allow e.g. product owners to write the relevant acceptance test specifications. At the same time it allows the developer to implement the steps described in the specification in her development environment.
*
* You can use the [sample projects](https://github.com/getgauge/gauge-mvn-archetypes) of Gauge.
*
* !!! note "Make sure to run against a Selenium Hub configuration"
* In the test example of _gauge-archetype-selenium_ please make sure to allow it to run against a Selenium hub:
*
* Please extend DriverFactory.java for example in following way:
*
* ``` java
* String hubUrl = System.getenv("HUB_URL");
* //when running on a Docker deamon (and not using Kubernetes plugin), Docker images will be linked
* //in this case hubUrl will be http://selenium:4444/wd/hub due to the linking of the containers
* hubUrl = (hubUrl == null) ? "http://localhost:4444/wd/hub" : hubUrl;
* Capabilities chromeCapabilities = DesiredCapabilities.chrome();
* System.out.println("Running on Selenium Hub: " + hubUrl);
* return new RemoteWebDriver(new URL(hubUrl), chromeCapabilities);
* ```
*/
@GenerateDocumentation
void call(Map parameters = [:]) {
handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters) {
def script = checkScript(this, parameters) ?: this
def utils = parameters.juStabUtils ?: new Utils()
String stageName = parameters.stageName ?: env.STAGE_NAME
InfluxData.addField('step_data', 'gauge', false)
// load default & individual configuration
Map config = ConfigurationHelper.newInstance(this)
.loadStepDefaults([:], stageName)
.mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
.mixinStageConfig(script.commonPipelineEnvironment, stageName, STEP_CONFIG_KEYS)
.mixin(parameters, PARAMETER_KEYS)
.dependingOn('buildTool').mixin('dockerImage')
.dependingOn('buildTool').mixin('dockerName')
.dependingOn('buildTool').mixin('dockerOptions')
.dependingOn('buildTool').mixin('dockerEnvVars')
.dependingOn('buildTool').mixin('dockerWorkspace')
.dependingOn('buildTool').mixin('languageRunner')
.dependingOn('buildTool').mixin('runCommand')
.dependingOn('buildTool').mixin('testOptions')
.use()
utils.pushToSWA([
step: STEP_NAME,
stepParamKey1: 'buildTool',
stepParam1: config.buildTool,
stepParamKey2: 'dockerName',
stepParam2: config.dockerName
], config)
if(!config.dockerEnvVars.TARGET_SERVER_URL && config.testServerUrl)
config.dockerEnvVars.TARGET_SERVER_URL = config.testServerUrl
if (config.testRepository) {
// handle separate test repository
config.stashContent = [GitUtils.handleTestRepository(this, config)]
} else {
config.stashContent = utils.unstashAll(config.stashContent)
}
seleniumExecuteTests (
script: script,
buildTool: config.buildTool,
dockerImage: config.dockerImage,
dockerName: config.dockerName,
dockerEnvVars: config.dockerEnvVars,
dockerOptions: config.dockerOptions,
dockerWorkspace: config.dockerWorkspace,
stashContent: config.stashContent
) {
String gaugeScript = ''
if (config.installCommand) {
gaugeScript = '''export HOME=${HOME:-$(pwd)}
if [ "$HOME" = "/" ]; then export HOME=$(pwd); fi
export PATH=$HOME/bin/gauge:$PATH
mkdir -p $HOME/bin/gauge
''' + config.installCommand + '''
gauge telemetry off
gauge install ''' + config.languageRunner + '''
gauge install html-report
gauge install xml-report
'''
}
gaugeScript += config.runCommand
try {
sh "${gaugeScript} ${config.testOptions}"
InfluxData.addField('step_data', 'gauge', true)
} catch (err) {
echo "[${STEP_NAME}] One or more tests failed"
script.currentBuild.result = 'UNSTABLE'
if (config.failOnError) error "[${STEP_NAME}] ERROR: The execution of the gauge tests failed, see the log for details."
}
}
}
List credentials = [
[type: 'usernamePassword', id: 'seleniumHubCredentialsId', env: ['PIPER_SELENIUM_HUB_USER', 'PIPER_SELENIUM_HUB_PASSWORD']],
]
final script = checkScript(this, parameters) ?: this
piperExecuteBin(parameters, STEP_NAME, METADATA_FILE, credentials)
}