diff --git a/godog.go b/godog.go index 895032e4..d3bdb960 100644 --- a/godog.go +++ b/godog.go @@ -10,7 +10,7 @@ Godog acts similar compared to go test command. It uses go compiler and linker tool in order to produce test executable. Godog contexts needs to be exported same as Test functions for go test. -For example, imagine you’re about to create the famous UNIX ls command. +For example, imagine you're about to create the famous UNIX ls command. Before you begin, you describe how the feature should work, see the example below.. Example: @@ -30,9 +30,9 @@ Example: foo """ -Now, wouldn’t it be cool if something could read this sentence and use it to actually -run a test against the ls command? Hey, that’s exactly what this package does! -As you’ll see, Godog is easy to learn, quick to use, and will put the fun back into tests. +Now, wouldn't it be cool if something could read this sentence and use it to actually +run a test against the ls command? Hey, that's exactly what this package does! +As you'll see, Godog is easy to learn, quick to use, and will put the fun back into tests. Godog was inspired by Behat and Cucumber the above description is taken from it's documentation. */ diff --git a/run.go b/run.go index de2a0c56..b36e55fb 100644 --- a/run.go +++ b/run.go @@ -15,6 +15,7 @@ import ( "github.com/cucumber/messages-go/v10" "github.com/cucumber/godog/colors" + "github.com/cucumber/godog/formatters" "github.com/cucumber/godog/internal/models" "github.com/cucumber/godog/internal/parser" "github.com/cucumber/godog/internal/storage" @@ -140,6 +141,24 @@ func runWithOptions(suiteName string, runner runner, opt Options) int { output = opt.Output } + if formatterParts := strings.SplitN(opt.Format, ":", 2); len(formatterParts) > 1 { + f, err := os.Create(formatterParts[1]) + if err != nil { + err = fmt.Errorf( + `couldn't create file with name: "%s", error: %s`, + formatterParts[1], err.Error(), + ) + fmt.Fprintln(os.Stderr, err) + + return exitOptionError + } + + defer f.Close() + + output = f + opt.Format = formatterParts[0] + } + if opt.NoColors { output = colors.Uncolored(output) } else { @@ -165,10 +184,10 @@ func runWithOptions(suiteName string, runner runner, opt Options) int { opt.Concurrency = 1 } - formatter := FindFmt(opt.Format) + formatter := formatters.FindFmt(opt.Format) if nil == formatter { var names []string - for name := range AvailableFormatters() { + for name := range formatters.AvailableFormatters() { names = append(names, name) } fmt.Fprintln(os.Stderr, fmt.Errorf( diff --git a/run_test.go b/run_test.go index 8e9a1b9e..d3f370f5 100644 --- a/run_test.go +++ b/run_test.go @@ -6,6 +6,7 @@ import ( "io" "io/ioutil" "os" + "path/filepath" "regexp" "strconv" "strings" @@ -308,6 +309,79 @@ func Test_RandomizeRun(t *testing.T) { assert.Equal(t, expectedOutput, actualOutput) } +func Test_FormatOutputRun(t *testing.T) { + const noRandomFlag = 0 + const noConcurrencyFlag = 1 + const formatter = "junit" + const featurePath = "internal/formatters/formatter-tests/features/with_few_empty_scenarios.feature" + + fmtOutputScenarioInitializer := func(ctx *ScenarioContext) { + ctx.Step(`^(?:a )?failing step`, failingStepDef) + ctx.Step(`^(?:a )?pending step$`, pendingStepDef) + ctx.Step(`^(?:a )?passing step$`, passingStepDef) + ctx.Step(`^odd (\d+) and even (\d+) number$`, oddEvenStepDef) + } + + expectedStatus, expectedOutput := testRun(t, + fmtOutputScenarioInitializer, + formatter, noConcurrencyFlag, + noRandomFlag, []string{featurePath}, + ) + + dir := filepath.Join(os.TempDir(), t.Name()) + err := os.MkdirAll(dir, 0755) + require.NoError(t, err) + + defer os.RemoveAll(dir) + + file := filepath.Join(dir, "result.xml") + + actualStatus, actualOutput := testRun(t, + fmtOutputScenarioInitializer, + formatter+":"+file, noConcurrencyFlag, + noRandomFlag, []string{featurePath}, + ) + + result, err := ioutil.ReadFile(file) + require.NoError(t, err) + actualOutputFromFile := string(result) + + assert.Equal(t, expectedStatus, actualStatus) + assert.Empty(t, actualOutput) + assert.Equal(t, expectedOutput, actualOutputFromFile) +} + +func Test_FormatOutputRun_Error(t *testing.T) { + const noRandomFlag = 0 + const noConcurrencyFlag = 1 + const formatter = "junit" + const featurePath = "internal/formatters/formatter-tests/features/with_few_empty_scenarios.feature" + + fmtOutputScenarioInitializer := func(ctx *ScenarioContext) { + ctx.Step(`^(?:a )?failing step`, failingStepDef) + ctx.Step(`^(?:a )?pending step$`, pendingStepDef) + ctx.Step(`^(?:a )?passing step$`, passingStepDef) + ctx.Step(`^odd (\d+) and even (\d+) number$`, oddEvenStepDef) + } + + expectedStatus, expectedOutput := exitOptionError, "" + + dir := filepath.Join(os.TempDir(), t.Name()) + file := filepath.Join(dir, "result.xml") + + actualStatus, actualOutput := testRun(t, + fmtOutputScenarioInitializer, + formatter+":"+file, noConcurrencyFlag, + noRandomFlag, []string{featurePath}, + ) + + assert.Equal(t, expectedStatus, actualStatus) + assert.Equal(t, expectedOutput, actualOutput) + + _, err := ioutil.ReadFile(file) + assert.Error(t, err) +} + func Test_AllFeaturesRun(t *testing.T) { const concurrency = 100 const noRandomFlag = 0