diff --git a/.circleci/config.yml b/.circleci/config.yml
index fc17772..b5d6949 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -36,7 +36,7 @@ commands:
go_test:
description: "Run go test"
steps:
- - run: sed -i 's#github.com/cucumber/godog_test#_test#g' formatter-tests/*/*
+ - run: sed -i 's#github.com/cucumber/godog/internal/formatters_test#/internal/formatters_test#g' internal/formatters/formatter-tests/*/*
- run: go test -v -race -coverprofile=coverage.txt -covermode=atomic ./...
godog:
description: "Run godog"
diff --git a/flags.go b/flags.go
index 88aa9da..80ea8d1 100644
--- a/flags.go
+++ b/flags.go
@@ -10,8 +10,12 @@ import (
"time"
"github.com/cucumber/godog/colors"
+ "github.com/cucumber/godog/internal/utils"
)
+// repeats a space n times
+var s = utils.S
+
var descFeaturesArgument = "Optional feature(s) to run. Can be:\n" +
s(4) + "- dir " + colors.Yellow("(features/)") + "\n" +
s(4) + "- feature " + colors.Yellow("(*.feature)") + "\n" +
diff --git a/flags_test.go b/flags_test.go
index 8520d27..5390d67 100644
--- a/flags_test.go
+++ b/flags_test.go
@@ -8,6 +8,7 @@ import (
"testing"
"github.com/cucumber/godog/colors"
+ "github.com/cucumber/godog/internal/formatters"
)
func TestFlagsShouldRandomizeAndGenerateSeed(t *testing.T) {
@@ -61,7 +62,7 @@ func TestFlagsUsageShouldIncludeFormatDescriptons(t *testing.T) {
output := colors.Uncolored(&buf)
// register some custom formatter
- Format("custom", "custom format description", junitFunc)
+ Format("custom", "custom format description", formatters.JUnitFormatterFunc)
var opt Options
flags := FlagSet(&opt)
diff --git a/fmt.go b/fmt.go
index ee4dab4..5934ddb 100644
--- a/fmt.go
+++ b/fmt.go
@@ -6,28 +6,18 @@ import (
"strings"
"unicode/utf8"
- "github.com/cucumber/messages-go/v10"
+ "github.com/cucumber/godog/colors"
+ "github.com/cucumber/godog/formatters"
+ internal_fmt "github.com/cucumber/godog/internal/formatters"
+ "github.com/cucumber/godog/internal/models"
+ "github.com/cucumber/godog/internal/storage"
)
-type registeredFormatter struct {
- name string
- description string
- fmt FormatterFunc
-}
-
-var formatters []*registeredFormatter
-
// FindFmt searches available formatters registered
// and returns FormaterFunc matched by given
// format name or nil otherwise
func FindFmt(name string) FormatterFunc {
- for _, el := range formatters {
- if el.name == name {
- return el.fmt
- }
- }
-
- return nil
+ return formatters.FindFmt(name)
}
// Format registers a feature suite output
@@ -35,24 +25,14 @@ func FindFmt(name string) FormatterFunc {
// FormatterFunc constructor function, to initialize
// formatter with the output recorder.
func Format(name, description string, f FormatterFunc) {
- formatters = append(formatters, ®isteredFormatter{
- name: name,
- fmt: f,
- description: description,
- })
+ formatters.Format(name, description, f)
}
// AvailableFormatters gives a map of all
// formatters registered with their name as key
// and description as value
func AvailableFormatters() map[string]string {
- fmts := make(map[string]string, len(formatters))
-
- for _, f := range formatters {
- fmts[f.name] = f.description
- }
-
- return fmts
+ return formatters.AvailableFormatters()
}
// Formatter is an interface for feature runner
@@ -62,32 +42,17 @@ func AvailableFormatters() map[string]string {
// suite results in different ways. These new
// formatters needs to be registered with a
// godog.Format function call
-type Formatter interface {
- TestRunStarted()
- Feature(*messages.GherkinDocument, string, []byte)
- Pickle(*messages.Pickle)
- Defined(*messages.Pickle, *messages.Pickle_PickleStep, *StepDefinition)
- Failed(*messages.Pickle, *messages.Pickle_PickleStep, *StepDefinition, error)
- Passed(*messages.Pickle, *messages.Pickle_PickleStep, *StepDefinition)
- Skipped(*messages.Pickle, *messages.Pickle_PickleStep, *StepDefinition)
- Undefined(*messages.Pickle, *messages.Pickle_PickleStep, *StepDefinition)
- Pending(*messages.Pickle, *messages.Pickle_PickleStep, *StepDefinition)
- Summary()
-}
+type Formatter = formatters.Formatter
type storageFormatter interface {
- setStorage(*storage)
+ SetStorage(*storage.Storage)
}
// FormatterFunc builds a formatter with given
// suite name and io.Writer to record output
-type FormatterFunc func(string, io.Writer) Formatter
+type FormatterFunc = formatters.FormatterFunc
-func isLastStep(pickle *messages.Pickle, step *messages.Pickle_PickleStep) bool {
- return pickle.Steps[len(pickle.Steps)-1].Id == step.Id
-}
-
-func printStepDefinitions(steps []*StepDefinition, w io.Writer) {
+func printStepDefinitions(steps []*models.StepDefinition, w io.Writer) {
var longest int
for _, def := range steps {
n := utf8.RuneCountInString(def.Expr.String())
@@ -98,9 +63,11 @@ func printStepDefinitions(steps []*StepDefinition, w io.Writer) {
for _, def := range steps {
n := utf8.RuneCountInString(def.Expr.String())
- location := def.definitionID()
+ location := internal_fmt.DefinitionID(def)
spaces := strings.Repeat(" ", longest-n)
- fmt.Fprintln(w, yellow(def.Expr.String())+spaces, blackb("# "+location))
+ fmt.Fprintln(w,
+ colors.Yellow(def.Expr.String())+spaces,
+ colors.Bold(colors.Black)("# "+location))
}
if len(steps) == 0 {
diff --git a/formatters/fmt.go b/formatters/fmt.go
new file mode 100644
index 0000000..9f56ffe
--- /dev/null
+++ b/formatters/fmt.go
@@ -0,0 +1,79 @@
+package formatters
+
+import (
+ "io"
+
+ "github.com/cucumber/messages-go/v10"
+
+ "github.com/cucumber/godog/internal/models"
+)
+
+type registeredFormatter struct {
+ name string
+ description string
+ fmt FormatterFunc
+}
+
+var registeredFormatters []*registeredFormatter
+
+// FindFmt searches available formatters registered
+// and returns FormaterFunc matched by given
+// format name or nil otherwise
+func FindFmt(name string) FormatterFunc {
+ for _, el := range registeredFormatters {
+ if el.name == name {
+ return el.fmt
+ }
+ }
+
+ return nil
+}
+
+// Format registers a feature suite output
+// formatter by given name, description and
+// FormatterFunc constructor function, to initialize
+// formatter with the output recorder.
+func Format(name, description string, f FormatterFunc) {
+ registeredFormatters = append(registeredFormatters, ®isteredFormatter{
+ name: name,
+ fmt: f,
+ description: description,
+ })
+}
+
+// AvailableFormatters gives a map of all
+// formatters registered with their name as key
+// and description as value
+func AvailableFormatters() map[string]string {
+ fmts := make(map[string]string, len(registeredFormatters))
+
+ for _, f := range registeredFormatters {
+ fmts[f.name] = f.description
+ }
+
+ return fmts
+}
+
+// Formatter is an interface for feature runner
+// output summary presentation.
+//
+// New formatters may be created to represent
+// suite results in different ways. These new
+// formatters needs to be registered with a
+// godog.Format function call
+type Formatter interface {
+ TestRunStarted()
+ Feature(*messages.GherkinDocument, string, []byte)
+ Pickle(*messages.Pickle)
+ Defined(*messages.Pickle, *messages.Pickle_PickleStep, *models.StepDefinition)
+ Failed(*messages.Pickle, *messages.Pickle_PickleStep, *models.StepDefinition, error)
+ Passed(*messages.Pickle, *messages.Pickle_PickleStep, *models.StepDefinition)
+ Skipped(*messages.Pickle, *messages.Pickle_PickleStep, *models.StepDefinition)
+ Undefined(*messages.Pickle, *messages.Pickle_PickleStep, *models.StepDefinition)
+ Pending(*messages.Pickle, *messages.Pickle_PickleStep, *models.StepDefinition)
+ Summary()
+}
+
+// FormatterFunc builds a formatter with given
+// suite name and io.Writer to record output
+type FormatterFunc func(string, io.Writer) Formatter
diff --git a/formatters/fmt_test.go b/formatters/fmt_test.go
new file mode 100644
index 0000000..186861c
--- /dev/null
+++ b/formatters/fmt_test.go
@@ -0,0 +1,65 @@
+package formatters_test
+
+import (
+ "io"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+
+ "github.com/cucumber/godog"
+)
+
+func Test_FindFmt(t *testing.T) {
+ cases := map[string]bool{
+ "cucumber": true,
+ "events": true,
+ "junit": true,
+ "pretty": true,
+ "progress": true,
+ "unknown": false,
+ "undef": false,
+ }
+
+ for name, expected := range cases {
+ t.Run(
+ name,
+ func(t *testing.T) {
+ actual := godog.FindFmt(name)
+
+ if expected {
+ assert.NotNilf(t, actual, "expected %s formatter should be available", name)
+ } else {
+ assert.Nilf(t, actual, "expected %s formatter should be available", name)
+ }
+ },
+ )
+ }
+}
+
+func Test_AvailableFormatters(t *testing.T) {
+ expected := map[string]string{
+ "cucumber": "Produces cucumber JSON format output.",
+ "events": "Produces JSON event stream, based on spec: 0.1.0.",
+ "junit": "Prints junit compatible xml to stdout",
+ "pretty": "Prints every feature with runtime statuses.",
+ "progress": "Prints a character per step.",
+ }
+
+ actual := godog.AvailableFormatters()
+ assert.Equal(t, expected, actual)
+}
+
+func Test_Format(t *testing.T) {
+ actual := godog.FindFmt("Test_Format")
+ require.Nil(t, actual)
+
+ godog.Format("Test_Format", "...", testFormatterFunc)
+ actual = godog.FindFmt("Test_Format")
+
+ assert.NotNil(t, actual)
+}
+
+func testFormatterFunc(suiteName string, out io.Writer) godog.Formatter {
+ return nil
+}
diff --git a/internal/formatters/fmt.go b/internal/formatters/fmt.go
new file mode 100644
index 0000000..e41b5d0
--- /dev/null
+++ b/internal/formatters/fmt.go
@@ -0,0 +1,104 @@
+package formatters
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+ "regexp"
+ "runtime"
+ "strconv"
+ "strings"
+
+ "github.com/cucumber/godog/colors"
+ "github.com/cucumber/godog/internal/models"
+ "github.com/cucumber/godog/internal/utils"
+ "github.com/cucumber/messages-go/v10"
+)
+
+var (
+ red = colors.Red
+ redb = colors.Bold(colors.Red)
+ green = colors.Green
+ blackb = colors.Bold(colors.Black)
+ yellow = colors.Yellow
+ cyan = colors.Cyan
+ cyanb = colors.Bold(colors.Cyan)
+ whiteb = colors.Bold(colors.White)
+)
+
+// repeats a space n times
+var s = utils.S
+
+var (
+ passed = models.Passed
+ failed = models.Failed
+ skipped = models.Skipped
+ undefined = models.Undefined
+ pending = models.Pending
+)
+
+type sortFeaturesByName []*models.Feature
+
+func (s sortFeaturesByName) Len() int { return len(s) }
+func (s sortFeaturesByName) Less(i, j int) bool { return s[i].Feature.Name < s[j].Feature.Name }
+func (s sortFeaturesByName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+
+type sortPicklesByID []*messages.Pickle
+
+func (s sortPicklesByID) Len() int { return len(s) }
+func (s sortPicklesByID) Less(i, j int) bool {
+ iID := mustConvertStringToInt(s[i].Id)
+ jID := mustConvertStringToInt(s[j].Id)
+ return iID < jID
+}
+func (s sortPicklesByID) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+
+type sortPickleStepResultsByPickleStepID []models.PickleStepResult
+
+func (s sortPickleStepResultsByPickleStepID) Len() int { return len(s) }
+func (s sortPickleStepResultsByPickleStepID) Less(i, j int) bool {
+ iID := mustConvertStringToInt(s[i].PickleStepID)
+ jID := mustConvertStringToInt(s[j].PickleStepID)
+ return iID < jID
+}
+func (s sortPickleStepResultsByPickleStepID) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+
+func mustConvertStringToInt(s string) int {
+ i, err := strconv.Atoi(s)
+ if err != nil {
+ panic(err)
+ }
+
+ return i
+}
+
+// DefinitionID ...
+func DefinitionID(sd *models.StepDefinition) string {
+ ptr := sd.HandlerValue.Pointer()
+ f := runtime.FuncForPC(ptr)
+ file, line := f.FileLine(ptr)
+ dir := filepath.Dir(file)
+
+ fn := strings.Replace(f.Name(), dir, "", -1)
+ var parts []string
+ for _, gr := range matchFuncDefRef.FindAllStringSubmatch(fn, -1) {
+ parts = append(parts, strings.Trim(gr[1], "_."))
+ }
+ if len(parts) > 0 {
+ // case when suite is a structure with methods
+ fn = strings.Join(parts, ".")
+ } else {
+ // case when steps are just plain funcs
+ fn = strings.Trim(fn, "_.")
+ }
+
+ if pkg := os.Getenv("GODOG_TESTED_PACKAGE"); len(pkg) > 0 {
+ fn = strings.Replace(fn, pkg, "", 1)
+ fn = strings.TrimLeft(fn, ".")
+ fn = strings.Replace(fn, "..", ".", -1)
+ }
+
+ return fmt.Sprintf("%s:%d -> %s", filepath.Base(file), line, fn)
+}
+
+var matchFuncDefRef = regexp.MustCompile(`\(([^\)]+)\)`)
diff --git a/fmt_base.go b/internal/formatters/fmt_base.go
similarity index 68%
rename from fmt_base.go
rename to internal/formatters/fmt_base.go
index 45fcb60..d723f16 100644
--- a/fmt_base.go
+++ b/internal/formatters/fmt_base.go
@@ -1,4 +1,4 @@
-package godog
+package formatters
import (
"bytes"
@@ -14,14 +14,20 @@ 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/storage"
+ "github.com/cucumber/godog/internal/utils"
)
-func baseFmtFunc(suite string, out io.Writer) Formatter {
- return newBaseFmt(suite, out)
+// BaseFormatterFunc implements the FormatterFunc for the base formatter
+func BaseFormatterFunc(suite string, out io.Writer) formatters.Formatter {
+ return NewBaseFmt(suite, out)
}
-func newBaseFmt(suite string, out io.Writer) *basefmt {
- return &basefmt{
+// NewBaseFmt creates a new base formatter
+func NewBaseFmt(suite string, out io.Writer) *Basefmt {
+ return &Basefmt{
suiteName: suite,
indent: 2,
out: out,
@@ -29,47 +35,63 @@ func newBaseFmt(suite string, out io.Writer) *basefmt {
}
}
-type basefmt struct {
+// Basefmt ...
+type Basefmt struct {
suiteName string
out io.Writer
indent int
- storage *storage
+ storage *storage.Storage
lock *sync.Mutex
}
-func (f *basefmt) setStorage(st *storage) {
+// SetStorage ...
+func (f *Basefmt) SetStorage(st *storage.Storage) {
f.lock.Lock()
defer f.lock.Unlock()
f.storage = st
}
-func (f *basefmt) TestRunStarted() {}
-func (f *basefmt) Feature(ft *messages.GherkinDocument, p string, c []byte) {}
-func (f *basefmt) Pickle(p *messages.Pickle) {}
-func (f *basefmt) Defined(*messages.Pickle, *messages.Pickle_PickleStep, *StepDefinition) {}
-func (f *basefmt) Passed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
-}
-func (f *basefmt) Skipped(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
-}
-func (f *basefmt) Undefined(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
-}
-func (f *basefmt) Failed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition, err error) {
-}
-func (f *basefmt) Pending(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
+// TestRunStarted ...
+func (f *Basefmt) TestRunStarted() {}
+
+// Feature ...
+func (f *Basefmt) Feature(*messages.GherkinDocument, string, []byte) {}
+
+// Pickle ...
+func (f *Basefmt) Pickle(*messages.Pickle) {}
+
+// Defined ...
+func (f *Basefmt) Defined(*messages.Pickle, *messages.Pickle_PickleStep, *models.StepDefinition) {}
+
+// Passed ...
+func (f *Basefmt) Passed(*messages.Pickle, *messages.Pickle_PickleStep, *models.StepDefinition) {}
+
+// Skipped ...
+func (f *Basefmt) Skipped(*messages.Pickle, *messages.Pickle_PickleStep, *models.StepDefinition) {}
+
+// Undefined ...
+func (f *Basefmt) Undefined(*messages.Pickle, *messages.Pickle_PickleStep, *models.StepDefinition) {}
+
+// Failed ...
+func (f *Basefmt) Failed(*messages.Pickle, *messages.Pickle_PickleStep, *models.StepDefinition, error) {
}
-func (f *basefmt) Summary() {
+// Pending ...
+func (f *Basefmt) Pending(*messages.Pickle, *messages.Pickle_PickleStep, *models.StepDefinition) {}
+
+// Summary ...
+func (f *Basefmt) Summary() {
var totalSc, passedSc, undefinedSc int
var totalSt, passedSt, failedSt, skippedSt, pendingSt, undefinedSt int
- pickleResults := f.storage.mustGetPickleResults()
+ pickleResults := f.storage.MustGetPickleResults()
for _, pr := range pickleResults {
- var prStatus stepResultStatus
+ var prStatus models.StepResultStatus
totalSc++
- pickleStepResults := f.storage.mustGetPickleStepResultsByPickleID(pr.PickleID)
+ pickleStepResults := f.storage.MustGetPickleStepResultsByPickleID(pr.PickleID)
if len(pickleStepResults) == 0 {
prStatus = undefined
@@ -130,8 +152,8 @@ func (f *basefmt) Summary() {
}
scenarios = append(scenarios, parts...)
- testRunStartedAt := f.storage.mustGetTestRunStarted().StartedAt
- elapsed := timeNowFunc().Sub(testRunStartedAt)
+ testRunStartedAt := f.storage.MustGetTestRunStarted().StartedAt
+ elapsed := utils.TimeNowFunc().Sub(testRunStartedAt)
fmt.Fprintln(f.out, "")
@@ -161,15 +183,16 @@ func (f *basefmt) Summary() {
fmt.Fprintln(f.out, "Randomized with seed:", colors.Yellow(seed))
}
- if text := f.snippets(); text != "" {
+ if text := f.Snippets(); text != "" {
fmt.Fprintln(f.out, "")
fmt.Fprintln(f.out, yellow("You can implement step definitions for undefined steps with these snippets:"))
fmt.Fprintln(f.out, yellow(text))
}
}
-func (f *basefmt) snippets() string {
- undefinedStepResults := f.storage.mustGetPickleStepResultsByStatus(undefined)
+// Snippets ...
+func (f *Basefmt) Snippets() string {
+ undefinedStepResults := f.storage.MustGetPickleStepResultsByStatus(undefined)
if len(undefinedStepResults) == 0 {
return ""
}
@@ -178,12 +201,12 @@ func (f *basefmt) snippets() string {
var snips []undefinedSnippet
// build snippets
for _, u := range undefinedStepResults {
- pickleStep := f.storage.mustGetPickleStep(u.PickleStepID)
+ pickleStep := f.storage.MustGetPickleStep(u.PickleStepID)
steps := []string{pickleStep.Text}
arg := pickleStep.Argument
- if u.def != nil {
- steps = u.def.undefined
+ if u.Def != nil {
+ steps = u.Def.Undefined
arg = nil
}
for _, step := range steps {
diff --git a/fmt_color_tag_test.go b/internal/formatters/fmt_color_tag_test.go
similarity index 99%
rename from fmt_color_tag_test.go
rename to internal/formatters/fmt_color_tag_test.go
index 6a2a19a..871e333 100644
--- a/fmt_color_tag_test.go
+++ b/internal/formatters/fmt_color_tag_test.go
@@ -1,4 +1,4 @@
-package godog_test
+package formatters_test
import (
"bytes"
diff --git a/fmt_cucumber.go b/internal/formatters/fmt_cucumber.go
similarity index 80%
rename from fmt_cucumber.go
rename to internal/formatters/fmt_cucumber.go
index 10c1b5a..5f3743e 100644
--- a/fmt_cucumber.go
+++ b/internal/formatters/fmt_cucumber.go
@@ -1,4 +1,4 @@
-package godog
+package formatters
/*
The specification for the formatting originated from https://www.relishapp.com/cucumber/cucumber/docs/formatters/json-output-formatter.
@@ -19,22 +19,26 @@ import (
"strings"
"github.com/cucumber/messages-go/v10"
+
+ "github.com/cucumber/godog/formatters"
+ "github.com/cucumber/godog/internal/models"
)
func init() {
- Format("cucumber", "Produces cucumber JSON format output.", cucumberFunc)
+ formatters.Format("cucumber", "Produces cucumber JSON format output.", CucumberFormatterFunc)
}
-func cucumberFunc(suite string, out io.Writer) Formatter {
- return &cukefmt{basefmt: newBaseFmt(suite, out)}
+// CucumberFormatterFunc implements the FormatterFunc for the cucumber formatter
+func CucumberFormatterFunc(suite string, out io.Writer) formatters.Formatter {
+ return &cukefmt{Basefmt: NewBaseFmt(suite, out)}
}
type cukefmt struct {
- *basefmt
+ *Basefmt
}
func (f *cukefmt) Summary() {
- features := f.storage.mustGetFeatures()
+ features := f.storage.MustGetFeatures()
res := f.buildCukeFeatures(features)
@@ -46,15 +50,15 @@ func (f *cukefmt) Summary() {
fmt.Fprintf(f.out, "%s\n", string(dat))
}
-func (f *cukefmt) buildCukeFeatures(features []*feature) (res []cukeFeatureJSON) {
+func (f *cukefmt) buildCukeFeatures(features []*models.Feature) (res []CukeFeatureJSON) {
sort.Sort(sortFeaturesByName(features))
- res = make([]cukeFeatureJSON, len(features))
+ res = make([]CukeFeatureJSON, len(features))
for idx, feat := range features {
cukeFeature := buildCukeFeature(feat)
- pickles := f.storage.mustGetPickles(feat.Uri)
+ pickles := f.storage.MustGetPickles(feat.Uri)
sort.Sort(sortPicklesByID(pickles))
cukeFeature.Elements = f.buildCukeElements(pickles)
@@ -75,8 +79,8 @@ func (f *cukefmt) buildCukeElements(pickles []*messages.Pickle) (res []cukeEleme
res = make([]cukeElement, len(pickles))
for idx, pickle := range pickles {
- pickleResult := f.storage.mustGetPickleResult(pickle.Id)
- pickleStepResults := f.storage.mustGetPickleStepResultsByPickleID(pickle.Id)
+ pickleResult := f.storage.MustGetPickleResult(pickle.Id)
+ pickleStepResults := f.storage.MustGetPickleStepResultsByPickleID(pickle.Id)
cukeElement := f.buildCukeElement(pickle)
@@ -88,12 +92,14 @@ func (f *cukefmt) buildCukeElements(pickles []*messages.Pickle) (res []cukeEleme
for jdx, stepResult := range pickleStepResults {
cukeStep := f.buildCukeStep(pickle, stepResult)
- stepResultFinishedAt := stepResult.finishedAt
+ stepResultFinishedAt := stepResult.FinishedAt
d := int(stepResultFinishedAt.Sub(stepStartedAt).Nanoseconds())
stepStartedAt = stepResultFinishedAt
cukeStep.Result.Duration = &d
- if stepResult.Status == undefined || stepResult.Status == pending || stepResult.Status == skipped {
+ if stepResult.Status == undefined ||
+ stepResult.Status == pending ||
+ stepResult.Status == skipped {
cukeStep.Result.Duration = nil
}
@@ -157,7 +163,8 @@ type cukeElement struct {
Steps []cukeStep `json:"steps,omitempty"`
}
-type cukeFeatureJSON struct {
+// CukeFeatureJSON ...
+type CukeFeatureJSON struct {
URI string `json:"uri"`
ID string `json:"id"`
Keyword string `json:"keyword"`
@@ -169,8 +176,8 @@ type cukeFeatureJSON struct {
Elements []cukeElement `json:"elements,omitempty"`
}
-func buildCukeFeature(feat *feature) cukeFeatureJSON {
- cukeFeature := cukeFeatureJSON{
+func buildCukeFeature(feat *models.Feature) CukeFeatureJSON {
+ cukeFeature := CukeFeatureJSON{
URI: feat.Uri,
ID: makeCukeID(feat.Feature.Name),
Keyword: feat.Feature.Keyword,
@@ -195,8 +202,8 @@ func buildCukeFeature(feat *feature) cukeFeatureJSON {
}
func (f *cukefmt) buildCukeElement(pickle *messages.Pickle) (cukeElement cukeElement) {
- feature := f.storage.mustGetFeature(pickle.Uri)
- scenario := feature.findScenario(pickle.AstNodeIds[0])
+ feature := f.storage.MustGetFeature(pickle.Uri)
+ scenario := feature.FindScenario(pickle.AstNodeIds[0])
cukeElement.Name = pickle.Name
cukeElement.Line = int(scenario.Location.Line)
@@ -214,7 +221,7 @@ func (f *cukefmt) buildCukeElement(pickle *messages.Pickle) (cukeElement cukeEle
return
}
- example, _ := feature.findExample(pickle.AstNodeIds[1])
+ example, _ := feature.FindExample(pickle.AstNodeIds[1])
for _, tag := range example.Tags {
tag := cukeTag{Line: int(tag.Location.Line), Name: tag.Name}
@@ -238,14 +245,14 @@ func (f *cukefmt) buildCukeElement(pickle *messages.Pickle) (cukeElement cukeEle
return cukeElement
}
-func (f *cukefmt) buildCukeStep(pickle *messages.Pickle, stepResult pickleStepResult) (cukeStep cukeStep) {
- feature := f.storage.mustGetFeature(pickle.Uri)
- pickleStep := f.storage.mustGetPickleStep(stepResult.PickleStepID)
- step := feature.findStep(pickleStep.AstNodeIds[0])
+func (f *cukefmt) buildCukeStep(pickle *messages.Pickle, stepResult models.PickleStepResult) (cukeStep cukeStep) {
+ feature := f.storage.MustGetFeature(pickle.Uri)
+ pickleStep := f.storage.MustGetPickleStep(stepResult.PickleStepID)
+ step := feature.FindStep(pickleStep.AstNodeIds[0])
line := step.Location.Line
if len(pickle.AstNodeIds) == 2 {
- _, row := feature.findExample(pickle.AstNodeIds[1])
+ _, row := feature.FindExample(pickle.AstNodeIds[1])
line = row.Location.Line
}
@@ -273,13 +280,13 @@ func (f *cukefmt) buildCukeStep(pickle *messages.Pickle, stepResult pickleStepRe
}
}
- if stepResult.def != nil {
- cukeStep.Match.Location = strings.Split(stepResult.def.definitionID(), " ")[0]
+ if stepResult.Def != nil {
+ cukeStep.Match.Location = strings.Split(DefinitionID(stepResult.Def), " ")[0]
}
cukeStep.Result.Status = stepResult.Status.String()
- if stepResult.err != nil {
- cukeStep.Result.Error = stepResult.err.Error()
+ if stepResult.Err != nil {
+ cukeStep.Result.Error = stepResult.Err.Error()
}
if stepResult.Status == undefined || stepResult.Status == pending {
diff --git a/fmt_events.go b/internal/formatters/fmt_events.go
similarity index 53%
rename from fmt_events.go
rename to internal/formatters/fmt_events.go
index a129f78..2ff0776 100644
--- a/fmt_events.go
+++ b/internal/formatters/fmt_events.go
@@ -1,4 +1,4 @@
-package godog
+package formatters
import (
"encoding/json"
@@ -6,24 +6,29 @@ import (
"io"
"github.com/cucumber/messages-go/v10"
+
+ "github.com/cucumber/godog/formatters"
+ "github.com/cucumber/godog/internal/models"
+ "github.com/cucumber/godog/internal/utils"
)
const nanoSec = 1000000
const spec = "0.1.0"
func init() {
- Format("events", fmt.Sprintf("Produces JSON event stream, based on spec: %s.", spec), eventsFunc)
+ formatters.Format("events", fmt.Sprintf("Produces JSON event stream, based on spec: %s.", spec), EventsFormatterFunc)
}
-func eventsFunc(suite string, out io.Writer) Formatter {
- return &events{basefmt: newBaseFmt(suite, out)}
+// EventsFormatterFunc implements the FormatterFunc for the events formatter
+func EventsFormatterFunc(suite string, out io.Writer) formatters.Formatter {
+ return &eventsFormatter{Basefmt: NewBaseFmt(suite, out)}
}
-type events struct {
- *basefmt
+type eventsFormatter struct {
+ *Basefmt
}
-func (f *events) event(ev interface{}) {
+func (f *eventsFormatter) event(ev interface{}) {
data, err := json.Marshal(ev)
if err != nil {
panic(fmt.Sprintf("failed to marshal stream event: %+v - %v", ev, err))
@@ -31,8 +36,8 @@ func (f *events) event(ev interface{}) {
fmt.Fprintln(f.out, string(data))
}
-func (f *events) Pickle(pickle *messages.Pickle) {
- f.basefmt.Pickle(pickle)
+func (f *eventsFormatter) Pickle(pickle *messages.Pickle) {
+ f.Basefmt.Pickle(pickle)
f.lock.Lock()
defer f.lock.Unlock()
@@ -44,7 +49,7 @@ func (f *events) Pickle(pickle *messages.Pickle) {
}{
"TestCaseStarted",
f.scenarioLocation(pickle),
- timeNowFunc().UnixNano() / nanoSec,
+ utils.TimeNowFunc().UnixNano() / nanoSec,
})
if len(pickle.Steps) == 0 {
@@ -58,14 +63,14 @@ func (f *events) Pickle(pickle *messages.Pickle) {
}{
"TestCaseFinished",
f.scenarioLocation(pickle),
- timeNowFunc().UnixNano() / nanoSec,
+ utils.TimeNowFunc().UnixNano() / nanoSec,
"undefined",
})
}
}
-func (f *events) TestRunStarted() {
- f.basefmt.TestRunStarted()
+func (f *eventsFormatter) TestRunStarted() {
+ f.Basefmt.TestRunStarted()
f.lock.Lock()
defer f.lock.Unlock()
@@ -78,13 +83,13 @@ func (f *events) TestRunStarted() {
}{
"TestRunStarted",
spec,
- timeNowFunc().UnixNano() / nanoSec,
+ utils.TimeNowFunc().UnixNano() / nanoSec,
f.suiteName,
})
}
-func (f *events) Feature(ft *messages.GherkinDocument, p string, c []byte) {
- f.basefmt.Feature(ft, p, c)
+func (f *eventsFormatter) Feature(ft *messages.GherkinDocument, p string, c []byte) {
+ f.Basefmt.Feature(ft, p, c)
f.lock.Lock()
defer f.lock.Unlock()
@@ -100,23 +105,23 @@ func (f *events) Feature(ft *messages.GherkinDocument, p string, c []byte) {
})
}
-func (f *events) Summary() {
+func (f *eventsFormatter) Summary() {
// @TODO: determine status
status := passed
- f.storage.mustGetPickleStepResultsByStatus(failed)
+ f.storage.MustGetPickleStepResultsByStatus(failed)
- if len(f.storage.mustGetPickleStepResultsByStatus(failed)) > 0 {
+ if len(f.storage.MustGetPickleStepResultsByStatus(failed)) > 0 {
status = failed
- } else if len(f.storage.mustGetPickleStepResultsByStatus(passed)) == 0 {
- if len(f.storage.mustGetPickleStepResultsByStatus(undefined)) > len(f.storage.mustGetPickleStepResultsByStatus(pending)) {
+ } else if len(f.storage.MustGetPickleStepResultsByStatus(passed)) == 0 {
+ if len(f.storage.MustGetPickleStepResultsByStatus(undefined)) > len(f.storage.MustGetPickleStepResultsByStatus(pending)) {
status = undefined
} else {
status = pending
}
}
- snips := f.snippets()
+ snips := f.Snippets()
if len(snips) > 0 {
snips = "You can implement step definitions for undefined steps with these snippets:\n" + snips
}
@@ -130,20 +135,20 @@ func (f *events) Summary() {
}{
"TestRunFinished",
status.String(),
- timeNowFunc().UnixNano() / nanoSec,
+ utils.TimeNowFunc().UnixNano() / nanoSec,
snips,
"", // @TODO not sure that could be correctly implemented
})
}
-func (f *events) step(pickle *messages.Pickle, pickleStep *messages.Pickle_PickleStep) {
- feature := f.storage.mustGetFeature(pickle.Uri)
- pickleStepResult := f.storage.mustGetPickleStepResult(pickleStep.Id)
- step := feature.findStep(pickleStep.AstNodeIds[0])
+func (f *eventsFormatter) step(pickle *messages.Pickle, pickleStep *messages.Pickle_PickleStep) {
+ feature := f.storage.MustGetFeature(pickle.Uri)
+ pickleStepResult := f.storage.MustGetPickleStepResult(pickleStep.Id)
+ step := feature.FindStep(pickleStep.AstNodeIds[0])
var errMsg string
- if pickleStepResult.err != nil {
- errMsg = pickleStepResult.err.Error()
+ if pickleStepResult.Err != nil {
+ errMsg = pickleStepResult.Err.Error()
}
f.event(&struct {
Event string `json:"event"`
@@ -154,7 +159,7 @@ func (f *events) step(pickle *messages.Pickle, pickleStep *messages.Pickle_Pickl
}{
"TestStepFinished",
fmt.Sprintf("%s:%d", pickle.Uri, step.Location.Line),
- timeNowFunc().UnixNano() / nanoSec,
+ utils.TimeNowFunc().UnixNano() / nanoSec,
pickleStepResult.Status.String(),
errMsg,
})
@@ -162,17 +167,11 @@ func (f *events) step(pickle *messages.Pickle, pickleStep *messages.Pickle_Pickl
if isLastStep(pickle, pickleStep) {
var status string
- pickleStepResults := f.storage.mustGetPickleStepResultsByPickleID(pickle.Id)
+ pickleStepResults := f.storage.MustGetPickleStepResultsByPickleID(pickle.Id)
for _, stepResult := range pickleStepResults {
switch stepResult.Status {
- case passed:
- status = passed.String()
- case failed:
- status = failed.String()
- case undefined:
- status = undefined.String()
- case pending:
- status = pending.String()
+ case passed, failed, undefined, pending:
+ status = stepResult.Status.String()
}
}
@@ -184,20 +183,20 @@ func (f *events) step(pickle *messages.Pickle, pickleStep *messages.Pickle_Pickl
}{
"TestCaseFinished",
f.scenarioLocation(pickle),
- timeNowFunc().UnixNano() / nanoSec,
+ utils.TimeNowFunc().UnixNano() / nanoSec,
status,
})
}
}
-func (f *events) Defined(pickle *messages.Pickle, pickleStep *messages.Pickle_PickleStep, def *StepDefinition) {
- f.basefmt.Defined(pickle, pickleStep, def)
+func (f *eventsFormatter) Defined(pickle *messages.Pickle, pickleStep *messages.Pickle_PickleStep, def *models.StepDefinition) {
+ f.Basefmt.Defined(pickle, pickleStep, def)
f.lock.Lock()
defer f.lock.Unlock()
- feature := f.storage.mustGetFeature(pickle.Uri)
- step := feature.findStep(pickleStep.AstNodeIds[0])
+ feature := f.storage.MustGetFeature(pickle.Uri)
+ step := feature.FindStep(pickleStep.AstNodeIds[0])
if def != nil {
m := def.Expr.FindStringSubmatchIndex(pickleStep.Text)[2:]
@@ -222,7 +221,7 @@ func (f *events) Defined(pickle *messages.Pickle, pickleStep *messages.Pickle_Pi
}{
"StepDefinitionFound",
fmt.Sprintf("%s:%d", pickle.Uri, step.Location.Line),
- def.definitionID(),
+ DefinitionID(def),
args,
})
}
@@ -234,12 +233,12 @@ func (f *events) Defined(pickle *messages.Pickle, pickleStep *messages.Pickle_Pi
}{
"TestStepStarted",
fmt.Sprintf("%s:%d", pickle.Uri, step.Location.Line),
- timeNowFunc().UnixNano() / nanoSec,
+ utils.TimeNowFunc().UnixNano() / nanoSec,
})
}
-func (f *events) Passed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
- f.basefmt.Passed(pickle, step, match)
+func (f *eventsFormatter) Passed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *models.StepDefinition) {
+ f.Basefmt.Passed(pickle, step, match)
f.lock.Lock()
defer f.lock.Unlock()
@@ -247,8 +246,8 @@ func (f *events) Passed(pickle *messages.Pickle, step *messages.Pickle_PickleSte
f.step(pickle, step)
}
-func (f *events) Skipped(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
- f.basefmt.Skipped(pickle, step, match)
+func (f *eventsFormatter) Skipped(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *models.StepDefinition) {
+ f.Basefmt.Skipped(pickle, step, match)
f.lock.Lock()
defer f.lock.Unlock()
@@ -256,8 +255,8 @@ func (f *events) Skipped(pickle *messages.Pickle, step *messages.Pickle_PickleSt
f.step(pickle, step)
}
-func (f *events) Undefined(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
- f.basefmt.Undefined(pickle, step, match)
+func (f *eventsFormatter) Undefined(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *models.StepDefinition) {
+ f.Basefmt.Undefined(pickle, step, match)
f.lock.Lock()
defer f.lock.Unlock()
@@ -265,8 +264,8 @@ func (f *events) Undefined(pickle *messages.Pickle, step *messages.Pickle_Pickle
f.step(pickle, step)
}
-func (f *events) Failed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition, err error) {
- f.basefmt.Failed(pickle, step, match, err)
+func (f *eventsFormatter) Failed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *models.StepDefinition, err error) {
+ f.Basefmt.Failed(pickle, step, match, err)
f.lock.Lock()
defer f.lock.Unlock()
@@ -274,8 +273,8 @@ func (f *events) Failed(pickle *messages.Pickle, step *messages.Pickle_PickleSte
f.step(pickle, step)
}
-func (f *events) Pending(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
- f.basefmt.Pending(pickle, step, match)
+func (f *eventsFormatter) Pending(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *models.StepDefinition) {
+ f.Basefmt.Pending(pickle, step, match)
f.lock.Lock()
defer f.lock.Unlock()
@@ -283,15 +282,19 @@ func (f *events) Pending(pickle *messages.Pickle, step *messages.Pickle_PickleSt
f.step(pickle, step)
}
-func (f *events) scenarioLocation(pickle *messages.Pickle) string {
- feature := f.storage.mustGetFeature(pickle.Uri)
- scenario := feature.findScenario(pickle.AstNodeIds[0])
+func (f *eventsFormatter) scenarioLocation(pickle *messages.Pickle) string {
+ feature := f.storage.MustGetFeature(pickle.Uri)
+ scenario := feature.FindScenario(pickle.AstNodeIds[0])
line := scenario.Location.Line
if len(pickle.AstNodeIds) == 2 {
- _, row := feature.findExample(pickle.AstNodeIds[1])
+ _, row := feature.FindExample(pickle.AstNodeIds[1])
line = row.Location.Line
}
return fmt.Sprintf("%s:%d", pickle.Uri, line)
}
+
+func isLastStep(pickle *messages.Pickle, step *messages.Pickle_PickleStep) bool {
+ return pickle.Steps[len(pickle.Steps)-1].Id == step.Id
+}
diff --git a/fmt_junit.go b/internal/formatters/fmt_junit.go
similarity index 78%
rename from fmt_junit.go
rename to internal/formatters/fmt_junit.go
index ec38cb2..8beab0d 100644
--- a/fmt_junit.go
+++ b/internal/formatters/fmt_junit.go
@@ -1,4 +1,4 @@
-package godog
+package formatters
import (
"encoding/xml"
@@ -8,18 +8,22 @@ import (
"sort"
"strconv"
"time"
+
+ "github.com/cucumber/godog/formatters"
+ "github.com/cucumber/godog/internal/utils"
)
func init() {
- Format("junit", "Prints junit compatible xml to stdout", junitFunc)
+ formatters.Format("junit", "Prints junit compatible xml to stdout", JUnitFormatterFunc)
}
-func junitFunc(suite string, out io.Writer) Formatter {
- return &junitFormatter{basefmt: newBaseFmt(suite, out)}
+// JUnitFormatterFunc implements the FormatterFunc for the junit formatter
+func JUnitFormatterFunc(suite string, out io.Writer) formatters.Formatter {
+ return &junitFormatter{Basefmt: NewBaseFmt(suite, out)}
}
type junitFormatter struct {
- *basefmt
+ *Basefmt
}
func (f *junitFormatter) Summary() {
@@ -41,20 +45,20 @@ func junitTimeDuration(from, to time.Time) string {
return strconv.FormatFloat(to.Sub(from).Seconds(), 'f', -1, 64)
}
-func (f *junitFormatter) buildJUNITPackageSuite() junitPackageSuite {
- features := f.storage.mustGetFeatures()
+func (f *junitFormatter) buildJUNITPackageSuite() JunitPackageSuite {
+ features := f.storage.MustGetFeatures()
sort.Sort(sortFeaturesByName(features))
- testRunStartedAt := f.storage.mustGetTestRunStarted().StartedAt
+ testRunStartedAt := f.storage.MustGetTestRunStarted().StartedAt
- suite := junitPackageSuite{
+ suite := JunitPackageSuite{
Name: f.suiteName,
TestSuites: make([]*junitTestSuite, len(features)),
- Time: junitTimeDuration(testRunStartedAt, timeNowFunc()),
+ Time: junitTimeDuration(testRunStartedAt, utils.TimeNowFunc()),
}
for idx, feature := range features {
- pickles := f.storage.mustGetPickles(feature.Uri)
+ pickles := f.storage.MustGetPickles(feature.Uri)
sort.Sort(sortPicklesByID(pickles))
ts := junitTestSuite{
@@ -74,7 +78,7 @@ func (f *junitFormatter) buildJUNITPackageSuite() junitPackageSuite {
for idx, pickle := range pickles {
tc := junitTestCase{}
- pickleResult := f.storage.mustGetPickleResult(pickle.Id)
+ pickleResult := f.storage.MustGetPickleResult(pickle.Id)
if idx == 0 {
firstPickleStartedAt = pickleResult.StartedAt
@@ -84,8 +88,8 @@ func (f *junitFormatter) buildJUNITPackageSuite() junitPackageSuite {
if len(pickle.Steps) > 0 {
lastStep := pickle.Steps[len(pickle.Steps)-1]
- lastPickleStepResult := f.storage.mustGetPickleStepResult(lastStep.Id)
- lastPickleFinishedAt = lastPickleStepResult.finishedAt
+ lastPickleStepResult := f.storage.MustGetPickleStepResult(lastStep.Id)
+ lastPickleFinishedAt = lastPickleStepResult.FinishedAt
}
tc.Time = junitTimeDuration(pickleResult.StartedAt, lastPickleFinishedAt)
@@ -99,9 +103,9 @@ func (f *junitFormatter) buildJUNITPackageSuite() junitPackageSuite {
ts.Tests++
suite.Tests++
- pickleStepResults := f.storage.mustGetPickleStepResultsByPickleID(pickle.Id)
+ pickleStepResults := f.storage.MustGetPickleStepResultsByPickleID(pickle.Id)
for _, stepResult := range pickleStepResults {
- pickleStep := f.storage.mustGetPickleStep(stepResult.PickleStepID)
+ pickleStep := f.storage.MustGetPickleStep(stepResult.PickleStepID)
switch stepResult.Status {
case passed:
@@ -109,7 +113,7 @@ func (f *junitFormatter) buildJUNITPackageSuite() junitPackageSuite {
case failed:
tc.Status = failed.String()
tc.Failure = &junitFailure{
- Message: fmt.Sprintf("Step %s: %s", pickleStep.Text, stepResult.err),
+ Message: fmt.Sprintf("Step %s: %s", pickleStep.Text, stepResult.Err),
}
case skipped:
tc.Error = append(tc.Error, &junitError{
@@ -182,7 +186,8 @@ type junitTestSuite struct {
TestCases []*junitTestCase
}
-type junitPackageSuite struct {
+// JunitPackageSuite ...
+type JunitPackageSuite struct {
XMLName xml.Name `xml:"testsuites"`
Name string `xml:"name,attr"`
Tests int `xml:"tests,attr"`
diff --git a/fmt_output_test.go b/internal/formatters/fmt_output_test.go
similarity index 99%
rename from fmt_output_test.go
rename to internal/formatters/fmt_output_test.go
index 329676e..5a9d689 100644
--- a/fmt_output_test.go
+++ b/internal/formatters/fmt_output_test.go
@@ -1,4 +1,4 @@
-package godog_test
+package formatters_test
import (
"bytes"
diff --git a/fmt_pretty.go b/internal/formatters/fmt_pretty.go
similarity index 80%
rename from fmt_pretty.go
rename to internal/formatters/fmt_pretty.go
index b5bef37..db6ddf6 100644
--- a/fmt_pretty.go
+++ b/internal/formatters/fmt_pretty.go
@@ -1,4 +1,4 @@
-package godog
+package formatters
import (
"fmt"
@@ -11,26 +11,29 @@ import (
"github.com/cucumber/messages-go/v10"
"github.com/cucumber/godog/colors"
+ "github.com/cucumber/godog/formatters"
+ "github.com/cucumber/godog/internal/models"
)
func init() {
- Format("pretty", "Prints every feature with runtime statuses.", prettyFunc)
+ formatters.Format("pretty", "Prints every feature with runtime statuses.", PrettyFormatterFunc)
}
-func prettyFunc(suite string, out io.Writer) Formatter {
- return &pretty{basefmt: newBaseFmt(suite, out)}
+// PrettyFormatterFunc implements the FormatterFunc for the pretty formatter
+func PrettyFormatterFunc(suite string, out io.Writer) formatters.Formatter {
+ return &pretty{Basefmt: NewBaseFmt(suite, out)}
}
var outlinePlaceholderRegexp = regexp.MustCompile("<[^>]+>")
// a built in default pretty formatter
type pretty struct {
- *basefmt
+ *Basefmt
firstFeature *bool
}
func (f *pretty) TestRunStarted() {
- f.basefmt.TestRunStarted()
+ f.Basefmt.TestRunStarted()
f.lock.Lock()
defer f.lock.Unlock()
@@ -48,7 +51,7 @@ func (f *pretty) Feature(gd *messages.GherkinDocument, p string, c []byte) {
*f.firstFeature = false
f.lock.Unlock()
- f.basefmt.Feature(gd, p, c)
+ f.Basefmt.Feature(gd, p, c)
f.lock.Lock()
defer f.lock.Unlock()
@@ -58,7 +61,7 @@ func (f *pretty) Feature(gd *messages.GherkinDocument, p string, c []byte) {
// Pickle takes a gherkin node for formatting
func (f *pretty) Pickle(pickle *messages.Pickle) {
- f.basefmt.Pickle(pickle)
+ f.Basefmt.Pickle(pickle)
f.lock.Lock()
defer f.lock.Unlock()
@@ -69,8 +72,8 @@ func (f *pretty) Pickle(pickle *messages.Pickle) {
}
}
-func (f *pretty) Passed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
- f.basefmt.Passed(pickle, step, match)
+func (f *pretty) Passed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *models.StepDefinition) {
+ f.Basefmt.Passed(pickle, step, match)
f.lock.Lock()
defer f.lock.Unlock()
@@ -78,8 +81,8 @@ func (f *pretty) Passed(pickle *messages.Pickle, step *messages.Pickle_PickleSte
f.printStep(pickle, step)
}
-func (f *pretty) Skipped(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
- f.basefmt.Skipped(pickle, step, match)
+func (f *pretty) Skipped(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *models.StepDefinition) {
+ f.Basefmt.Skipped(pickle, step, match)
f.lock.Lock()
defer f.lock.Unlock()
@@ -87,8 +90,8 @@ func (f *pretty) Skipped(pickle *messages.Pickle, step *messages.Pickle_PickleSt
f.printStep(pickle, step)
}
-func (f *pretty) Undefined(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
- f.basefmt.Undefined(pickle, step, match)
+func (f *pretty) Undefined(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *models.StepDefinition) {
+ f.Basefmt.Undefined(pickle, step, match)
f.lock.Lock()
defer f.lock.Unlock()
@@ -96,8 +99,8 @@ func (f *pretty) Undefined(pickle *messages.Pickle, step *messages.Pickle_Pickle
f.printStep(pickle, step)
}
-func (f *pretty) Failed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition, err error) {
- f.basefmt.Failed(pickle, step, match, err)
+func (f *pretty) Failed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *models.StepDefinition, err error) {
+ f.Basefmt.Failed(pickle, step, match, err)
f.lock.Lock()
defer f.lock.Unlock()
@@ -105,8 +108,8 @@ func (f *pretty) Failed(pickle *messages.Pickle, step *messages.Pickle_PickleSte
f.printStep(pickle, step)
}
-func (f *pretty) Pending(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
- f.basefmt.Pending(pickle, step, match)
+func (f *pretty) Pending(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *models.StepDefinition) {
+ f.Basefmt.Pending(pickle, step, match)
f.lock.Lock()
defer f.lock.Unlock()
@@ -132,9 +135,9 @@ func keywordAndName(keyword, name string) string {
}
func (f *pretty) scenarioLengths(pickle *messages.Pickle) (scenarioHeaderLength int, maxLength int) {
- feature := f.storage.mustGetFeature(pickle.Uri)
- astScenario := feature.findScenario(pickle.AstNodeIds[0])
- astBackground := feature.findBackground(pickle.AstNodeIds[0])
+ feature := f.storage.MustGetFeature(pickle.Uri)
+ astScenario := feature.FindScenario(pickle.AstNodeIds[0])
+ astBackground := feature.FindBackground(pickle.AstNodeIds[0])
scenarioHeaderLength = f.lengthPickle(astScenario.Keyword, astScenario.Name)
maxLength = f.longestStep(astScenario.Steps, scenarioHeaderLength)
@@ -147,16 +150,16 @@ func (f *pretty) scenarioLengths(pickle *messages.Pickle) (scenarioHeaderLength
}
func (f *pretty) printScenarioHeader(pickle *messages.Pickle, astScenario *messages.GherkinDocument_Feature_Scenario, spaceFilling int) {
- feature := f.storage.mustGetFeature(pickle.Uri)
+ feature := f.storage.MustGetFeature(pickle.Uri)
text := s(f.indent) + keywordAndName(astScenario.Keyword, astScenario.Name)
text += s(spaceFilling) + line(feature.Uri, astScenario.Location)
fmt.Fprintln(f.out, "\n"+text)
}
func (f *pretty) printUndefinedPickle(pickle *messages.Pickle) {
- feature := f.storage.mustGetFeature(pickle.Uri)
- astScenario := feature.findScenario(pickle.AstNodeIds[0])
- astBackground := feature.findBackground(pickle.AstNodeIds[0])
+ feature := f.storage.MustGetFeature(pickle.Uri)
+ astScenario := feature.FindScenario(pickle.AstNodeIds[0])
+ astBackground := feature.FindBackground(pickle.AstNodeIds[0])
scenarioHeaderLength, maxLength := f.scenarioLengths(pickle)
@@ -170,7 +173,7 @@ func (f *pretty) printUndefinedPickle(pickle *messages.Pickle) {
// do not print scenario headers and examples multiple times
if len(astScenario.Examples) > 0 {
- exampleTable, exampleRow := feature.findExample(pickle.AstNodeIds[1])
+ exampleTable, exampleRow := feature.FindExample(pickle.AstNodeIds[1])
firstExampleRow := exampleTable.TableBody[0].Id == exampleRow.Id
firstExamplesTable := astScenario.Examples[0].Location.Line == exampleTable.Location.Line
@@ -197,45 +200,45 @@ func (f *pretty) printUndefinedPickle(pickle *messages.Pickle) {
// Summary sumarize the feature formatter output
func (f *pretty) Summary() {
- failedStepResults := f.storage.mustGetPickleStepResultsByStatus(failed)
+ failedStepResults := f.storage.MustGetPickleStepResultsByStatus(failed)
if len(failedStepResults) > 0 {
fmt.Fprintln(f.out, "\n--- "+red("Failed steps:")+"\n")
sort.Sort(sortPickleStepResultsByPickleStepID(failedStepResults))
for _, fail := range failedStepResults {
- pickle := f.storage.mustGetPickle(fail.PickleID)
- pickleStep := f.storage.mustGetPickleStep(fail.PickleStepID)
- feature := f.storage.mustGetFeature(pickle.Uri)
+ pickle := f.storage.MustGetPickle(fail.PickleID)
+ pickleStep := f.storage.MustGetPickleStep(fail.PickleStepID)
+ feature := f.storage.MustGetFeature(pickle.Uri)
- astScenario := feature.findScenario(pickle.AstNodeIds[0])
+ astScenario := feature.FindScenario(pickle.AstNodeIds[0])
scenarioDesc := fmt.Sprintf("%s: %s", astScenario.Keyword, pickle.Name)
- astStep := feature.findStep(pickleStep.AstNodeIds[0])
+ astStep := feature.FindStep(pickleStep.AstNodeIds[0])
stepDesc := strings.TrimSpace(astStep.Keyword) + " " + pickleStep.Text
fmt.Fprintln(f.out, s(f.indent)+red(scenarioDesc)+line(feature.Uri, astScenario.Location))
fmt.Fprintln(f.out, s(f.indent*2)+red(stepDesc)+line(feature.Uri, astStep.Location))
- fmt.Fprintln(f.out, s(f.indent*3)+red("Error: ")+redb(fmt.Sprintf("%+v", fail.err))+"\n")
+ fmt.Fprintln(f.out, s(f.indent*3)+red("Error: ")+redb(fmt.Sprintf("%+v", fail.Err))+"\n")
}
}
- f.basefmt.Summary()
+ f.Basefmt.Summary()
}
func (f *pretty) printOutlineExample(pickle *messages.Pickle, backgroundSteps int) {
var errorMsg string
var clr = green
- feature := f.storage.mustGetFeature(pickle.Uri)
- astScenario := feature.findScenario(pickle.AstNodeIds[0])
+ feature := f.storage.MustGetFeature(pickle.Uri)
+ astScenario := feature.FindScenario(pickle.AstNodeIds[0])
scenarioHeaderLength, maxLength := f.scenarioLengths(pickle)
- exampleTable, exampleRow := feature.findExample(pickle.AstNodeIds[1])
+ exampleTable, exampleRow := feature.FindExample(pickle.AstNodeIds[1])
printExampleHeader := exampleTable.TableBody[0].Id == exampleRow.Id
firstExamplesTable := astScenario.Examples[0].Location.Line == exampleTable.Location.Line
- pickleStepResults := f.storage.mustGetPickleStepResultsByPickleID(pickle.Id)
+ pickleStepResults := f.storage.MustGetPickleStepResultsByPickleID(pickle.Id)
firstExecutedScenarioStep := len(pickleStepResults) == backgroundSteps+1
if firstExamplesTable && printExampleHeader && firstExecutedScenarioStep {
@@ -257,10 +260,10 @@ func (f *pretty) printOutlineExample(pickle *messages.Pickle, backgroundSteps in
// determine example row status
switch {
case result.Status == failed:
- errorMsg = result.err.Error()
- clr = result.Status.clr()
+ errorMsg = result.Err.Error()
+ clr = result.Status.Color()
case result.Status == undefined || result.Status == pending:
- clr = result.Status.clr()
+ clr = result.Status.Color()
case result.Status == skipped && clr == nil:
clr = cyan
}
@@ -268,11 +271,11 @@ func (f *pretty) printOutlineExample(pickle *messages.Pickle, backgroundSteps in
if firstExamplesTable && printExampleHeader {
// in first example, we need to print steps
- pickleStep := f.storage.mustGetPickleStep(result.PickleStepID)
- astStep := feature.findStep(pickleStep.AstNodeIds[0])
+ pickleStep := f.storage.MustGetPickleStep(result.PickleStepID)
+ astStep := feature.FindStep(pickleStep.AstNodeIds[0])
var text = ""
- if result.def != nil {
+ if result.Def != nil {
if m := outlinePlaceholderRegexp.FindAllStringIndex(astStep.Text, -1); len(m) > 0 {
var pos int
for i := 0; i < len(m); i++ {
@@ -290,7 +293,7 @@ func (f *pretty) printOutlineExample(pickle *messages.Pickle, backgroundSteps in
stepLength := f.lengthPickleStep(astStep.Keyword, astStep.Text)
text += s(maxLength - stepLength)
- text += " " + blackb("# "+result.def.definitionID())
+ text += " " + blackb("# "+DefinitionID(result.Def))
}
// print the step outline
@@ -340,10 +343,10 @@ func (f *pretty) printTableHeader(row *messages.GherkinDocument_Feature_TableRow
}
func (f *pretty) printStep(pickle *messages.Pickle, pickleStep *messages.Pickle_PickleStep) {
- feature := f.storage.mustGetFeature(pickle.Uri)
- astBackground := feature.findBackground(pickle.AstNodeIds[0])
- astScenario := feature.findScenario(pickle.AstNodeIds[0])
- astStep := feature.findStep(pickleStep.AstNodeIds[0])
+ feature := f.storage.MustGetFeature(pickle.Uri)
+ astBackground := feature.FindBackground(pickle.AstNodeIds[0])
+ astScenario := feature.FindScenario(pickle.AstNodeIds[0])
+ astStep := feature.FindStep(pickleStep.AstNodeIds[0])
var astBackgroundStep bool
var firstExecutedBackgroundStep bool
@@ -360,7 +363,7 @@ func (f *pretty) printStep(pickle *messages.Pickle, pickleStep *messages.Pickle_
}
}
- firstPickle := feature.pickles[0].Id == pickle.Id
+ firstPickle := feature.Pickles[0].Id == pickle.Id
if astBackgroundStep && !firstPickle {
return
@@ -383,11 +386,11 @@ func (f *pretty) printStep(pickle *messages.Pickle, pickleStep *messages.Pickle_
f.printScenarioHeader(pickle, astScenario, maxLength-scenarioHeaderLength)
}
- pickleStepResult := f.storage.mustGetPickleStepResult(pickleStep.Id)
- text := s(f.indent*2) + pickleStepResult.Status.clr()(strings.TrimSpace(astStep.Keyword)) + " " + pickleStepResult.Status.clr()(pickleStep.Text)
- if pickleStepResult.def != nil {
+ pickleStepResult := f.storage.MustGetPickleStepResult(pickleStep.Id)
+ text := s(f.indent*2) + pickleStepResult.Status.Color()(strings.TrimSpace(astStep.Keyword)) + " " + pickleStepResult.Status.Color()(pickleStep.Text)
+ if pickleStepResult.Def != nil {
text += s(maxLength - stepLength + 1)
- text += blackb("# " + pickleStepResult.def.definitionID())
+ text += blackb("# " + DefinitionID(pickleStepResult.Def))
}
fmt.Fprintln(f.out, text)
@@ -399,8 +402,8 @@ func (f *pretty) printStep(pickle *messages.Pickle, pickleStep *messages.Pickle_
f.printDocString(docString)
}
- if pickleStepResult.err != nil {
- fmt.Fprintln(f.out, s(f.indent*2)+redb(fmt.Sprintf("%+v", pickleStepResult.err)))
+ if pickleStepResult.Err != nil {
+ fmt.Fprintln(f.out, s(f.indent*2)+redb(fmt.Sprintf("%+v", pickleStepResult.Err)))
}
if pickleStepResult.Status == pending {
diff --git a/fmt_progress.go b/internal/formatters/fmt_progress.go
similarity index 63%
rename from fmt_progress.go
rename to internal/formatters/fmt_progress.go
index 78c9d8d..3f626fd 100644
--- a/fmt_progress.go
+++ b/internal/formatters/fmt_progress.go
@@ -1,4 +1,4 @@
-package godog
+package formatters
import (
"fmt"
@@ -8,23 +8,27 @@ import (
"strings"
"github.com/cucumber/messages-go/v10"
+
+ "github.com/cucumber/godog/formatters"
+ "github.com/cucumber/godog/internal/models"
)
func init() {
- Format("progress", "Prints a character per step.", progressFunc)
+ formatters.Format("progress", "Prints a character per step.", ProgressFormatterFunc)
}
-func progressFunc(suite string, out io.Writer) Formatter {
+// ProgressFormatterFunc implements the FormatterFunc for the progress formatter
+func ProgressFormatterFunc(suite string, out io.Writer) formatters.Formatter {
steps := 0
return &progress{
- basefmt: newBaseFmt(suite, out),
+ Basefmt: NewBaseFmt(suite, out),
stepsPerRow: 70,
steps: &steps,
}
}
type progress struct {
- *basefmt
+ *Basefmt
stepsPerRow int
steps *int
}
@@ -41,20 +45,20 @@ func (f *progress) Summary() {
var failedStepsOutput []string
- failedSteps := f.storage.mustGetPickleStepResultsByStatus(failed)
+ failedSteps := f.storage.MustGetPickleStepResultsByStatus(failed)
sort.Sort(sortPickleStepResultsByPickleStepID(failedSteps))
for _, sr := range failedSteps {
if sr.Status == failed {
- pickle := f.storage.mustGetPickle(sr.PickleID)
- pickleStep := f.storage.mustGetPickleStep(sr.PickleStepID)
- feature := f.storage.mustGetFeature(pickle.Uri)
+ pickle := f.storage.MustGetPickle(sr.PickleID)
+ pickleStep := f.storage.MustGetPickleStep(sr.PickleStepID)
+ feature := f.storage.MustGetFeature(pickle.Uri)
- sc := feature.findScenario(pickle.AstNodeIds[0])
+ sc := feature.FindScenario(pickle.AstNodeIds[0])
scenarioDesc := fmt.Sprintf("%s: %s", sc.Keyword, pickle.Name)
scenarioLine := fmt.Sprintf("%s:%d", pickle.Uri, sc.Location.Line)
- step := feature.findStep(pickleStep.AstNodeIds[0])
+ step := feature.FindStep(pickleStep.AstNodeIds[0])
stepDesc := strings.TrimSpace(step.Keyword) + " " + pickleStep.Text
stepLine := fmt.Sprintf("%s:%d", pickle.Uri, step.Location.Line)
@@ -62,7 +66,7 @@ func (f *progress) Summary() {
failedStepsOutput,
s(2)+red(scenarioDesc)+blackb(" # "+scenarioLine),
s(4)+red(stepDesc)+blackb(" # "+stepLine),
- s(6)+red("Error: ")+redb(fmt.Sprintf("%+v", sr.err)),
+ s(6)+red("Error: ")+redb(fmt.Sprintf("%+v", sr.Err)),
"",
)
}
@@ -74,11 +78,11 @@ func (f *progress) Summary() {
}
fmt.Fprintln(f.out, "")
- f.basefmt.Summary()
+ f.Basefmt.Summary()
}
func (f *progress) step(pickleStepID string) {
- pickleStepResult := f.storage.mustGetPickleStepResult(pickleStepID)
+ pickleStepResult := f.storage.MustGetPickleStepResult(pickleStepID)
switch pickleStepResult.Status {
case passed:
@@ -100,8 +104,8 @@ func (f *progress) step(pickleStepID string) {
}
}
-func (f *progress) Passed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
- f.basefmt.Passed(pickle, step, match)
+func (f *progress) Passed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *models.StepDefinition) {
+ f.Basefmt.Passed(pickle, step, match)
f.lock.Lock()
defer f.lock.Unlock()
@@ -109,8 +113,8 @@ func (f *progress) Passed(pickle *messages.Pickle, step *messages.Pickle_PickleS
f.step(step.Id)
}
-func (f *progress) Skipped(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
- f.basefmt.Skipped(pickle, step, match)
+func (f *progress) Skipped(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *models.StepDefinition) {
+ f.Basefmt.Skipped(pickle, step, match)
f.lock.Lock()
defer f.lock.Unlock()
@@ -118,8 +122,8 @@ func (f *progress) Skipped(pickle *messages.Pickle, step *messages.Pickle_Pickle
f.step(step.Id)
}
-func (f *progress) Undefined(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
- f.basefmt.Undefined(pickle, step, match)
+func (f *progress) Undefined(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *models.StepDefinition) {
+ f.Basefmt.Undefined(pickle, step, match)
f.lock.Lock()
defer f.lock.Unlock()
@@ -127,8 +131,8 @@ func (f *progress) Undefined(pickle *messages.Pickle, step *messages.Pickle_Pick
f.step(step.Id)
}
-func (f *progress) Failed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition, err error) {
- f.basefmt.Failed(pickle, step, match, err)
+func (f *progress) Failed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *models.StepDefinition, err error) {
+ f.Basefmt.Failed(pickle, step, match, err)
f.lock.Lock()
defer f.lock.Unlock()
@@ -136,8 +140,8 @@ func (f *progress) Failed(pickle *messages.Pickle, step *messages.Pickle_PickleS
f.step(step.Id)
}
-func (f *progress) Pending(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
- f.basefmt.Pending(pickle, step, match)
+func (f *progress) Pending(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *models.StepDefinition) {
+ f.Basefmt.Pending(pickle, step, match)
f.lock.Lock()
defer f.lock.Unlock()
diff --git a/formatter-tests/cucumber/empty b/internal/formatters/formatter-tests/cucumber/empty
similarity index 100%
rename from formatter-tests/cucumber/empty
rename to internal/formatters/formatter-tests/cucumber/empty
diff --git a/formatter-tests/cucumber/empty_with_description b/internal/formatters/formatter-tests/cucumber/empty_with_description
similarity index 100%
rename from formatter-tests/cucumber/empty_with_description
rename to internal/formatters/formatter-tests/cucumber/empty_with_description
diff --git a/formatter-tests/cucumber/empty_with_single_scenario_without_steps b/internal/formatters/formatter-tests/cucumber/empty_with_single_scenario_without_steps
similarity index 100%
rename from formatter-tests/cucumber/empty_with_single_scenario_without_steps
rename to internal/formatters/formatter-tests/cucumber/empty_with_single_scenario_without_steps
diff --git a/formatter-tests/cucumber/empty_with_single_scenario_without_steps_and_description b/internal/formatters/formatter-tests/cucumber/empty_with_single_scenario_without_steps_and_description
similarity index 100%
rename from formatter-tests/cucumber/empty_with_single_scenario_without_steps_and_description
rename to internal/formatters/formatter-tests/cucumber/empty_with_single_scenario_without_steps_and_description
diff --git a/formatter-tests/cucumber/scenario_outline b/internal/formatters/formatter-tests/cucumber/scenario_outline
similarity index 100%
rename from formatter-tests/cucumber/scenario_outline
rename to internal/formatters/formatter-tests/cucumber/scenario_outline
diff --git a/formatter-tests/cucumber/scenario_with_background b/internal/formatters/formatter-tests/cucumber/scenario_with_background
similarity index 100%
rename from formatter-tests/cucumber/scenario_with_background
rename to internal/formatters/formatter-tests/cucumber/scenario_with_background
diff --git a/formatter-tests/cucumber/scenario_without_steps_with_background b/internal/formatters/formatter-tests/cucumber/scenario_without_steps_with_background
similarity index 100%
rename from formatter-tests/cucumber/scenario_without_steps_with_background
rename to internal/formatters/formatter-tests/cucumber/scenario_without_steps_with_background
diff --git a/formatter-tests/cucumber/single_scenario_with_passing_step b/internal/formatters/formatter-tests/cucumber/single_scenario_with_passing_step
similarity index 100%
rename from formatter-tests/cucumber/single_scenario_with_passing_step
rename to internal/formatters/formatter-tests/cucumber/single_scenario_with_passing_step
diff --git a/formatter-tests/cucumber/some_scenarions_including_failing b/internal/formatters/formatter-tests/cucumber/some_scenarions_including_failing
similarity index 100%
rename from formatter-tests/cucumber/some_scenarions_including_failing
rename to internal/formatters/formatter-tests/cucumber/some_scenarions_including_failing
diff --git a/formatter-tests/cucumber/two_scenarios_with_background_fail b/internal/formatters/formatter-tests/cucumber/two_scenarios_with_background_fail
similarity index 100%
rename from formatter-tests/cucumber/two_scenarios_with_background_fail
rename to internal/formatters/formatter-tests/cucumber/two_scenarios_with_background_fail
diff --git a/formatter-tests/cucumber/with_few_empty_scenarios b/internal/formatters/formatter-tests/cucumber/with_few_empty_scenarios
similarity index 100%
rename from formatter-tests/cucumber/with_few_empty_scenarios
rename to internal/formatters/formatter-tests/cucumber/with_few_empty_scenarios
diff --git a/formatter-tests/events/empty b/internal/formatters/formatter-tests/events/empty
similarity index 100%
rename from formatter-tests/events/empty
rename to internal/formatters/formatter-tests/events/empty
diff --git a/formatter-tests/events/empty_with_description b/internal/formatters/formatter-tests/events/empty_with_description
similarity index 100%
rename from formatter-tests/events/empty_with_description
rename to internal/formatters/formatter-tests/events/empty_with_description
diff --git a/formatter-tests/events/empty_with_single_scenario_without_steps b/internal/formatters/formatter-tests/events/empty_with_single_scenario_without_steps
similarity index 100%
rename from formatter-tests/events/empty_with_single_scenario_without_steps
rename to internal/formatters/formatter-tests/events/empty_with_single_scenario_without_steps
diff --git a/formatter-tests/events/empty_with_single_scenario_without_steps_and_description b/internal/formatters/formatter-tests/events/empty_with_single_scenario_without_steps_and_description
similarity index 100%
rename from formatter-tests/events/empty_with_single_scenario_without_steps_and_description
rename to internal/formatters/formatter-tests/events/empty_with_single_scenario_without_steps_and_description
diff --git a/formatter-tests/events/scenario_outline b/internal/formatters/formatter-tests/events/scenario_outline
similarity index 83%
rename from formatter-tests/events/scenario_outline
rename to internal/formatters/formatter-tests/events/scenario_outline
index bfb9dc6..44a976d 100644
--- a/formatter-tests/events/scenario_outline
+++ b/internal/formatters/formatter-tests/events/scenario_outline
@@ -1,57 +1,57 @@
{"event":"TestRunStarted","version":"0.1.0","timestamp":-6795364578871,"suite":"events"}
{"event":"TestSource","location":"formatter-tests/features/scenario_outline.feature:2","source":"@outline @tag\nFeature: outline\n\n @scenario\n Scenario Outline: outline\n Given passing step\n When passing step\n Then odd \u003codd\u003e and even \u003ceven\u003e number\n\n @tagged\n Examples: tagged\n | odd | even |\n | 1 | 2 |\n | 2 | 0 |\n | 3 | 11 |\n\n @tag2\n Examples:\n | odd | even |\n | 1 | 14 |\n | 3 | 9 |\n"}
{"event":"TestCaseStarted","location":"formatter-tests/features/scenario_outline.feature:13","timestamp":-6795364578871}
-{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:6","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
+{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:6","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/scenario_outline.feature:6","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/scenario_outline.feature:6","timestamp":-6795364578871,"status":"passed"}
-{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:7","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
+{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:7","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/scenario_outline.feature:7","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/scenario_outline.feature:7","timestamp":-6795364578871,"status":"passed"}
-{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:8","definition_id":"fmt_output_test.go:103 -\u003e github.com/cucumber/godog_test.oddEvenStepDef","arguments":[[4,5],[5,15]]}
+{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:8","definition_id":"fmt_output_test.go:103 -\u003e github.com/cucumber/godog/internal/formatters_test.oddEvenStepDef","arguments":[[4,5],[5,15]]}
{"event":"TestStepStarted","location":"formatter-tests/features/scenario_outline.feature:8","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/scenario_outline.feature:8","timestamp":-6795364578871,"status":"passed"}
{"event":"TestCaseFinished","location":"formatter-tests/features/scenario_outline.feature:13","timestamp":-6795364578871,"status":"passed"}
{"event":"TestCaseStarted","location":"formatter-tests/features/scenario_outline.feature:14","timestamp":-6795364578871}
-{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:6","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
+{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:6","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/scenario_outline.feature:6","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/scenario_outline.feature:6","timestamp":-6795364578871,"status":"passed"}
-{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:7","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
+{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:7","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/scenario_outline.feature:7","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/scenario_outline.feature:7","timestamp":-6795364578871,"status":"passed"}
-{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:8","definition_id":"fmt_output_test.go:103 -\u003e github.com/cucumber/godog_test.oddEvenStepDef","arguments":[[4,5],[5,15]]}
+{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:8","definition_id":"fmt_output_test.go:103 -\u003e github.com/cucumber/godog/internal/formatters_test.oddEvenStepDef","arguments":[[4,5],[5,15]]}
{"event":"TestStepStarted","location":"formatter-tests/features/scenario_outline.feature:8","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/scenario_outline.feature:8","timestamp":-6795364578871,"status":"failed","summary":"2 is not odd"}
{"event":"TestCaseFinished","location":"formatter-tests/features/scenario_outline.feature:14","timestamp":-6795364578871,"status":"failed"}
{"event":"TestCaseStarted","location":"formatter-tests/features/scenario_outline.feature:15","timestamp":-6795364578871}
-{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:6","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
+{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:6","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/scenario_outline.feature:6","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/scenario_outline.feature:6","timestamp":-6795364578871,"status":"passed"}
-{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:7","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
+{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:7","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/scenario_outline.feature:7","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/scenario_outline.feature:7","timestamp":-6795364578871,"status":"passed"}
-{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:8","definition_id":"fmt_output_test.go:103 -\u003e github.com/cucumber/godog_test.oddEvenStepDef","arguments":[[4,5],[5,15]]}
+{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:8","definition_id":"fmt_output_test.go:103 -\u003e github.com/cucumber/godog/internal/formatters_test.oddEvenStepDef","arguments":[[4,5],[5,15]]}
{"event":"TestStepStarted","location":"formatter-tests/features/scenario_outline.feature:8","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/scenario_outline.feature:8","timestamp":-6795364578871,"status":"failed","summary":"11 is not even"}
{"event":"TestCaseFinished","location":"formatter-tests/features/scenario_outline.feature:15","timestamp":-6795364578871,"status":"failed"}
{"event":"TestCaseStarted","location":"formatter-tests/features/scenario_outline.feature:20","timestamp":-6795364578871}
-{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:6","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
+{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:6","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/scenario_outline.feature:6","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/scenario_outline.feature:6","timestamp":-6795364578871,"status":"passed"}
-{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:7","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
+{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:7","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/scenario_outline.feature:7","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/scenario_outline.feature:7","timestamp":-6795364578871,"status":"passed"}
-{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:8","definition_id":"fmt_output_test.go:103 -\u003e github.com/cucumber/godog_test.oddEvenStepDef","arguments":[[4,5],[5,15]]}
+{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:8","definition_id":"fmt_output_test.go:103 -\u003e github.com/cucumber/godog/internal/formatters_test.oddEvenStepDef","arguments":[[4,5],[5,15]]}
{"event":"TestStepStarted","location":"formatter-tests/features/scenario_outline.feature:8","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/scenario_outline.feature:8","timestamp":-6795364578871,"status":"passed"}
{"event":"TestCaseFinished","location":"formatter-tests/features/scenario_outline.feature:20","timestamp":-6795364578871,"status":"passed"}
{"event":"TestCaseStarted","location":"formatter-tests/features/scenario_outline.feature:21","timestamp":-6795364578871}
-{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:6","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
+{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:6","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/scenario_outline.feature:6","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/scenario_outline.feature:6","timestamp":-6795364578871,"status":"passed"}
-{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:7","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
+{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:7","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/scenario_outline.feature:7","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/scenario_outline.feature:7","timestamp":-6795364578871,"status":"passed"}
-{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:8","definition_id":"fmt_output_test.go:103 -\u003e github.com/cucumber/godog_test.oddEvenStepDef","arguments":[[4,5],[5,15]]}
+{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:8","definition_id":"fmt_output_test.go:103 -\u003e github.com/cucumber/godog/internal/formatters_test.oddEvenStepDef","arguments":[[4,5],[5,15]]}
{"event":"TestStepStarted","location":"formatter-tests/features/scenario_outline.feature:8","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/scenario_outline.feature:8","timestamp":-6795364578871,"status":"failed","summary":"9 is not even"}
{"event":"TestCaseFinished","location":"formatter-tests/features/scenario_outline.feature:21","timestamp":-6795364578871,"status":"failed"}
diff --git a/formatter-tests/events/scenario_with_background b/internal/formatters/formatter-tests/events/scenario_with_background
similarity index 84%
rename from formatter-tests/events/scenario_with_background
rename to internal/formatters/formatter-tests/events/scenario_with_background
index 629e76a..0b15684 100644
--- a/formatter-tests/events/scenario_with_background
+++ b/internal/formatters/formatter-tests/events/scenario_with_background
@@ -1,16 +1,16 @@
{"event":"TestRunStarted","version":"0.1.0","timestamp":-6795364578871,"suite":"events"}
{"event":"TestSource","location":"formatter-tests/features/scenario_with_background.feature:1","source":"Feature: single scenario with background\n\n Background: named\n Given passing step\n And passing step\n\n Scenario: scenario\n When passing step\n Then passing step\n"}
{"event":"TestCaseStarted","location":"formatter-tests/features/scenario_with_background.feature:7","timestamp":-6795364578871}
-{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_with_background.feature:4","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
+{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_with_background.feature:4","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/scenario_with_background.feature:4","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/scenario_with_background.feature:4","timestamp":-6795364578871,"status":"passed"}
-{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_with_background.feature:5","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
+{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_with_background.feature:5","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/scenario_with_background.feature:5","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/scenario_with_background.feature:5","timestamp":-6795364578871,"status":"passed"}
-{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_with_background.feature:8","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
+{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_with_background.feature:8","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/scenario_with_background.feature:8","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/scenario_with_background.feature:8","timestamp":-6795364578871,"status":"passed"}
-{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_with_background.feature:9","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
+{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_with_background.feature:9","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/scenario_with_background.feature:9","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/scenario_with_background.feature:9","timestamp":-6795364578871,"status":"passed"}
{"event":"TestCaseFinished","location":"formatter-tests/features/scenario_with_background.feature:7","timestamp":-6795364578871,"status":"passed"}
diff --git a/formatter-tests/events/scenario_without_steps_with_background b/internal/formatters/formatter-tests/events/scenario_without_steps_with_background
similarity index 100%
rename from formatter-tests/events/scenario_without_steps_with_background
rename to internal/formatters/formatter-tests/events/scenario_without_steps_with_background
diff --git a/formatter-tests/events/single_scenario_with_passing_step b/internal/formatters/formatter-tests/events/single_scenario_with_passing_step
similarity index 90%
rename from formatter-tests/events/single_scenario_with_passing_step
rename to internal/formatters/formatter-tests/events/single_scenario_with_passing_step
index b487e60..bac2f33 100644
--- a/formatter-tests/events/single_scenario_with_passing_step
+++ b/internal/formatters/formatter-tests/events/single_scenario_with_passing_step
@@ -1,7 +1,7 @@
{"event":"TestRunStarted","version":"0.1.0","timestamp":-6795364578871,"suite":"events"}
{"event":"TestSource","location":"formatter-tests/features/single_scenario_with_passing_step.feature:1","source":"Feature: single passing scenario\n describes\n a single scenario\n feature\n\n Scenario: one step passing\n Given a passing step\n"}
{"event":"TestCaseStarted","location":"formatter-tests/features/single_scenario_with_passing_step.feature:6","timestamp":-6795364578871}
-{"event":"StepDefinitionFound","location":"formatter-tests/features/single_scenario_with_passing_step.feature:7","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
+{"event":"StepDefinitionFound","location":"formatter-tests/features/single_scenario_with_passing_step.feature:7","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/single_scenario_with_passing_step.feature:7","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/single_scenario_with_passing_step.feature:7","timestamp":-6795364578871,"status":"passed"}
{"event":"TestCaseFinished","location":"formatter-tests/features/single_scenario_with_passing_step.feature:6","timestamp":-6795364578871,"status":"passed"}
diff --git a/formatter-tests/events/some_scenarions_including_failing b/internal/formatters/formatter-tests/events/some_scenarions_including_failing
similarity index 86%
rename from formatter-tests/events/some_scenarions_including_failing
rename to internal/formatters/formatter-tests/events/some_scenarions_including_failing
index d4bee67..d220e14 100644
--- a/formatter-tests/events/some_scenarions_including_failing
+++ b/internal/formatters/formatter-tests/events/some_scenarions_including_failing
@@ -1,28 +1,28 @@
{"event":"TestRunStarted","version":"0.1.0","timestamp":-6795364578871,"suite":"events"}
{"event":"TestSource","location":"formatter-tests/features/some_scenarions_including_failing.feature:1","source":"Feature: some scenarios\n\n Scenario: failing\n Given passing step\n When failing step\n Then passing step\n\n Scenario: pending\n When pending step\n Then passing step\n\n Scenario: undefined\n When undefined\n Then passing step\n"}
{"event":"TestCaseStarted","location":"formatter-tests/features/some_scenarions_including_failing.feature:3","timestamp":-6795364578871}
-{"event":"StepDefinitionFound","location":"formatter-tests/features/some_scenarions_including_failing.feature:4","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
+{"event":"StepDefinitionFound","location":"formatter-tests/features/some_scenarions_including_failing.feature:4","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/some_scenarions_including_failing.feature:4","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/some_scenarions_including_failing.feature:4","timestamp":-6795364578871,"status":"passed"}
-{"event":"StepDefinitionFound","location":"formatter-tests/features/some_scenarions_including_failing.feature:5","definition_id":"fmt_output_test.go:117 -\u003e github.com/cucumber/godog_test.failingStepDef","arguments":[]}
+{"event":"StepDefinitionFound","location":"formatter-tests/features/some_scenarions_including_failing.feature:5","definition_id":"fmt_output_test.go:117 -\u003e github.com/cucumber/godog/internal/formatters_test.failingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/some_scenarions_including_failing.feature:5","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/some_scenarions_including_failing.feature:5","timestamp":-6795364578871,"status":"failed","summary":"step failed"}
-{"event":"StepDefinitionFound","location":"formatter-tests/features/some_scenarions_including_failing.feature:6","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
+{"event":"StepDefinitionFound","location":"formatter-tests/features/some_scenarions_including_failing.feature:6","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/some_scenarions_including_failing.feature:6","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/some_scenarions_including_failing.feature:6","timestamp":-6795364578871,"status":"skipped"}
{"event":"TestCaseFinished","location":"formatter-tests/features/some_scenarions_including_failing.feature:3","timestamp":-6795364578871,"status":"failed"}
{"event":"TestCaseStarted","location":"formatter-tests/features/some_scenarions_including_failing.feature:8","timestamp":-6795364578871}
-{"event":"StepDefinitionFound","location":"formatter-tests/features/some_scenarions_including_failing.feature:9","definition_id":"fmt_output_test.go:115 -\u003e github.com/cucumber/godog_test.pendingStepDef","arguments":[]}
+{"event":"StepDefinitionFound","location":"formatter-tests/features/some_scenarions_including_failing.feature:9","definition_id":"fmt_output_test.go:115 -\u003e github.com/cucumber/godog/internal/formatters_test.pendingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/some_scenarions_including_failing.feature:9","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/some_scenarions_including_failing.feature:9","timestamp":-6795364578871,"status":"pending"}
-{"event":"StepDefinitionFound","location":"formatter-tests/features/some_scenarions_including_failing.feature:10","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
+{"event":"StepDefinitionFound","location":"formatter-tests/features/some_scenarions_including_failing.feature:10","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/some_scenarions_including_failing.feature:10","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/some_scenarions_including_failing.feature:10","timestamp":-6795364578871,"status":"skipped"}
{"event":"TestCaseFinished","location":"formatter-tests/features/some_scenarions_including_failing.feature:8","timestamp":-6795364578871,"status":"pending"}
{"event":"TestCaseStarted","location":"formatter-tests/features/some_scenarions_including_failing.feature:12","timestamp":-6795364578871}
{"event":"TestStepStarted","location":"formatter-tests/features/some_scenarions_including_failing.feature:13","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/some_scenarions_including_failing.feature:13","timestamp":-6795364578871,"status":"undefined"}
-{"event":"StepDefinitionFound","location":"formatter-tests/features/some_scenarions_including_failing.feature:14","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
+{"event":"StepDefinitionFound","location":"formatter-tests/features/some_scenarions_including_failing.feature:14","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/some_scenarions_including_failing.feature:14","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/some_scenarions_including_failing.feature:14","timestamp":-6795364578871,"status":"skipped"}
{"event":"TestCaseFinished","location":"formatter-tests/features/some_scenarions_including_failing.feature:12","timestamp":-6795364578871,"status":"undefined"}
diff --git a/formatter-tests/events/two_scenarios_with_background_fail b/internal/formatters/formatter-tests/events/two_scenarios_with_background_fail
similarity index 83%
rename from formatter-tests/events/two_scenarios_with_background_fail
rename to internal/formatters/formatter-tests/events/two_scenarios_with_background_fail
index faa05b6..633f996 100644
--- a/formatter-tests/events/two_scenarios_with_background_fail
+++ b/internal/formatters/formatter-tests/events/two_scenarios_with_background_fail
@@ -1,27 +1,27 @@
{"event":"TestRunStarted","version":"0.1.0","timestamp":-6795364578871,"suite":"events"}
{"event":"TestSource","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:1","source":"Feature: two scenarios with background fail\n\n Background:\n Given passing step\n And failing step\n\n Scenario: one\n When passing step\n Then passing step\n\n Scenario: two\n Then passing step\n"}
{"event":"TestCaseStarted","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:7","timestamp":-6795364578871}
-{"event":"StepDefinitionFound","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:4","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
+{"event":"StepDefinitionFound","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:4","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:4","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:4","timestamp":-6795364578871,"status":"passed"}
-{"event":"StepDefinitionFound","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:5","definition_id":"fmt_output_test.go:117 -\u003e github.com/cucumber/godog_test.failingStepDef","arguments":[]}
+{"event":"StepDefinitionFound","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:5","definition_id":"fmt_output_test.go:117 -\u003e github.com/cucumber/godog/internal/formatters_test.failingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:5","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:5","timestamp":-6795364578871,"status":"failed","summary":"step failed"}
-{"event":"StepDefinitionFound","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:8","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
+{"event":"StepDefinitionFound","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:8","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:8","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:8","timestamp":-6795364578871,"status":"skipped"}
-{"event":"StepDefinitionFound","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:9","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
+{"event":"StepDefinitionFound","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:9","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:9","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:9","timestamp":-6795364578871,"status":"skipped"}
{"event":"TestCaseFinished","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:7","timestamp":-6795364578871,"status":"failed"}
{"event":"TestCaseStarted","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:11","timestamp":-6795364578871}
-{"event":"StepDefinitionFound","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:4","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
+{"event":"StepDefinitionFound","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:4","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:4","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:4","timestamp":-6795364578871,"status":"passed"}
-{"event":"StepDefinitionFound","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:5","definition_id":"fmt_output_test.go:117 -\u003e github.com/cucumber/godog_test.failingStepDef","arguments":[]}
+{"event":"StepDefinitionFound","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:5","definition_id":"fmt_output_test.go:117 -\u003e github.com/cucumber/godog/internal/formatters_test.failingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:5","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:5","timestamp":-6795364578871,"status":"failed","summary":"step failed"}
-{"event":"StepDefinitionFound","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:12","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
+{"event":"StepDefinitionFound","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:12","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:12","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:12","timestamp":-6795364578871,"status":"skipped"}
{"event":"TestCaseFinished","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:11","timestamp":-6795364578871,"status":"failed"}
diff --git a/formatter-tests/events/with_few_empty_scenarios b/internal/formatters/formatter-tests/events/with_few_empty_scenarios
similarity index 100%
rename from formatter-tests/events/with_few_empty_scenarios
rename to internal/formatters/formatter-tests/events/with_few_empty_scenarios
diff --git a/formatter-tests/features/empty.feature b/internal/formatters/formatter-tests/features/empty.feature
similarity index 100%
rename from formatter-tests/features/empty.feature
rename to internal/formatters/formatter-tests/features/empty.feature
diff --git a/formatter-tests/features/empty_with_description.feature b/internal/formatters/formatter-tests/features/empty_with_description.feature
similarity index 100%
rename from formatter-tests/features/empty_with_description.feature
rename to internal/formatters/formatter-tests/features/empty_with_description.feature
diff --git a/formatter-tests/features/empty_with_single_scenario_without_steps.feature b/internal/formatters/formatter-tests/features/empty_with_single_scenario_without_steps.feature
similarity index 100%
rename from formatter-tests/features/empty_with_single_scenario_without_steps.feature
rename to internal/formatters/formatter-tests/features/empty_with_single_scenario_without_steps.feature
diff --git a/formatter-tests/features/empty_with_single_scenario_without_steps_and_description.feature b/internal/formatters/formatter-tests/features/empty_with_single_scenario_without_steps_and_description.feature
similarity index 100%
rename from formatter-tests/features/empty_with_single_scenario_without_steps_and_description.feature
rename to internal/formatters/formatter-tests/features/empty_with_single_scenario_without_steps_and_description.feature
diff --git a/formatter-tests/features/scenario_outline.feature b/internal/formatters/formatter-tests/features/scenario_outline.feature
similarity index 100%
rename from formatter-tests/features/scenario_outline.feature
rename to internal/formatters/formatter-tests/features/scenario_outline.feature
diff --git a/formatter-tests/features/scenario_with_background.feature b/internal/formatters/formatter-tests/features/scenario_with_background.feature
similarity index 100%
rename from formatter-tests/features/scenario_with_background.feature
rename to internal/formatters/formatter-tests/features/scenario_with_background.feature
diff --git a/formatter-tests/features/scenario_without_steps_with_background.feature b/internal/formatters/formatter-tests/features/scenario_without_steps_with_background.feature
similarity index 100%
rename from formatter-tests/features/scenario_without_steps_with_background.feature
rename to internal/formatters/formatter-tests/features/scenario_without_steps_with_background.feature
diff --git a/formatter-tests/features/single_scenario_with_passing_step.feature b/internal/formatters/formatter-tests/features/single_scenario_with_passing_step.feature
similarity index 100%
rename from formatter-tests/features/single_scenario_with_passing_step.feature
rename to internal/formatters/formatter-tests/features/single_scenario_with_passing_step.feature
diff --git a/formatter-tests/features/some_scenarions_including_failing.feature b/internal/formatters/formatter-tests/features/some_scenarions_including_failing.feature
similarity index 100%
rename from formatter-tests/features/some_scenarions_including_failing.feature
rename to internal/formatters/formatter-tests/features/some_scenarions_including_failing.feature
diff --git a/formatter-tests/features/two_scenarios_with_background_fail.feature b/internal/formatters/formatter-tests/features/two_scenarios_with_background_fail.feature
similarity index 100%
rename from formatter-tests/features/two_scenarios_with_background_fail.feature
rename to internal/formatters/formatter-tests/features/two_scenarios_with_background_fail.feature
diff --git a/formatter-tests/features/with_few_empty_scenarios.feature b/internal/formatters/formatter-tests/features/with_few_empty_scenarios.feature
similarity index 100%
rename from formatter-tests/features/with_few_empty_scenarios.feature
rename to internal/formatters/formatter-tests/features/with_few_empty_scenarios.feature
diff --git a/formatter-tests/junit/empty b/internal/formatters/formatter-tests/junit/empty
similarity index 100%
rename from formatter-tests/junit/empty
rename to internal/formatters/formatter-tests/junit/empty
diff --git a/formatter-tests/junit/empty_with_description b/internal/formatters/formatter-tests/junit/empty_with_description
similarity index 100%
rename from formatter-tests/junit/empty_with_description
rename to internal/formatters/formatter-tests/junit/empty_with_description
diff --git a/formatter-tests/junit/empty_with_single_scenario_without_steps b/internal/formatters/formatter-tests/junit/empty_with_single_scenario_without_steps
similarity index 100%
rename from formatter-tests/junit/empty_with_single_scenario_without_steps
rename to internal/formatters/formatter-tests/junit/empty_with_single_scenario_without_steps
diff --git a/formatter-tests/junit/empty_with_single_scenario_without_steps_and_description b/internal/formatters/formatter-tests/junit/empty_with_single_scenario_without_steps_and_description
similarity index 100%
rename from formatter-tests/junit/empty_with_single_scenario_without_steps_and_description
rename to internal/formatters/formatter-tests/junit/empty_with_single_scenario_without_steps_and_description
diff --git a/formatter-tests/junit/scenario_outline b/internal/formatters/formatter-tests/junit/scenario_outline
similarity index 100%
rename from formatter-tests/junit/scenario_outline
rename to internal/formatters/formatter-tests/junit/scenario_outline
diff --git a/formatter-tests/junit/scenario_with_background b/internal/formatters/formatter-tests/junit/scenario_with_background
similarity index 100%
rename from formatter-tests/junit/scenario_with_background
rename to internal/formatters/formatter-tests/junit/scenario_with_background
diff --git a/formatter-tests/junit/scenario_without_steps_with_background b/internal/formatters/formatter-tests/junit/scenario_without_steps_with_background
similarity index 100%
rename from formatter-tests/junit/scenario_without_steps_with_background
rename to internal/formatters/formatter-tests/junit/scenario_without_steps_with_background
diff --git a/formatter-tests/junit/single_scenario_with_passing_step b/internal/formatters/formatter-tests/junit/single_scenario_with_passing_step
similarity index 100%
rename from formatter-tests/junit/single_scenario_with_passing_step
rename to internal/formatters/formatter-tests/junit/single_scenario_with_passing_step
diff --git a/formatter-tests/junit/some_scenarions_including_failing b/internal/formatters/formatter-tests/junit/some_scenarions_including_failing
similarity index 100%
rename from formatter-tests/junit/some_scenarions_including_failing
rename to internal/formatters/formatter-tests/junit/some_scenarions_including_failing
diff --git a/formatter-tests/junit/two_scenarios_with_background_fail b/internal/formatters/formatter-tests/junit/two_scenarios_with_background_fail
similarity index 100%
rename from formatter-tests/junit/two_scenarios_with_background_fail
rename to internal/formatters/formatter-tests/junit/two_scenarios_with_background_fail
diff --git a/formatter-tests/junit/with_few_empty_scenarios b/internal/formatters/formatter-tests/junit/with_few_empty_scenarios
similarity index 100%
rename from formatter-tests/junit/with_few_empty_scenarios
rename to internal/formatters/formatter-tests/junit/with_few_empty_scenarios
diff --git a/formatter-tests/pretty/empty b/internal/formatters/formatter-tests/pretty/empty
similarity index 100%
rename from formatter-tests/pretty/empty
rename to internal/formatters/formatter-tests/pretty/empty
diff --git a/formatter-tests/pretty/empty_with_description b/internal/formatters/formatter-tests/pretty/empty_with_description
similarity index 100%
rename from formatter-tests/pretty/empty_with_description
rename to internal/formatters/formatter-tests/pretty/empty_with_description
diff --git a/formatter-tests/pretty/empty_with_single_scenario_without_steps b/internal/formatters/formatter-tests/pretty/empty_with_single_scenario_without_steps
similarity index 100%
rename from formatter-tests/pretty/empty_with_single_scenario_without_steps
rename to internal/formatters/formatter-tests/pretty/empty_with_single_scenario_without_steps
diff --git a/formatter-tests/pretty/empty_with_single_scenario_without_steps_and_description b/internal/formatters/formatter-tests/pretty/empty_with_single_scenario_without_steps_and_description
similarity index 100%
rename from formatter-tests/pretty/empty_with_single_scenario_without_steps_and_description
rename to internal/formatters/formatter-tests/pretty/empty_with_single_scenario_without_steps_and_description
diff --git a/formatter-tests/pretty/scenario_outline b/internal/formatters/formatter-tests/pretty/scenario_outline
similarity index 92%
rename from formatter-tests/pretty/scenario_outline
rename to internal/formatters/formatter-tests/pretty/scenario_outline
index 372412e..aa50d28 100644
--- a/formatter-tests/pretty/scenario_outline
+++ b/internal/formatters/formatter-tests/pretty/scenario_outline
@@ -1,9 +1,9 @@
Feature: outline
Scenario Outline: outline # formatter-tests/features/scenario_outline.feature:5
- Given passing step # fmt_output_test.go:101 -> github.com/cucumber/godog_test.passingStepDef
- When passing step # fmt_output_test.go:101 -> github.com/cucumber/godog_test.passingStepDef
- Then odd and even number # fmt_output_test.go:103 -> github.com/cucumber/godog_test.oddEvenStepDef
+ Given passing step # fmt_output_test.go:101 -> github.com/cucumber/godog/internal/formatters_test.passingStepDef
+ When passing step # fmt_output_test.go:101 -> github.com/cucumber/godog/internal/formatters_test.passingStepDef
+ Then odd and even number # fmt_output_test.go:103 -> github.com/cucumber/godog/internal/formatters_test.oddEvenStepDef
Examples: tagged
| odd | even |
diff --git a/formatter-tests/pretty/scenario_with_background b/internal/formatters/formatter-tests/pretty/scenario_with_background
similarity index 57%
rename from formatter-tests/pretty/scenario_with_background
rename to internal/formatters/formatter-tests/pretty/scenario_with_background
index 22c75e3..c31b9a6 100644
--- a/formatter-tests/pretty/scenario_with_background
+++ b/internal/formatters/formatter-tests/pretty/scenario_with_background
@@ -1,12 +1,12 @@
Feature: single scenario with background
Background: named
- Given passing step # fmt_output_test.go:101 -> github.com/cucumber/godog_test.passingStepDef
- And passing step # fmt_output_test.go:101 -> github.com/cucumber/godog_test.passingStepDef
+ Given passing step # fmt_output_test.go:101 -> github.com/cucumber/godog/internal/formatters_test.passingStepDef
+ And passing step # fmt_output_test.go:101 -> github.com/cucumber/godog/internal/formatters_test.passingStepDef
Scenario: scenario # formatter-tests/features/scenario_with_background.feature:7
- When passing step # fmt_output_test.go:101 -> github.com/cucumber/godog_test.passingStepDef
- Then passing step # fmt_output_test.go:101 -> github.com/cucumber/godog_test.passingStepDef
+ When passing step # fmt_output_test.go:101 -> github.com/cucumber/godog/internal/formatters_test.passingStepDef
+ Then passing step # fmt_output_test.go:101 -> github.com/cucumber/godog/internal/formatters_test.passingStepDef
1 scenarios (1 passed)
4 steps (4 passed)
diff --git a/formatter-tests/pretty/scenario_without_steps_with_background b/internal/formatters/formatter-tests/pretty/scenario_without_steps_with_background
similarity index 100%
rename from formatter-tests/pretty/scenario_without_steps_with_background
rename to internal/formatters/formatter-tests/pretty/scenario_without_steps_with_background
diff --git a/formatter-tests/pretty/single_scenario_with_passing_step b/internal/formatters/formatter-tests/pretty/single_scenario_with_passing_step
similarity index 77%
rename from formatter-tests/pretty/single_scenario_with_passing_step
rename to internal/formatters/formatter-tests/pretty/single_scenario_with_passing_step
index d24086f..537a239 100644
--- a/formatter-tests/pretty/single_scenario_with_passing_step
+++ b/internal/formatters/formatter-tests/pretty/single_scenario_with_passing_step
@@ -4,7 +4,7 @@
feature
Scenario: one step passing # formatter-tests/features/single_scenario_with_passing_step.feature:6
- Given a passing step # fmt_output_test.go:101 -> github.com/cucumber/godog_test.passingStepDef
+ Given a passing step # fmt_output_test.go:101 -> github.com/cucumber/godog/internal/formatters_test.passingStepDef
1 scenarios (1 passed)
1 steps (1 passed)
diff --git a/formatter-tests/pretty/some_scenarions_including_failing b/internal/formatters/formatter-tests/pretty/some_scenarions_including_failing
similarity index 74%
rename from formatter-tests/pretty/some_scenarions_including_failing
rename to internal/formatters/formatter-tests/pretty/some_scenarions_including_failing
index 689e4d4..cbd7f83 100644
--- a/formatter-tests/pretty/some_scenarions_including_failing
+++ b/internal/formatters/formatter-tests/pretty/some_scenarions_including_failing
@@ -1,19 +1,19 @@
Feature: some scenarios
Scenario: failing # formatter-tests/features/some_scenarions_including_failing.feature:3
- Given passing step # fmt_output_test.go:101 -> github.com/cucumber/godog_test.passingStepDef
- When failing step # fmt_output_test.go:117 -> github.com/cucumber/godog_test.failingStepDef
+ Given passing step # fmt_output_test.go:101 -> github.com/cucumber/godog/internal/formatters_test.passingStepDef
+ When failing step # fmt_output_test.go:117 -> github.com/cucumber/godog/internal/formatters_test.failingStepDef
step failed
- Then passing step # fmt_output_test.go:101 -> github.com/cucumber/godog_test.passingStepDef
+ Then passing step # fmt_output_test.go:101 -> github.com/cucumber/godog/internal/formatters_test.passingStepDef
Scenario: pending # formatter-tests/features/some_scenarions_including_failing.feature:8
- When pending step # fmt_output_test.go:115 -> github.com/cucumber/godog_test.pendingStepDef
+ When pending step # fmt_output_test.go:115 -> github.com/cucumber/godog/internal/formatters_test.pendingStepDef
TODO: write pending definition
- Then passing step # fmt_output_test.go:101 -> github.com/cucumber/godog_test.passingStepDef
+ Then passing step # fmt_output_test.go:101 -> github.com/cucumber/godog/internal/formatters_test.passingStepDef
Scenario: undefined # formatter-tests/features/some_scenarions_including_failing.feature:12
When undefined
- Then passing step # fmt_output_test.go:101 -> github.com/cucumber/godog_test.passingStepDef
+ Then passing step # fmt_output_test.go:101 -> github.com/cucumber/godog/internal/formatters_test.passingStepDef
--- Failed steps:
diff --git a/formatter-tests/pretty/two_scenarios_with_background_fail b/internal/formatters/formatter-tests/pretty/two_scenarios_with_background_fail
similarity index 74%
rename from formatter-tests/pretty/two_scenarios_with_background_fail
rename to internal/formatters/formatter-tests/pretty/two_scenarios_with_background_fail
index 643c910..a41f2c3 100644
--- a/formatter-tests/pretty/two_scenarios_with_background_fail
+++ b/internal/formatters/formatter-tests/pretty/two_scenarios_with_background_fail
@@ -1,16 +1,16 @@
Feature: two scenarios with background fail
Background:
- Given passing step # fmt_output_test.go:101 -> github.com/cucumber/godog_test.passingStepDef
- And failing step # fmt_output_test.go:117 -> github.com/cucumber/godog_test.failingStepDef
+ Given passing step # fmt_output_test.go:101 -> github.com/cucumber/godog/internal/formatters_test.passingStepDef
+ And failing step # fmt_output_test.go:117 -> github.com/cucumber/godog/internal/formatters_test.failingStepDef
step failed
Scenario: one # formatter-tests/features/two_scenarios_with_background_fail.feature:7
- When passing step # fmt_output_test.go:101 -> github.com/cucumber/godog_test.passingStepDef
- Then passing step # fmt_output_test.go:101 -> github.com/cucumber/godog_test.passingStepDef
+ When passing step # fmt_output_test.go:101 -> github.com/cucumber/godog/internal/formatters_test.passingStepDef
+ Then passing step # fmt_output_test.go:101 -> github.com/cucumber/godog/internal/formatters_test.passingStepDef
Scenario: two # formatter-tests/features/two_scenarios_with_background_fail.feature:11
- Then passing step # fmt_output_test.go:101 -> github.com/cucumber/godog_test.passingStepDef
+ Then passing step # fmt_output_test.go:101 -> github.com/cucumber/godog/internal/formatters_test.passingStepDef
--- Failed steps:
diff --git a/formatter-tests/pretty/with_few_empty_scenarios b/internal/formatters/formatter-tests/pretty/with_few_empty_scenarios
similarity index 100%
rename from formatter-tests/pretty/with_few_empty_scenarios
rename to internal/formatters/formatter-tests/pretty/with_few_empty_scenarios
diff --git a/formatter-tests/progress/empty b/internal/formatters/formatter-tests/progress/empty
similarity index 100%
rename from formatter-tests/progress/empty
rename to internal/formatters/formatter-tests/progress/empty
diff --git a/formatter-tests/progress/empty_with_description b/internal/formatters/formatter-tests/progress/empty_with_description
similarity index 100%
rename from formatter-tests/progress/empty_with_description
rename to internal/formatters/formatter-tests/progress/empty_with_description
diff --git a/formatter-tests/progress/empty_with_single_scenario_without_steps b/internal/formatters/formatter-tests/progress/empty_with_single_scenario_without_steps
similarity index 100%
rename from formatter-tests/progress/empty_with_single_scenario_without_steps
rename to internal/formatters/formatter-tests/progress/empty_with_single_scenario_without_steps
diff --git a/formatter-tests/progress/empty_with_single_scenario_without_steps_and_description b/internal/formatters/formatter-tests/progress/empty_with_single_scenario_without_steps_and_description
similarity index 100%
rename from formatter-tests/progress/empty_with_single_scenario_without_steps_and_description
rename to internal/formatters/formatter-tests/progress/empty_with_single_scenario_without_steps_and_description
diff --git a/formatter-tests/progress/scenario_outline b/internal/formatters/formatter-tests/progress/scenario_outline
similarity index 100%
rename from formatter-tests/progress/scenario_outline
rename to internal/formatters/formatter-tests/progress/scenario_outline
diff --git a/formatter-tests/progress/scenario_with_background b/internal/formatters/formatter-tests/progress/scenario_with_background
similarity index 100%
rename from formatter-tests/progress/scenario_with_background
rename to internal/formatters/formatter-tests/progress/scenario_with_background
diff --git a/formatter-tests/progress/scenario_without_steps_with_background b/internal/formatters/formatter-tests/progress/scenario_without_steps_with_background
similarity index 100%
rename from formatter-tests/progress/scenario_without_steps_with_background
rename to internal/formatters/formatter-tests/progress/scenario_without_steps_with_background
diff --git a/formatter-tests/progress/single_scenario_with_passing_step b/internal/formatters/formatter-tests/progress/single_scenario_with_passing_step
similarity index 100%
rename from formatter-tests/progress/single_scenario_with_passing_step
rename to internal/formatters/formatter-tests/progress/single_scenario_with_passing_step
diff --git a/formatter-tests/progress/some_scenarions_including_failing b/internal/formatters/formatter-tests/progress/some_scenarions_including_failing
similarity index 100%
rename from formatter-tests/progress/some_scenarions_including_failing
rename to internal/formatters/formatter-tests/progress/some_scenarions_including_failing
diff --git a/formatter-tests/progress/two_scenarios_with_background_fail b/internal/formatters/formatter-tests/progress/two_scenarios_with_background_fail
similarity index 100%
rename from formatter-tests/progress/two_scenarios_with_background_fail
rename to internal/formatters/formatter-tests/progress/two_scenarios_with_background_fail
diff --git a/formatter-tests/progress/with_few_empty_scenarios b/internal/formatters/formatter-tests/progress/with_few_empty_scenarios
similarity index 100%
rename from formatter-tests/progress/with_few_empty_scenarios
rename to internal/formatters/formatter-tests/progress/with_few_empty_scenarios
diff --git a/undefined_snippets_gen.go b/internal/formatters/undefined_snippets_gen.go
similarity index 99%
rename from undefined_snippets_gen.go
rename to internal/formatters/undefined_snippets_gen.go
index 456b50d..b61dfd3 100644
--- a/undefined_snippets_gen.go
+++ b/internal/formatters/undefined_snippets_gen.go
@@ -1,4 +1,4 @@
-package godog
+package formatters
import (
"fmt"
diff --git a/internal/formatters/utils_test.go b/internal/formatters/utils_test.go
new file mode 100644
index 0000000..6463141
--- /dev/null
+++ b/internal/formatters/utils_test.go
@@ -0,0 +1,24 @@
+package formatters
+
+import (
+ "testing"
+ "time"
+
+ "github.com/cucumber/godog/internal/utils"
+)
+
+// this zeroes the time throughout whole test suite
+// and makes it easier to assert output
+// activated only when godog tests are being run
+func init() {
+ utils.TimeNowFunc = func() time.Time {
+ return time.Time{}
+ }
+}
+
+func TestTimeNowFunc(t *testing.T) {
+ now := utils.TimeNowFunc()
+ if !now.IsZero() {
+ t.Fatalf("expected zeroed time, but got: %s", now.Format(time.RFC3339))
+ }
+}
diff --git a/feature.go b/internal/models/feature.go
similarity index 69%
rename from feature.go
rename to internal/models/feature.go
index 9dcebd1..bf1c77b 100644
--- a/feature.go
+++ b/internal/models/feature.go
@@ -1,22 +1,20 @@
-package godog
+package models
import (
"github.com/cucumber/messages-go/v10"
)
-type feature struct {
+// Feature is an internal object to group together
+// the parsed gherkin document, the pickles and the
+// raw content.
+type Feature struct {
*messages.GherkinDocument
- pickles []*messages.Pickle
- content []byte
+ Pickles []*messages.Pickle
+ Content []byte
}
-type sortFeaturesByName []*feature
-
-func (s sortFeaturesByName) Len() int { return len(s) }
-func (s sortFeaturesByName) Less(i, j int) bool { return s[i].Feature.Name < s[j].Feature.Name }
-func (s sortFeaturesByName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
-
-func (f feature) findScenario(astScenarioID string) *messages.GherkinDocument_Feature_Scenario {
+// FindScenario ...
+func (f Feature) FindScenario(astScenarioID string) *messages.GherkinDocument_Feature_Scenario {
for _, child := range f.GherkinDocument.Feature.Children {
if sc := child.GetScenario(); sc != nil && sc.Id == astScenarioID {
return sc
@@ -26,7 +24,8 @@ func (f feature) findScenario(astScenarioID string) *messages.GherkinDocument_Fe
return nil
}
-func (f feature) findBackground(astScenarioID string) *messages.GherkinDocument_Feature_Background {
+// FindBackground ...
+func (f Feature) FindBackground(astScenarioID string) *messages.GherkinDocument_Feature_Background {
var bg *messages.GherkinDocument_Feature_Background
for _, child := range f.GherkinDocument.Feature.Children {
@@ -42,7 +41,8 @@ func (f feature) findBackground(astScenarioID string) *messages.GherkinDocument_
return nil
}
-func (f feature) findExample(exampleAstID string) (*messages.GherkinDocument_Feature_Scenario_Examples, *messages.GherkinDocument_Feature_TableRow) {
+// FindExample ...
+func (f Feature) FindExample(exampleAstID string) (*messages.GherkinDocument_Feature_Scenario_Examples, *messages.GherkinDocument_Feature_TableRow) {
for _, child := range f.GherkinDocument.Feature.Children {
if sc := child.GetScenario(); sc != nil {
for _, example := range sc.Examples {
@@ -58,7 +58,8 @@ func (f feature) findExample(exampleAstID string) (*messages.GherkinDocument_Fea
return nil, nil
}
-func (f feature) findStep(astStepID string) *messages.GherkinDocument_Feature_Step {
+// FindStep ...
+func (f Feature) FindStep(astStepID string) *messages.GherkinDocument_Feature_Step {
for _, child := range f.GherkinDocument.Feature.Children {
if sc := child.GetScenario(); sc != nil {
for _, step := range sc.GetSteps() {
diff --git a/internal/models/feature_test.go b/internal/models/feature_test.go
new file mode 100644
index 0000000..89fac1e
--- /dev/null
+++ b/internal/models/feature_test.go
@@ -0,0 +1,60 @@
+package models_test
+
+import (
+ "testing"
+
+ "github.com/cucumber/godog/internal/testutils"
+ "github.com/stretchr/testify/assert"
+)
+
+func Test_Find(t *testing.T) {
+ ft := testutils.BuildTestFeature(t)
+
+ t.Run("scenario", func(t *testing.T) {
+ sc := ft.FindScenario(ft.Pickles[0].AstNodeIds[0])
+ assert.NotNilf(t, sc, "expected scenario to not be nil")
+ })
+
+ t.Run("background", func(t *testing.T) {
+ bg := ft.FindBackground(ft.Pickles[0].AstNodeIds[0])
+ assert.NotNilf(t, bg, "expected background to not be nil")
+ })
+
+ t.Run("example", func(t *testing.T) {
+ example, row := ft.FindExample(ft.Pickles[1].AstNodeIds[1])
+ assert.NotNilf(t, example, "expected example to not be nil")
+ assert.NotNilf(t, row, "expected table row to not be nil")
+ })
+
+ t.Run("step", func(t *testing.T) {
+ for _, ps := range ft.Pickles[0].Steps {
+ step := ft.FindStep(ps.AstNodeIds[0])
+ assert.NotNilf(t, step, "expected step to not be nil")
+ }
+ })
+}
+
+func Test_NotFind(t *testing.T) {
+ ft := testutils.BuildTestFeature(t)
+
+ t.Run("scenario", func(t *testing.T) {
+ sc := ft.FindScenario("-")
+ assert.Nilf(t, sc, "expected scenario to be nil")
+ })
+
+ t.Run("background", func(t *testing.T) {
+ bg := ft.FindBackground("-")
+ assert.Nilf(t, bg, "expected background to be nil")
+ })
+
+ t.Run("example", func(t *testing.T) {
+ example, row := ft.FindExample("-")
+ assert.Nilf(t, example, "expected example to be nil")
+ assert.Nilf(t, row, "expected table row to be nil")
+ })
+
+ t.Run("step", func(t *testing.T) {
+ step := ft.FindStep("-")
+ assert.Nilf(t, step, "expected step to be nil")
+ })
+}
diff --git a/internal/models/results.go b/internal/models/results.go
new file mode 100644
index 0000000..461a5b8
--- /dev/null
+++ b/internal/models/results.go
@@ -0,0 +1,84 @@
+package models
+
+import (
+ "time"
+
+ "github.com/cucumber/godog/colors"
+ "github.com/cucumber/godog/internal/utils"
+)
+
+// TestRunStarted ...
+type TestRunStarted struct {
+ StartedAt time.Time
+}
+
+// PickleResult ...
+type PickleResult struct {
+ PickleID string
+ StartedAt time.Time
+}
+
+// PickleStepResult ...
+type PickleStepResult struct {
+ Status StepResultStatus
+ FinishedAt time.Time
+ Err error
+
+ PickleID string
+ PickleStepID string
+
+ Def *StepDefinition
+}
+
+// NewStepResult ...
+func NewStepResult(pickleID, pickleStepID string, match *StepDefinition) PickleStepResult {
+ return PickleStepResult{FinishedAt: utils.TimeNowFunc(), PickleID: pickleID, PickleStepID: pickleStepID, Def: match}
+}
+
+// StepResultStatus ...
+type StepResultStatus int
+
+const (
+ // Passed ...
+ Passed StepResultStatus = iota
+ // Failed ...
+ Failed
+ // Skipped ...
+ Skipped
+ // Undefined ...
+ Undefined
+ // Pending ...
+ Pending
+)
+
+// Color ...
+func (st StepResultStatus) Color() colors.ColorFunc {
+ switch st {
+ case Passed:
+ return colors.Green
+ case Failed:
+ return colors.Red
+ case Skipped:
+ return colors.Cyan
+ default:
+ return colors.Yellow
+ }
+}
+
+// String ...
+func (st StepResultStatus) String() string {
+ switch st {
+ case Passed:
+ return "passed"
+ case Failed:
+ return "failed"
+ case Skipped:
+ return "skipped"
+ case Undefined:
+ return "undefined"
+ case Pending:
+ return "pending"
+ default:
+ return "unknown"
+ }
+}
diff --git a/internal/models/results_test.go b/internal/models/results_test.go
new file mode 100644
index 0000000..2c74c63
--- /dev/null
+++ b/internal/models/results_test.go
@@ -0,0 +1,34 @@
+package models_test
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+
+ "github.com/cucumber/godog/colors"
+ "github.com/cucumber/godog/internal/models"
+)
+
+type stepResultStatusTestCase struct {
+ st models.StepResultStatus
+ str string
+ clr colors.ColorFunc
+}
+
+var stepResultStatusTestCases = []stepResultStatusTestCase{
+ {st: models.Passed, str: "passed", clr: colors.Green},
+ {st: models.Failed, str: "failed", clr: colors.Red},
+ {st: models.Skipped, str: "skipped", clr: colors.Cyan},
+ {st: models.Undefined, str: "undefined", clr: colors.Yellow},
+ {st: models.Pending, str: "pending", clr: colors.Yellow},
+ {st: -1, str: "unknown", clr: colors.Yellow},
+}
+
+func Test_StepResultStatus(t *testing.T) {
+ for _, tc := range stepResultStatusTestCases {
+ t.Run(tc.str, func(t *testing.T) {
+ assert.Equal(t, tc.str, tc.st.String())
+ assert.Equal(t, tc.clr(tc.str), tc.st.Color()(tc.str))
+ })
+ }
+}
diff --git a/stepdef.go b/internal/models/stepdef.go
similarity index 71%
rename from stepdef.go
rename to internal/models/stepdef.go
index 2f075c6..b78da05 100644
--- a/stepdef.go
+++ b/internal/models/stepdef.go
@@ -1,35 +1,15 @@
-package godog
+package models
import (
"fmt"
- "os"
- "path/filepath"
"reflect"
"regexp"
- "runtime"
"strconv"
- "strings"
"github.com/cucumber/messages-go/v10"
)
-var matchFuncDefRef = regexp.MustCompile(`\(([^\)]+)\)`)
-
-// Steps allows to nest steps
-// instead of returning an error in step func
-// it is possible to return combined steps:
-//
-// func multistep(name string) godog.Steps {
-// return godog.Steps{
-// fmt.Sprintf(`an user named "%s"`, name),
-// fmt.Sprintf(`user "%s" is authenticated`, name),
-// }
-// }
-//
-// These steps will be matched and executed in
-// sequential order. The first one which fails
-// will result in main step failure.
-type Steps []string
+var typeOfBytes = reflect.TypeOf([]byte(nil))
// StepDefinition is a registered step definition
// contains a StepHandler and regexp which
@@ -40,50 +20,21 @@ type Steps []string
// when step is matched and is either failed
// or successful
type StepDefinition struct {
- args []interface{}
- hv reflect.Value
- Expr *regexp.Regexp
- Handler interface{}
+ Args []interface{}
+ HandlerValue reflect.Value
+ Expr *regexp.Regexp
+ Handler interface{}
// multistep related
- nested bool
- undefined []string
+ Nested bool
+ Undefined []string
}
-func (sd *StepDefinition) definitionID() string {
- ptr := sd.hv.Pointer()
- f := runtime.FuncForPC(ptr)
- file, line := f.FileLine(ptr)
- dir := filepath.Dir(file)
-
- fn := strings.Replace(f.Name(), dir, "", -1)
- var parts []string
- for _, gr := range matchFuncDefRef.FindAllStringSubmatch(fn, -1) {
- parts = append(parts, strings.Trim(gr[1], "_."))
- }
- if len(parts) > 0 {
- // case when suite is a structure with methods
- fn = strings.Join(parts, ".")
- } else {
- // case when steps are just plain funcs
- fn = strings.Trim(fn, "_.")
- }
-
- if pkg := os.Getenv("GODOG_TESTED_PACKAGE"); len(pkg) > 0 {
- fn = strings.Replace(fn, pkg, "", 1)
- fn = strings.TrimLeft(fn, ".")
- fn = strings.Replace(fn, "..", ".", -1)
- }
-
- return fmt.Sprintf("%s:%d -> %s", filepath.Base(file), line, fn)
-}
-
-// run a step with the matched arguments using
-// reflect
-func (sd *StepDefinition) run() interface{} {
- typ := sd.hv.Type()
- if len(sd.args) < typ.NumIn() {
- return fmt.Errorf("func expects %d arguments, which is more than %d matched from step", typ.NumIn(), len(sd.args))
+// Run a step with the matched arguments using reflect
+func (sd *StepDefinition) Run() interface{} {
+ typ := sd.HandlerValue.Type()
+ if len(sd.Args) < typ.NumIn() {
+ return fmt.Errorf("func expects %d arguments, which is more than %d matched from step", typ.NumIn(), len(sd.Args))
}
var values []reflect.Value
for i := 0; i < typ.NumIn(); i++ {
@@ -166,7 +117,7 @@ func (sd *StepDefinition) run() interface{} {
}
values = append(values, reflect.ValueOf(float32(v)))
case reflect.Ptr:
- arg := sd.args[i]
+ arg := sd.Args[i]
switch param.Elem().String() {
case "messages.PickleStepArgument_PickleDocString":
if v, ok := arg.(*messages.PickleStepArgument); ok {
@@ -211,11 +162,11 @@ func (sd *StepDefinition) run() interface{} {
}
}
- return sd.hv.Call(values)[0].Interface()
+ return sd.HandlerValue.Call(values)[0].Interface()
}
func (sd *StepDefinition) shouldBeString(idx int) (string, error) {
- arg := sd.args[idx]
+ arg := sd.Args[idx]
s, ok := arg.(string)
if !ok {
return "", fmt.Errorf(`cannot convert argument %d: "%v" of type "%T" to string`, idx, arg, arg)
diff --git a/internal/models/stepdef_test.go b/internal/models/stepdef_test.go
new file mode 100644
index 0000000..e989000
--- /dev/null
+++ b/internal/models/stepdef_test.go
@@ -0,0 +1,107 @@
+package models_test
+
+import (
+ "reflect"
+ "strings"
+ "testing"
+
+ "github.com/cucumber/godog/internal/models"
+ "github.com/cucumber/messages-go/v10"
+)
+
+func TestShouldSupportIntTypes(t *testing.T) {
+ fn := func(a int64, b int32, c int16, d int8) error { return nil }
+
+ def := &models.StepDefinition{
+ Handler: fn,
+ HandlerValue: reflect.ValueOf(fn),
+ }
+
+ def.Args = []interface{}{"1", "1", "1", "1"}
+ if err := def.Run(); err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+
+ def.Args = []interface{}{"1", "1", "1", strings.Repeat("1", 9)}
+ if err := def.Run(); err == nil {
+ t.Fatalf("expected convertion fail for int8, but got none")
+ }
+}
+
+func TestShouldSupportFloatTypes(t *testing.T) {
+ fn := func(a float64, b float32) error { return nil }
+
+ def := &models.StepDefinition{
+ Handler: fn,
+ HandlerValue: reflect.ValueOf(fn),
+ }
+
+ def.Args = []interface{}{"1.1", "1.09"}
+ if err := def.Run(); err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+
+ def.Args = []interface{}{"1.08", strings.Repeat("1", 65) + ".67"}
+ if err := def.Run(); err == nil {
+ t.Fatalf("expected convertion fail for float32, but got none")
+ }
+}
+
+func TestShouldNotSupportOtherPointerTypesThanGherkin(t *testing.T) {
+ fn1 := func(a *int) error { return nil }
+ fn2 := func(a *messages.PickleStepArgument_PickleDocString) error { return nil }
+ fn3 := func(a *messages.PickleStepArgument_PickleTable) error { return nil }
+
+ def1 := &models.StepDefinition{Handler: fn1, HandlerValue: reflect.ValueOf(fn1), Args: []interface{}{(*int)(nil)}}
+ def2 := &models.StepDefinition{Handler: fn2, HandlerValue: reflect.ValueOf(fn2), Args: []interface{}{&messages.PickleStepArgument_PickleDocString{}}}
+ def3 := &models.StepDefinition{Handler: fn3, HandlerValue: reflect.ValueOf(fn3), Args: []interface{}{(*messages.PickleStepArgument_PickleTable)(nil)}}
+
+ if err := def1.Run(); err == nil {
+ t.Fatalf("expected conversion error, but got none")
+ }
+ if err := def2.Run(); err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ if err := def3.Run(); err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+}
+
+func TestShouldSupportOnlyByteSlice(t *testing.T) {
+ fn1 := func(a []byte) error { return nil }
+ fn2 := func(a []string) error { return nil }
+
+ def1 := &models.StepDefinition{Handler: fn1, HandlerValue: reflect.ValueOf(fn1), Args: []interface{}{"str"}}
+ def2 := &models.StepDefinition{Handler: fn2, HandlerValue: reflect.ValueOf(fn2), Args: []interface{}{[]string{}}}
+
+ if err := def1.Run(); err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ if err := def2.Run(); err == nil {
+ t.Fatalf("expected conversion error, but got none")
+ }
+}
+
+func TestUnexpectedArguments(t *testing.T) {
+ fn := func(a, b int) error { return nil }
+ def := &models.StepDefinition{Handler: fn, HandlerValue: reflect.ValueOf(fn)}
+
+ def.Args = []interface{}{"1"}
+ if err := def.Run(); err == nil {
+ t.Fatalf("expected an error due to wrong number of arguments, but got none")
+ }
+
+ def.Args = []interface{}{"one", "two"}
+ if err := def.Run(); err == nil {
+ t.Fatalf("expected conversion error, but got none")
+ }
+
+ // @TODO maybe we should support duration
+ // fn2 := func(err time.Duration) error { return nil }
+ // def = &models.StepDefinition{Handler: fn2, HandlerValue: reflect.ValueOf(fn2)}
+
+ // def.Args = []interface{}{"1"}
+ // if err := def.Run(); err == nil {
+ // t.Fatalf("expected an error due to wrong argument type, but got none")
+ // }
+}
diff --git a/storage.go b/internal/storage/storage.go
similarity index 59%
rename from storage.go
rename to internal/storage/storage.go
index 216d192..b3a8157 100644
--- a/storage.go
+++ b/internal/storage/storage.go
@@ -1,4 +1,4 @@
-package godog
+package storage
import (
"fmt"
@@ -6,6 +6,8 @@ import (
"github.com/cucumber/messages-go/v10"
"github.com/hashicorp/go-memdb"
+
+ "github.com/cucumber/godog/internal/models"
)
const (
@@ -31,15 +33,17 @@ const (
tablePickleStepResultIndexStatus string = "status"
)
-type storage struct {
+// Storage is a thread safe in-mem storage
+type Storage struct {
db *memdb.MemDB
- testRunStarted testRunStarted
- lock *sync.Mutex
+ testRunStarted models.TestRunStarted
+ testRunStartedLock *sync.Mutex
}
-func newStorage() *storage {
- // Create the DB schema
+// NewStorage will create an in-mem storage that
+// is used across concurrent runners and formatters
+func NewStorage() *Storage {
schema := memdb.DBSchema{
Tables: map[string]*memdb.TableSchema{
tableFeature: {
@@ -115,10 +119,12 @@ func newStorage() *storage {
panic(err)
}
- return &storage{db: db, lock: new(sync.Mutex)}
+ return &Storage{db: db, testRunStartedLock: new(sync.Mutex)}
}
-func (s *storage) mustInsertPickle(p *messages.Pickle) {
+// MustInsertPickle will insert a pickle and it's steps,
+// will panic on error.
+func (s *Storage) MustInsertPickle(p *messages.Pickle) {
txn := s.db.Txn(writeMode)
if err := txn.Insert(tablePickle, p); err != nil {
@@ -134,12 +140,14 @@ func (s *storage) mustInsertPickle(p *messages.Pickle) {
txn.Commit()
}
-func (s *storage) mustGetPickle(id string) *messages.Pickle {
+// MustGetPickle will retrieve a pickle by id and panic on error.
+func (s *Storage) MustGetPickle(id string) *messages.Pickle {
v := s.mustFirst(tablePickle, tablePickleIndexID, id)
return v.(*messages.Pickle)
}
-func (s *storage) mustGetPickles(uri string) (ps []*messages.Pickle) {
+// MustGetPickles will retrieve pickles by URI and panic on error.
+func (s *Storage) MustGetPickles(uri string) (ps []*messages.Pickle) {
it := s.mustGet(tablePickle, tablePickleIndexURI, uri)
for v := it.Next(); v != nil; v = it.Next() {
ps = append(ps, v.(*messages.Pickle))
@@ -148,89 +156,102 @@ func (s *storage) mustGetPickles(uri string) (ps []*messages.Pickle) {
return
}
-func (s *storage) mustGetPickleStep(id string) *messages.Pickle_PickleStep {
+// MustGetPickleStep will retrieve a pickle step and panic on error.
+func (s *Storage) MustGetPickleStep(id string) *messages.Pickle_PickleStep {
v := s.mustFirst(tablePickleStep, tablePickleStepIndexID, id)
return v.(*messages.Pickle_PickleStep)
}
-func (s *storage) mustInsertTestRunStarted(trs testRunStarted) {
- s.lock.Lock()
- defer s.lock.Unlock()
+// MustInsertTestRunStarted will set the test run started event and panic on error.
+func (s *Storage) MustInsertTestRunStarted(trs models.TestRunStarted) {
+ s.testRunStartedLock.Lock()
+ defer s.testRunStartedLock.Unlock()
s.testRunStarted = trs
}
-func (s *storage) mustGetTestRunStarted() testRunStarted {
- s.lock.Lock()
- defer s.lock.Unlock()
+// MustGetTestRunStarted will retrieve the test run started event and panic on error.
+func (s *Storage) MustGetTestRunStarted() models.TestRunStarted {
+ s.testRunStartedLock.Lock()
+ defer s.testRunStartedLock.Unlock()
return s.testRunStarted
}
-func (s *storage) mustInsertPickleResult(pr pickleResult) {
+// MustInsertPickleResult will instert a pickle result and panic on error.
+func (s *Storage) MustInsertPickleResult(pr models.PickleResult) {
s.mustInsert(tablePickleResult, pr)
}
-func (s *storage) mustInsertPickleStepResult(psr pickleStepResult) {
+// MustInsertPickleStepResult will insert a pickle step result and panic on error.
+func (s *Storage) MustInsertPickleStepResult(psr models.PickleStepResult) {
s.mustInsert(tablePickleStepResult, psr)
}
-func (s *storage) mustGetPickleResult(id string) pickleResult {
+// MustGetPickleResult will retrieve a pickle result by id and panic on error.
+func (s *Storage) MustGetPickleResult(id string) models.PickleResult {
v := s.mustFirst(tablePickleResult, tablePickleResultIndexPickleID, id)
- return v.(pickleResult)
+ return v.(models.PickleResult)
}
-func (s *storage) mustGetPickleResults() (prs []pickleResult) {
+// MustGetPickleResults will retrieve all pickle results and panic on error.
+func (s *Storage) MustGetPickleResults() (prs []models.PickleResult) {
it := s.mustGet(tablePickleResult, tablePickleResultIndexPickleID)
for v := it.Next(); v != nil; v = it.Next() {
- prs = append(prs, v.(pickleResult))
+ prs = append(prs, v.(models.PickleResult))
}
return prs
}
-func (s *storage) mustGetPickleStepResult(id string) pickleStepResult {
+// MustGetPickleStepResult will retrieve a pickle strep result by id and panic on error.
+func (s *Storage) MustGetPickleStepResult(id string) models.PickleStepResult {
v := s.mustFirst(tablePickleStepResult, tablePickleStepResultIndexPickleStepID, id)
- return v.(pickleStepResult)
+ return v.(models.PickleStepResult)
}
-func (s *storage) mustGetPickleStepResultsByPickleID(pickleID string) (psrs []pickleStepResult) {
+// MustGetPickleStepResultsByPickleID will retrieve pickle strep results by pickle id and panic on error.
+func (s *Storage) MustGetPickleStepResultsByPickleID(pickleID string) (psrs []models.PickleStepResult) {
it := s.mustGet(tablePickleStepResult, tablePickleStepResultIndexPickleID, pickleID)
for v := it.Next(); v != nil; v = it.Next() {
- psrs = append(psrs, v.(pickleStepResult))
+ psrs = append(psrs, v.(models.PickleStepResult))
}
return psrs
}
-func (s *storage) mustGetPickleStepResultsByStatus(status stepResultStatus) (psrs []pickleStepResult) {
+// MustGetPickleStepResultsByStatus will retrieve pickle strep results by status and panic on error.
+func (s *Storage) MustGetPickleStepResultsByStatus(status models.StepResultStatus) (psrs []models.PickleStepResult) {
it := s.mustGet(tablePickleStepResult, tablePickleStepResultIndexStatus, status)
for v := it.Next(); v != nil; v = it.Next() {
- psrs = append(psrs, v.(pickleStepResult))
+ psrs = append(psrs, v.(models.PickleStepResult))
}
return psrs
}
-func (s *storage) mustInsertFeature(f *feature) {
+// MustInsertFeature will insert a feature and panic on error.
+func (s *Storage) MustInsertFeature(f *models.Feature) {
s.mustInsert(tableFeature, f)
}
-func (s *storage) mustGetFeature(uri string) *feature {
+// MustGetFeature will retrieve a feature by URI and panic on error.
+func (s *Storage) MustGetFeature(uri string) *models.Feature {
v := s.mustFirst(tableFeature, tableFeatureIndexURI, uri)
- return v.(*feature)
+ return v.(*models.Feature)
}
-func (s *storage) mustGetFeatures() (fs []*feature) {
+// MustGetFeatures will retrieve all features by and panic on error.
+func (s *Storage) MustGetFeatures() (fs []*models.Feature) {
it := s.mustGet(tableFeature, tableFeatureIndexURI)
for v := it.Next(); v != nil; v = it.Next() {
- fs = append(fs, v.(*feature))
+ fs = append(fs, v.(*models.Feature))
}
return
}
-func (s *storage) mustInsert(table string, obj interface{}) {
+func (s *Storage) mustInsert(table string, obj interface{}) {
txn := s.db.Txn(writeMode)
if err := txn.Insert(table, obj); err != nil {
@@ -240,7 +261,7 @@ func (s *storage) mustInsert(table string, obj interface{}) {
txn.Commit()
}
-func (s *storage) mustFirst(table, index string, args ...interface{}) interface{} {
+func (s *Storage) mustFirst(table, index string, args ...interface{}) interface{} {
txn := s.db.Txn(readMode)
defer txn.Abort()
@@ -255,7 +276,7 @@ func (s *storage) mustFirst(table, index string, args ...interface{}) interface{
return v
}
-func (s *storage) mustGet(table, index string, args ...interface{}) memdb.ResultIterator {
+func (s *Storage) mustGet(table, index string, args ...interface{}) memdb.ResultIterator {
txn := s.db.Txn(readMode)
defer txn.Abort()
diff --git a/internal/storage/storage_test.go b/internal/storage/storage_test.go
new file mode 100644
index 0000000..be50a7b
--- /dev/null
+++ b/internal/storage/storage_test.go
@@ -0,0 +1,187 @@
+package storage_test
+
+import (
+ "testing"
+ "time"
+
+ "github.com/cucumber/messages-go/v10"
+ "github.com/stretchr/testify/assert"
+
+ "github.com/cucumber/godog/internal/models"
+ "github.com/cucumber/godog/internal/storage"
+ "github.com/cucumber/godog/internal/testutils"
+)
+
+func Test_MustGetPickle(t *testing.T) {
+ s := storage.NewStorage()
+ ft := testutils.BuildTestFeature(t)
+
+ expected := ft.Pickles[0]
+ s.MustInsertPickle(expected)
+
+ actual := s.MustGetPickle(expected.Id)
+ assert.Equal(t, expected, actual)
+}
+
+func Test_MustGetPickles(t *testing.T) {
+ s := storage.NewStorage()
+ ft := testutils.BuildTestFeature(t)
+
+ expected := ft.Pickles
+ for _, pickle := range expected {
+ s.MustInsertPickle(pickle)
+ }
+
+ actual := s.MustGetPickles(ft.Uri)
+ assert.Equal(t, expected, actual)
+}
+
+func Test_MustGetPickleStep(t *testing.T) {
+ s := storage.NewStorage()
+ ft := testutils.BuildTestFeature(t)
+
+ for _, pickle := range ft.Pickles {
+ s.MustInsertPickle(pickle)
+ }
+
+ for _, pickle := range ft.Pickles {
+ for _, expected := range pickle.Steps {
+ actual := s.MustGetPickleStep(expected.Id)
+ assert.Equal(t, expected, actual)
+ }
+ }
+}
+
+func Test_MustGetTestRunStarted(t *testing.T) {
+ s := storage.NewStorage()
+
+ expected := models.TestRunStarted{StartedAt: time.Now()}
+ s.MustInsertTestRunStarted(expected)
+
+ actual := s.MustGetTestRunStarted()
+ assert.Equal(t, expected, actual)
+}
+
+func Test_MustGetPickleResult(t *testing.T) {
+ s := storage.NewStorage()
+
+ const pickleID = "1"
+ expected := models.PickleResult{PickleID: pickleID}
+ s.MustInsertPickleResult(expected)
+
+ actual := s.MustGetPickleResult(pickleID)
+ assert.Equal(t, expected, actual)
+}
+
+func Test_MustGetPickleResults(t *testing.T) {
+ s := storage.NewStorage()
+
+ expected := []models.PickleResult{{PickleID: "1"}, {PickleID: "2"}}
+ for _, pr := range expected {
+ s.MustInsertPickleResult(pr)
+ }
+
+ actual := s.MustGetPickleResults()
+ assert.Equal(t, expected, actual)
+}
+
+func Test_MustGetPickleStepResult(t *testing.T) {
+ s := storage.NewStorage()
+
+ const pickleID = "1"
+ const stepID = "2"
+
+ expected := models.PickleStepResult{
+ Status: models.Passed,
+ PickleID: pickleID,
+ PickleStepID: stepID,
+ }
+ s.MustInsertPickleStepResult(expected)
+
+ actual := s.MustGetPickleStepResult(stepID)
+ assert.Equal(t, expected, actual)
+}
+
+func Test_MustGetPickleStepResultsByPickleID(t *testing.T) {
+ s := storage.NewStorage()
+
+ const pickleID = "p1"
+
+ expected := []models.PickleStepResult{
+ {
+ Status: models.Passed,
+ PickleID: pickleID,
+ PickleStepID: "s1",
+ },
+ {
+ Status: models.Passed,
+ PickleID: pickleID,
+ PickleStepID: "s2",
+ },
+ }
+
+ for _, psr := range expected {
+ s.MustInsertPickleStepResult(psr)
+ }
+
+ actual := s.MustGetPickleStepResultsByPickleID(pickleID)
+ assert.Equal(t, expected, actual)
+}
+
+func Test_MustGetPickleStepResultsByStatus(t *testing.T) {
+ s := storage.NewStorage()
+
+ const pickleID = "p1"
+
+ expected := []models.PickleStepResult{
+ {
+ Status: models.Passed,
+ PickleID: pickleID,
+ PickleStepID: "s1",
+ },
+ }
+
+ testdata := []models.PickleStepResult{
+ expected[0],
+ {
+ Status: models.Failed,
+ PickleID: pickleID,
+ PickleStepID: "s2",
+ },
+ }
+
+ for _, psr := range testdata {
+ s.MustInsertPickleStepResult(psr)
+ }
+
+ actual := s.MustGetPickleStepResultsByStatus(models.Passed)
+ assert.Equal(t, expected, actual)
+}
+
+func Test_MustGetFeature(t *testing.T) {
+ s := storage.NewStorage()
+
+ const uri = ""
+
+ expected := &models.Feature{GherkinDocument: &messages.GherkinDocument{Uri: uri}}
+ s.MustInsertFeature(expected)
+
+ actual := s.MustGetFeature(uri)
+ assert.Equal(t, expected, actual)
+}
+
+func Test_MustGetFeatures(t *testing.T) {
+ s := storage.NewStorage()
+
+ expected := []*models.Feature{
+ {GherkinDocument: &messages.GherkinDocument{Uri: "uri1"}},
+ {GherkinDocument: &messages.GherkinDocument{Uri: "uri2"}},
+ }
+
+ for _, f := range expected {
+ s.MustInsertFeature(f)
+ }
+
+ actual := s.MustGetFeatures()
+ assert.Equal(t, expected, actual)
+}
diff --git a/internal/testutils/utils.go b/internal/testutils/utils.go
new file mode 100644
index 0000000..8965596
--- /dev/null
+++ b/internal/testutils/utils.go
@@ -0,0 +1,60 @@
+package testutils
+
+import (
+ "strings"
+ "testing"
+
+ "github.com/cucumber/gherkin-go/v11"
+ "github.com/cucumber/messages-go/v10"
+ "github.com/stretchr/testify/require"
+
+ "github.com/cucumber/godog/internal/models"
+)
+
+// BuildTestFeature creates a feature for testing purpose.
+//
+// The created feature includes:
+// - a background
+// - one normal scenario with three steps
+// - one outline scenario with one example and three steps
+func BuildTestFeature(t *testing.T) models.Feature {
+ newIDFunc := (&messages.Incrementing{}).NewId
+
+ gherkinDocument, err := gherkin.ParseGherkinDocument(strings.NewReader(featureContent), newIDFunc)
+ require.NoError(t, err)
+
+ path := t.Name()
+ gherkinDocument.Uri = path
+ pickles := gherkin.Pickles(*gherkinDocument, path, newIDFunc)
+
+ ft := models.Feature{GherkinDocument: gherkinDocument, Pickles: pickles, Content: []byte(featureContent)}
+ require.Len(t, ft.Pickles, 2)
+
+ require.Len(t, ft.Pickles[0].AstNodeIds, 1)
+ require.Len(t, ft.Pickles[0].Steps, 3)
+
+ require.Len(t, ft.Pickles[1].AstNodeIds, 2)
+ require.Len(t, ft.Pickles[1].Steps, 3)
+
+ return ft
+}
+
+const featureContent = `Feature: eat godogs
+In order to be happy
+As a hungry gopher
+I need to be able to eat godogs
+
+Background:
+ Given there are godogs
+
+Scenario: Eat 5 out of 12
+ When I eat 5
+ Then there should be 7 remaining
+
+Scenario Outline: Eat out of
+ When I eat
+ Then there should be remaining
+
+ Examples:
+ | begin | dec | remain |
+ | 12 | 5 | 7 |`
diff --git a/internal/utils/utils.go b/internal/utils/utils.go
new file mode 100644
index 0000000..f1ec21f
--- /dev/null
+++ b/internal/utils/utils.go
@@ -0,0 +1,21 @@
+package utils
+
+import (
+ "strings"
+ "time"
+)
+
+// S repeats a space n times
+func S(n int) string {
+ if n < 0 {
+ n = 1
+ }
+ return strings.Repeat(" ", n)
+}
+
+// TimeNowFunc is a utility function to simply testing
+// by allowing TimeNowFunc to be defined to zero time
+// to remove the time domain from tests
+var TimeNowFunc = func() time.Time {
+ return time.Now()
+}
diff --git a/parser.go b/parser.go
index 2824e35..3ff82e2 100644
--- a/parser.go
+++ b/parser.go
@@ -13,6 +13,7 @@ import (
"github.com/cucumber/gherkin-go/v11"
"github.com/cucumber/messages-go/v10"
+ "github.com/cucumber/godog/internal/models"
"github.com/cucumber/godog/internal/tags"
)
@@ -30,7 +31,7 @@ func extractFeaturePathLine(p string) (string, int) {
return retPath, line
}
-func parseFeatureFile(path string, newIDFunc func() string) (*feature, error) {
+func parseFeatureFile(path string, newIDFunc func() string) (*models.Feature, error) {
reader, err := os.Open(path)
if err != nil {
return nil, err
@@ -47,12 +48,12 @@ func parseFeatureFile(path string, newIDFunc func() string) (*feature, error) {
gherkinDocument.Uri = path
pickles := gherkin.Pickles(*gherkinDocument, path, newIDFunc)
- f := feature{GherkinDocument: gherkinDocument, pickles: pickles, content: buf.Bytes()}
+ f := models.Feature{GherkinDocument: gherkinDocument, Pickles: pickles, Content: buf.Bytes()}
return &f, nil
}
-func parseFeatureDir(dir string, newIDFunc func() string) ([]*feature, error) {
- var features []*feature
+func parseFeatureDir(dir string, newIDFunc func() string) ([]*models.Feature, error) {
+ var features []*models.Feature
return features, filepath.Walk(dir, func(p string, f os.FileInfo, err error) error {
if err != nil {
return err
@@ -76,8 +77,8 @@ func parseFeatureDir(dir string, newIDFunc func() string) ([]*feature, error) {
})
}
-func parsePath(path string) ([]*feature, error) {
- var features []*feature
+func parsePath(path string) ([]*models.Feature, error) {
+ var features []*models.Feature
path, line := extractFeaturePathLine(path)
@@ -99,23 +100,23 @@ func parsePath(path string) ([]*feature, error) {
// filter scenario by line number
var pickles []*messages.Pickle
- for _, pickle := range ft.pickles {
- sc := ft.findScenario(pickle.AstNodeIds[0])
+ for _, pickle := range ft.Pickles {
+ sc := ft.FindScenario(pickle.AstNodeIds[0])
if line == -1 || uint32(line) == sc.Location.Line {
pickles = append(pickles, pickle)
}
}
- ft.pickles = pickles
+ ft.Pickles = pickles
return append(features, ft), nil
}
-func parseFeatures(filter string, paths []string) ([]*feature, error) {
+func parseFeatures(filter string, paths []string) ([]*models.Feature, error) {
var order int
featureIdxs := make(map[string]int)
- uniqueFeatureURI := make(map[string]*feature)
+ uniqueFeatureURI := make(map[string]*models.Feature)
for _, path := range paths {
feats, err := parsePath(path)
@@ -140,7 +141,7 @@ func parseFeatures(filter string, paths []string) ([]*feature, error) {
}
}
- var features = make([]*feature, len(uniqueFeatureURI))
+ var features = make([]*models.Feature, len(uniqueFeatureURI))
for uri, feature := range uniqueFeatureURI {
idx := featureIdxs[uri]
features[idx] = feature
@@ -151,11 +152,11 @@ func parseFeatures(filter string, paths []string) ([]*feature, error) {
return features, nil
}
-func filterFeatures(filter string, features []*feature) (result []*feature) {
+func filterFeatures(filter string, features []*models.Feature) (result []*models.Feature) {
for _, ft := range features {
- ft.pickles = tags.ApplyTagFilter(filter, ft.pickles)
+ ft.Pickles = tags.ApplyTagFilter(filter, ft.Pickles)
- if ft.Feature != nil && len(ft.pickles) > 0 {
+ if ft.Feature != nil && len(ft.Pickles) > 0 {
result = append(result, ft)
}
}
diff --git a/results.go b/results.go
deleted file mode 100644
index 5424750..0000000
--- a/results.go
+++ /dev/null
@@ -1,81 +0,0 @@
-package godog
-
-import (
- "time"
-
- "github.com/cucumber/godog/colors"
-)
-
-type testRunStarted struct {
- StartedAt time.Time
-}
-
-type pickleResult struct {
- PickleID string
- StartedAt time.Time
-}
-
-type pickleStepResult struct {
- Status stepResultStatus
- finishedAt time.Time
- err error
-
- PickleID string
- PickleStepID string
-
- def *StepDefinition
-}
-
-func newStepResult(pickleID, pickleStepID string, match *StepDefinition) pickleStepResult {
- return pickleStepResult{finishedAt: timeNowFunc(), PickleID: pickleID, PickleStepID: pickleStepID, def: match}
-}
-
-type sortPickleStepResultsByPickleStepID []pickleStepResult
-
-func (s sortPickleStepResultsByPickleStepID) Len() int { return len(s) }
-func (s sortPickleStepResultsByPickleStepID) Less(i, j int) bool {
- iID := mustConvertStringToInt(s[i].PickleStepID)
- jID := mustConvertStringToInt(s[j].PickleStepID)
- return iID < jID
-}
-func (s sortPickleStepResultsByPickleStepID) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
-
-type stepResultStatus int
-
-const (
- passed stepResultStatus = iota
- failed
- skipped
- undefined
- pending
-)
-
-func (st stepResultStatus) clr() colors.ColorFunc {
- switch st {
- case passed:
- return green
- case failed:
- return red
- case skipped:
- return cyan
- default:
- return yellow
- }
-}
-
-func (st stepResultStatus) String() string {
- switch st {
- case passed:
- return "passed"
- case failed:
- return "failed"
- case skipped:
- return "skipped"
- case undefined:
- return "undefined"
- case pending:
- return "pending"
- default:
- return "unknown"
- }
-}
diff --git a/run.go b/run.go
index adf4a34..5e73721 100644
--- a/run.go
+++ b/run.go
@@ -12,8 +12,12 @@ import (
"strings"
"sync"
- "github.com/cucumber/godog/colors"
"github.com/cucumber/messages-go/v10"
+
+ "github.com/cucumber/godog/colors"
+ "github.com/cucumber/godog/internal/models"
+ "github.com/cucumber/godog/internal/storage"
+ "github.com/cucumber/godog/internal/utils"
)
const (
@@ -29,12 +33,12 @@ type runner struct {
randomSeed int64
stopOnFailure, strict bool
- features []*feature
+ features []*models.Feature
testSuiteInitializer testSuiteInitializer
scenarioInitializer scenarioInitializer
- storage *storage
+ storage *storage.Storage
fmt Formatter
}
@@ -42,7 +46,7 @@ func (r *runner) concurrent(rate int) (failed bool) {
var copyLock sync.Mutex
if fmt, ok := r.fmt.(storageFormatter); ok {
- fmt.setStorage(r.storage)
+ fmt.SetStorage(r.storage)
}
testSuiteContext := TestSuiteContext{}
@@ -50,8 +54,8 @@ func (r *runner) concurrent(rate int) (failed bool) {
r.testSuiteInitializer(&testSuiteContext)
}
- testRunStarted := testRunStarted{StartedAt: timeNowFunc()}
- r.storage.mustInsertTestRunStarted(testRunStarted)
+ testRunStarted := models.TestRunStarted{StartedAt: utils.TimeNowFunc()}
+ r.storage.MustInsertTestRunStarted(testRunStarted)
r.fmt.TestRunStarted()
// run before suite handlers
@@ -61,15 +65,15 @@ func (r *runner) concurrent(rate int) (failed bool) {
queue := make(chan int, rate)
for _, ft := range r.features {
- pickles := make([]*messages.Pickle, len(ft.pickles))
+ pickles := make([]*messages.Pickle, len(ft.Pickles))
if r.randomSeed != 0 {
r := rand.New(rand.NewSource(r.randomSeed))
- perm := r.Perm(len(ft.pickles))
+ perm := r.Perm(len(ft.Pickles))
for i, v := range perm {
- pickles[v] = ft.pickles[i]
+ pickles[v] = ft.Pickles[i]
}
} else {
- copy(pickles, ft.pickles)
+ copy(pickles, ft.Pickles)
}
for i, p := range pickles {
@@ -78,7 +82,7 @@ func (r *runner) concurrent(rate int) (failed bool) {
queue <- i // reserve space in queue
if i == 0 {
- r.fmt.Feature(ft.GherkinDocument, ft.Uri, ft.content)
+ r.fmt.Feature(ft.GherkinDocument, ft.Uri, ft.Content)
}
go func(fail *bool, pickle *messages.Pickle) {
@@ -181,12 +185,12 @@ func runWithOptions(suiteName string, runner runner, opt Options) int {
return exitOptionError
}
- runner.storage = newStorage()
+ runner.storage = storage.NewStorage()
for _, feat := range runner.features {
- runner.storage.mustInsertFeature(feat)
+ runner.storage.MustInsertFeature(feat)
- for _, pickle := range feat.pickles {
- runner.storage.mustInsertPickle(pickle)
+ for _, pickle := range feat.Pickles {
+ runner.storage.MustInsertPickle(pickle)
}
}
diff --git a/fmt_progress_test.go b/run_progress_test.go
similarity index 71%
rename from fmt_progress_test.go
rename to run_progress_test.go
index a2b281c..2095b80 100644
--- a/fmt_progress_test.go
+++ b/run_progress_test.go
@@ -11,6 +11,9 @@ import (
"github.com/stretchr/testify/require"
"github.com/cucumber/godog/colors"
+ "github.com/cucumber/godog/internal/formatters"
+ "github.com/cucumber/godog/internal/models"
+ "github.com/cucumber/godog/internal/storage"
)
var basicGherkinFeature = `
@@ -28,31 +31,31 @@ func Test_ProgressFormatterWhenStepPanics(t *testing.T) {
require.NoError(t, err)
gd.Uri = path
- ft := feature{GherkinDocument: gd}
- ft.pickles = gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId)
+ ft := models.Feature{GherkinDocument: gd}
+ ft.Pickles = gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId)
var buf bytes.Buffer
w := colors.Uncolored(&buf)
r := runner{
- fmt: progressFunc("progress", w),
- features: []*feature{&ft},
+ fmt: formatters.ProgressFormatterFunc("progress", w),
+ features: []*models.Feature{&ft},
scenarioInitializer: func(ctx *ScenarioContext) {
ctx.Step(`^one$`, func() error { return nil })
ctx.Step(`^two$`, func() error { panic("omg") })
},
}
- r.storage = newStorage()
- r.storage.mustInsertFeature(&ft)
- for _, pickle := range ft.pickles {
- r.storage.mustInsertPickle(pickle)
+ r.storage = storage.NewStorage()
+ r.storage.MustInsertFeature(&ft)
+ for _, pickle := range ft.Pickles {
+ r.storage.MustInsertPickle(pickle)
}
failed := r.concurrent(1)
require.True(t, failed)
actual := buf.String()
- assert.Contains(t, actual, "godog/fmt_progress_test.go:41")
+ assert.Contains(t, actual, "godog/run_progress_test.go:44")
}
func Test_ProgressFormatterWithPanicInMultistep(t *testing.T) {
@@ -62,14 +65,14 @@ func Test_ProgressFormatterWithPanicInMultistep(t *testing.T) {
require.NoError(t, err)
gd.Uri = path
- ft := feature{GherkinDocument: gd}
- ft.pickles = gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId)
+ ft := models.Feature{GherkinDocument: gd}
+ ft.Pickles = gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId)
var buf bytes.Buffer
w := colors.Uncolored(&buf)
r := runner{
- fmt: progressFunc("progress", w),
- features: []*feature{&ft},
+ fmt: formatters.ProgressFormatterFunc("progress", w),
+ features: []*models.Feature{&ft},
scenarioInitializer: func(ctx *ScenarioContext) {
ctx.Step(`^sub1$`, func() error { return nil })
ctx.Step(`^sub-sub$`, func() error { return nil })
@@ -79,10 +82,10 @@ func Test_ProgressFormatterWithPanicInMultistep(t *testing.T) {
},
}
- r.storage = newStorage()
- r.storage.mustInsertFeature(&ft)
- for _, pickle := range ft.pickles {
- r.storage.mustInsertPickle(pickle)
+ r.storage = storage.NewStorage()
+ r.storage.MustInsertFeature(&ft)
+ for _, pickle := range ft.Pickles {
+ r.storage.MustInsertPickle(pickle)
}
failed := r.concurrent(1)
@@ -96,14 +99,14 @@ func Test_ProgressFormatterMultistepTemplates(t *testing.T) {
require.NoError(t, err)
gd.Uri = path
- ft := feature{GherkinDocument: gd}
- ft.pickles = gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId)
+ ft := models.Feature{GherkinDocument: gd}
+ ft.Pickles = gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId)
var buf bytes.Buffer
w := colors.Uncolored(&buf)
r := runner{
- fmt: progressFunc("progress", w),
- features: []*feature{&ft},
+ fmt: formatters.ProgressFormatterFunc("progress", w),
+ features: []*models.Feature{&ft},
scenarioInitializer: func(ctx *ScenarioContext) {
ctx.Step(`^sub-sub$`, func() error { return nil })
ctx.Step(`^substep$`, func() Steps { return Steps{"sub-sub", `unavailable "John" cost 5`, "one", "three"} })
@@ -112,10 +115,10 @@ func Test_ProgressFormatterMultistepTemplates(t *testing.T) {
},
}
- r.storage = newStorage()
- r.storage.mustInsertFeature(&ft)
- for _, pickle := range ft.pickles {
- r.storage.mustInsertPickle(pickle)
+ r.storage = storage.NewStorage()
+ r.storage.MustInsertFeature(&ft)
+ for _, pickle := range ft.Pickles {
+ r.storage.MustInsertPickle(pickle)
}
failed := r.concurrent(1)
@@ -172,24 +175,24 @@ Feature: basic
require.NoError(t, err)
gd.Uri = path
- ft := feature{GherkinDocument: gd}
- ft.pickles = gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId)
+ ft := models.Feature{GherkinDocument: gd}
+ ft.Pickles = gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId)
var buf bytes.Buffer
w := colors.Uncolored(&buf)
r := runner{
- fmt: progressFunc("progress", w),
- features: []*feature{&ft},
+ fmt: formatters.ProgressFormatterFunc("progress", w),
+ features: []*models.Feature{&ft},
scenarioInitializer: func(ctx *ScenarioContext) {
ctx.Step(`^one$`, func() error { return nil })
ctx.Step(`^two:$`, func(doc *messages.PickleStepArgument_PickleDocString) Steps { return Steps{"one"} })
},
}
- r.storage = newStorage()
- r.storage.mustInsertFeature(&ft)
- for _, pickle := range ft.pickles {
- r.storage.mustInsertPickle(pickle)
+ r.storage = storage.NewStorage()
+ r.storage.MustInsertFeature(&ft)
+ for _, pickle := range ft.Pickles {
+ r.storage.MustInsertPickle(pickle)
}
failed := r.concurrent(1)
@@ -210,8 +213,8 @@ Feature: basic
require.NoError(t, err)
gd.Uri = path
- ft := feature{GherkinDocument: gd}
- ft.pickles = gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId)
+ ft := models.Feature{GherkinDocument: gd}
+ ft.Pickles = gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId)
var subStep = `three:
"""
@@ -221,8 +224,8 @@ Feature: basic
var buf bytes.Buffer
w := colors.Uncolored(&buf)
r := runner{
- fmt: progressFunc("progress", w),
- features: []*feature{&ft},
+ fmt: formatters.ProgressFormatterFunc("progress", w),
+ features: []*models.Feature{&ft},
scenarioInitializer: func(ctx *ScenarioContext) {
ctx.Step(`^one$`, func() error { return nil })
ctx.Step(`^two$`, func() Steps { return Steps{subStep} })
@@ -230,10 +233,10 @@ Feature: basic
},
}
- r.storage = newStorage()
- r.storage.mustInsertFeature(&ft)
- for _, pickle := range ft.pickles {
- r.storage.mustInsertPickle(pickle)
+ r.storage = storage.NewStorage()
+ r.storage.MustInsertFeature(&ft)
+ for _, pickle := range ft.Pickles {
+ r.storage.MustInsertPickle(pickle)
}
failed := r.concurrent(1)
diff --git a/run_test.go b/run_test.go
index 78a77be..94a6a80 100644
--- a/run_test.go
+++ b/run_test.go
@@ -17,6 +17,9 @@ import (
"github.com/stretchr/testify/require"
"github.com/cucumber/godog/colors"
+ "github.com/cucumber/godog/internal/formatters"
+ "github.com/cucumber/godog/internal/models"
+ "github.com/cucumber/godog/internal/storage"
)
func okStep() error {
@@ -70,22 +73,22 @@ func Test_FailsOrPassesBasedOnStrictModeWhenHasPendingSteps(t *testing.T) {
require.NoError(t, err)
gd.Uri = path
- ft := feature{GherkinDocument: gd}
- ft.pickles = gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId)
+ ft := models.Feature{GherkinDocument: gd}
+ ft.Pickles = gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId)
r := runner{
- fmt: progressFunc("progress", ioutil.Discard),
- features: []*feature{&ft},
+ fmt: formatters.ProgressFormatterFunc("progress", ioutil.Discard),
+ features: []*models.Feature{&ft},
scenarioInitializer: func(ctx *ScenarioContext) {
ctx.Step(`^one$`, func() error { return nil })
ctx.Step(`^two$`, func() error { return ErrPending })
},
}
- r.storage = newStorage()
- r.storage.mustInsertFeature(&ft)
- for _, pickle := range ft.pickles {
- r.storage.mustInsertPickle(pickle)
+ r.storage = storage.NewStorage()
+ r.storage.MustInsertFeature(&ft)
+ for _, pickle := range ft.Pickles {
+ r.storage.MustInsertPickle(pickle)
}
failed := r.concurrent(1)
@@ -103,22 +106,22 @@ func Test_FailsOrPassesBasedOnStrictModeWhenHasUndefinedSteps(t *testing.T) {
require.NoError(t, err)
gd.Uri = path
- ft := feature{GherkinDocument: gd}
- ft.pickles = gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId)
+ ft := models.Feature{GherkinDocument: gd}
+ ft.Pickles = gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId)
r := runner{
- fmt: progressFunc("progress", ioutil.Discard),
- features: []*feature{&ft},
+ fmt: formatters.ProgressFormatterFunc("progress", ioutil.Discard),
+ features: []*models.Feature{&ft},
scenarioInitializer: func(ctx *ScenarioContext) {
ctx.Step(`^one$`, func() error { return nil })
// two - is undefined
},
}
- r.storage = newStorage()
- r.storage.mustInsertFeature(&ft)
- for _, pickle := range ft.pickles {
- r.storage.mustInsertPickle(pickle)
+ r.storage = storage.NewStorage()
+ r.storage.MustInsertFeature(&ft)
+ for _, pickle := range ft.Pickles {
+ r.storage.MustInsertPickle(pickle)
}
failed := r.concurrent(1)
@@ -136,22 +139,22 @@ func Test_ShouldFailOnError(t *testing.T) {
require.NoError(t, err)
gd.Uri = path
- ft := feature{GherkinDocument: gd}
- ft.pickles = gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId)
+ ft := models.Feature{GherkinDocument: gd}
+ ft.Pickles = gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId)
r := runner{
- fmt: progressFunc("progress", ioutil.Discard),
- features: []*feature{&ft},
+ fmt: formatters.ProgressFormatterFunc("progress", ioutil.Discard),
+ features: []*models.Feature{&ft},
scenarioInitializer: func(ctx *ScenarioContext) {
- ctx.Step(`^one$`, func() error { return nil })
ctx.Step(`^two$`, func() error { return fmt.Errorf("error") })
+ ctx.Step(`^one$`, func() error { return nil })
},
}
- r.storage = newStorage()
- r.storage.mustInsertFeature(&ft)
- for _, pickle := range ft.pickles {
- r.storage.mustInsertPickle(pickle)
+ r.storage = storage.NewStorage()
+ r.storage.MustInsertFeature(&ft)
+ for _, pickle := range ft.Pickles {
+ r.storage.MustInsertPickle(pickle)
}
failed := r.concurrent(1)
@@ -284,7 +287,7 @@ func Test_RandomizeRun(t *testing.T) {
const createRandomSeedFlag = -1
const noConcurrencyFlag = 1
const formatter = "pretty"
- const featurePath = "formatter-tests/features/with_few_empty_scenarios.feature"
+ const featurePath = "internal/formatters/formatter-tests/features/with_few_empty_scenarios.feature"
fmtOutputScenarioInitializer := func(ctx *ScenarioContext) {
ctx.Step(`^(?:a )?failing step`, failingStepDef)
@@ -367,7 +370,7 @@ func Test_FormatterConcurrencyRun(t *testing.T) {
"cucumber",
}
- featurePaths := []string{"formatter-tests/features"}
+ featurePaths := []string{"internal/formatters/formatter-tests/features"}
const concurrency = 100
const noRandomFlag = 0
diff --git a/stacktrace_test.go b/stacktrace_test.go
index 7dcfbb4..e988d8e 100644
--- a/stacktrace_test.go
+++ b/stacktrace_test.go
@@ -24,7 +24,7 @@ func callstack3() *stack {
return &st
}
-func TestStacktrace(t *testing.T) {
+func Test_Stacktrace(t *testing.T) {
err := &traceError{
msg: "err msg",
stack: callstack1(),
diff --git a/stepdef_test.go b/stepdef_test.go
deleted file mode 100644
index a4f71a9..0000000
--- a/stepdef_test.go
+++ /dev/null
@@ -1,106 +0,0 @@
-package godog
-
-import (
- "reflect"
- "strings"
- "testing"
-
- "github.com/cucumber/messages-go/v10"
-)
-
-func TestShouldSupportIntTypes(t *testing.T) {
- fn := func(a int64, b int32, c int16, d int8) error { return nil }
-
- def := &StepDefinition{
- Handler: fn,
- hv: reflect.ValueOf(fn),
- }
-
- def.args = []interface{}{"1", "1", "1", "1"}
- if err := def.run(); err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
-
- def.args = []interface{}{"1", "1", "1", strings.Repeat("1", 9)}
- if err := def.run(); err == nil {
- t.Fatalf("expected convertion fail for int8, but got none")
- }
-}
-
-func TestShouldSupportFloatTypes(t *testing.T) {
- fn := func(a float64, b float32) error { return nil }
-
- def := &StepDefinition{
- Handler: fn,
- hv: reflect.ValueOf(fn),
- }
-
- def.args = []interface{}{"1.1", "1.09"}
- if err := def.run(); err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
-
- def.args = []interface{}{"1.08", strings.Repeat("1", 65) + ".67"}
- if err := def.run(); err == nil {
- t.Fatalf("expected convertion fail for float32, but got none")
- }
-}
-
-func TestShouldNotSupportOtherPointerTypesThanGherkin(t *testing.T) {
- fn1 := func(a *int) error { return nil }
- fn2 := func(a *messages.PickleStepArgument_PickleDocString) error { return nil }
- fn3 := func(a *messages.PickleStepArgument_PickleTable) error { return nil }
-
- def1 := &StepDefinition{Handler: fn1, hv: reflect.ValueOf(fn1), args: []interface{}{(*int)(nil)}}
- def2 := &StepDefinition{Handler: fn2, hv: reflect.ValueOf(fn2), args: []interface{}{&messages.PickleStepArgument_PickleDocString{}}}
- def3 := &StepDefinition{Handler: fn3, hv: reflect.ValueOf(fn3), args: []interface{}{(*messages.PickleStepArgument_PickleTable)(nil)}}
-
- if err := def1.run(); err == nil {
- t.Fatalf("expected conversion error, but got none")
- }
- if err := def2.run(); err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if err := def3.run(); err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
-}
-
-func TestShouldSupportOnlyByteSlice(t *testing.T) {
- fn1 := func(a []byte) error { return nil }
- fn2 := func(a []string) error { return nil }
-
- def1 := &StepDefinition{Handler: fn1, hv: reflect.ValueOf(fn1), args: []interface{}{"str"}}
- def2 := &StepDefinition{Handler: fn2, hv: reflect.ValueOf(fn2), args: []interface{}{[]string{}}}
-
- if err := def1.run(); err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if err := def2.run(); err == nil {
- t.Fatalf("expected conversion error, but got none")
- }
-}
-
-func TestUnexpectedArguments(t *testing.T) {
- fn := func(a, b int) error { return nil }
- def := &StepDefinition{Handler: fn, hv: reflect.ValueOf(fn)}
-
- def.args = []interface{}{"1"}
- if err := def.run(); err == nil {
- t.Fatalf("expected an error due to wrong number of arguments, but got none")
- }
-
- def.args = []interface{}{"one", "two"}
- if err := def.run(); err == nil {
- t.Fatalf("expected conversion error, but got none")
- }
-
- // @TODO maybe we should support duration
- // fn2 := func(err time.Duration) error { return nil }
- // def = &StepDefinition{Handler: fn2, hv: reflect.ValueOf(fn2)}
-
- // def.args = []interface{}{"1"}
- // if err := def.run(); err == nil {
- // t.Fatalf("expected an error due to wrong argument type, but got none")
- // }
-}
diff --git a/suite.go b/suite.go
index 7ce2f26..864f581 100644
--- a/suite.go
+++ b/suite.go
@@ -6,10 +6,13 @@ import (
"strings"
"github.com/cucumber/messages-go/v10"
+
+ "github.com/cucumber/godog/internal/models"
+ "github.com/cucumber/godog/internal/storage"
+ "github.com/cucumber/godog/internal/utils"
)
var errorInterface = reflect.TypeOf((*error)(nil)).Elem()
-var typeOfBytes = reflect.TypeOf([]byte(nil))
// ErrUndefined is returned in case if step definition was not found
var ErrUndefined = fmt.Errorf("step is undefined")
@@ -19,10 +22,10 @@ var ErrUndefined = fmt.Errorf("step is undefined")
var ErrPending = fmt.Errorf("step implementation is pending")
type suite struct {
- steps []*StepDefinition
+ steps []*models.StepDefinition
fmt Formatter
- storage *storage
+ storage *storage.Storage
failed bool
randomSeed int64
@@ -36,10 +39,10 @@ type suite struct {
afterScenarioHandlers []func(*Scenario, error)
}
-func (s *suite) matchStep(step *messages.Pickle_PickleStep) *StepDefinition {
+func (s *suite) matchStep(step *messages.Pickle_PickleStep) *models.StepDefinition {
def := s.matchStepText(step.Text)
if def != nil && step.Argument != nil {
- def.args = append(def.args, step.Argument)
+ def.Args = append(def.Args, step.Argument)
}
return def
}
@@ -70,23 +73,23 @@ func (s *suite) runStep(pickle *messages.Pickle, step *messages.Pickle_PickleSte
return
}
- sr := newStepResult(pickle.Id, step.Id, match)
+ sr := models.NewStepResult(pickle.Id, step.Id, match)
switch err {
case nil:
- sr.Status = passed
- s.storage.mustInsertPickleStepResult(sr)
+ sr.Status = models.Passed
+ s.storage.MustInsertPickleStepResult(sr)
s.fmt.Passed(pickle, step, match)
case ErrPending:
- sr.Status = pending
- s.storage.mustInsertPickleStepResult(sr)
+ sr.Status = models.Pending
+ s.storage.MustInsertPickleStepResult(sr)
s.fmt.Pending(pickle, step, match)
default:
- sr.Status = failed
- sr.err = err
- s.storage.mustInsertPickleStepResult(sr)
+ sr.Status = models.Failed
+ sr.Err = err
+ s.storage.MustInsertPickleStepResult(sr)
s.fmt.Failed(pickle, step, match, err)
}
@@ -101,34 +104,34 @@ func (s *suite) runStep(pickle *messages.Pickle, step *messages.Pickle_PickleSte
return err
} else if len(undef) > 0 {
if match != nil {
- match = &StepDefinition{
- args: match.args,
- hv: match.hv,
- Expr: match.Expr,
- Handler: match.Handler,
- nested: match.nested,
- undefined: undef,
+ match = &models.StepDefinition{
+ Args: match.Args,
+ HandlerValue: match.HandlerValue,
+ Expr: match.Expr,
+ Handler: match.Handler,
+ Nested: match.Nested,
+ Undefined: undef,
}
}
- sr := newStepResult(pickle.Id, step.Id, match)
- sr.Status = undefined
- s.storage.mustInsertPickleStepResult(sr)
+ sr := models.NewStepResult(pickle.Id, step.Id, match)
+ sr.Status = models.Undefined
+ s.storage.MustInsertPickleStepResult(sr)
s.fmt.Undefined(pickle, step, match)
return ErrUndefined
}
if prevStepErr != nil {
- sr := newStepResult(pickle.Id, step.Id, match)
- sr.Status = skipped
- s.storage.mustInsertPickleStepResult(sr)
+ sr := models.NewStepResult(pickle.Id, step.Id, match)
+ sr.Status = models.Skipped
+ s.storage.MustInsertPickleStepResult(sr)
s.fmt.Skipped(pickle, step, match)
return nil
}
- err = s.maybeSubSteps(match.run())
+ err = s.maybeSubSteps(match.Run())
return
}
@@ -139,15 +142,15 @@ func (s *suite) maybeUndefined(text string, arg interface{}) ([]string, error) {
}
var undefined []string
- if !step.nested {
+ if !step.Nested {
return undefined, nil
}
if arg != nil {
- step.args = append(step.args, arg)
+ step.Args = append(step.Args, arg)
}
- for _, next := range step.run().(Steps) {
+ for _, next := range step.Run().(Steps) {
lines := strings.Split(next, "\n")
// @TODO: we cannot currently parse table or content body from nested steps
if len(lines) > 1 {
@@ -182,14 +185,14 @@ func (s *suite) maybeSubSteps(result interface{}) error {
for _, text := range steps {
if def := s.matchStepText(text); def == nil {
return ErrUndefined
- } else if err := s.maybeSubSteps(def.run()); err != nil {
+ } else if err := s.maybeSubSteps(def.Run()); err != nil {
return fmt.Errorf("%s: %+v", text, err)
}
}
return nil
}
-func (s *suite) matchStepText(text string) *StepDefinition {
+func (s *suite) matchStepText(text string) *models.StepDefinition {
for _, h := range s.steps {
if m := h.Expr.FindStringSubmatch(text); len(m) > 0 {
var args []interface{}
@@ -199,12 +202,12 @@ func (s *suite) matchStepText(text string) *StepDefinition {
// since we need to assign arguments
// better to copy the step definition
- return &StepDefinition{
- args: args,
- hv: h.hv,
- Expr: h.Expr,
- Handler: h.Handler,
- nested: h.nested,
+ return &models.StepDefinition{
+ Args: args,
+ HandlerValue: h.HandlerValue,
+ Expr: h.Expr,
+ Handler: h.Handler,
+ Nested: h.Nested,
}
}
}
@@ -254,8 +257,8 @@ func isEmptyFeature(pickles []*messages.Pickle) bool {
func (s *suite) runPickle(pickle *messages.Pickle) (err error) {
if len(pickle.Steps) == 0 {
- pr := pickleResult{PickleID: pickle.Id, StartedAt: timeNowFunc()}
- s.storage.mustInsertPickleResult(pr)
+ pr := models.PickleResult{PickleID: pickle.Id, StartedAt: utils.TimeNowFunc()}
+ s.storage.MustInsertPickleResult(pr)
s.fmt.Pickle(pickle)
return ErrUndefined
@@ -266,8 +269,8 @@ func (s *suite) runPickle(pickle *messages.Pickle) (err error) {
f(pickle)
}
- pr := pickleResult{PickleID: pickle.Id, StartedAt: timeNowFunc()}
- s.storage.mustInsertPickleResult(pr)
+ pr := models.PickleResult{PickleID: pickle.Id, StartedAt: utils.TimeNowFunc()}
+ s.storage.MustInsertPickleResult(pr)
s.fmt.Pickle(pickle)
diff --git a/suite_context_test.go b/suite_context_test.go
index f7952f8..83eba78 100644
--- a/suite_context_test.go
+++ b/suite_context_test.go
@@ -15,7 +15,11 @@ import (
"github.com/stretchr/testify/assert"
"github.com/cucumber/godog/colors"
+ "github.com/cucumber/godog/internal/formatters"
+ "github.com/cucumber/godog/internal/models"
+ "github.com/cucumber/godog/internal/storage"
"github.com/cucumber/godog/internal/tags"
+ "github.com/cucumber/godog/internal/utils"
)
// InitializeScenario provides steps for godog suite execution and
@@ -142,7 +146,7 @@ type firedEvent struct {
type godogFeaturesScenario struct {
paths []string
- features []*feature
+ features []*models.Feature
testedSuite *suite
testSuiteContext TestSuiteContext
events []*firedEvent
@@ -155,7 +159,7 @@ func (tc *godogFeaturesScenario) ResetBeforeEachScenario(*Scenario) {
tc.out.Reset()
tc.paths = []string{}
- tc.features = []*feature{}
+ tc.features = []*models.Feature{}
tc.testedSuite = &suite{}
tc.testSuiteContext = TestSuiteContext{}
@@ -170,7 +174,7 @@ func (tc *godogFeaturesScenario) iSetVariableInjectionTo(to string) error {
}
func (tc *godogFeaturesScenario) iRunFeatureSuiteWithTags(tags string) error {
- return tc.iRunFeatureSuiteWithTagsAndFormatter(tags, baseFmtFunc)
+ return tc.iRunFeatureSuiteWithTagsAndFormatter(tags, formatters.BaseFormatterFunc)
}
func (tc *godogFeaturesScenario) iRunFeatureSuiteWithFormatter(name string) error {
@@ -188,25 +192,25 @@ func (tc *godogFeaturesScenario) iRunFeatureSuiteWithTagsAndFormatter(filter str
}
for _, feat := range tc.features {
- feat.pickles = tags.ApplyTagFilter(filter, feat.pickles)
+ feat.Pickles = tags.ApplyTagFilter(filter, feat.Pickles)
}
- tc.testedSuite.storage = newStorage()
+ tc.testedSuite.storage = storage.NewStorage()
for _, feat := range tc.features {
- tc.testedSuite.storage.mustInsertFeature(feat)
+ tc.testedSuite.storage.MustInsertFeature(feat)
- for _, pickle := range feat.pickles {
- tc.testedSuite.storage.mustInsertPickle(pickle)
+ for _, pickle := range feat.Pickles {
+ tc.testedSuite.storage.MustInsertPickle(pickle)
}
}
tc.testedSuite.fmt = fmtFunc("godog", colors.Uncolored(&tc.out))
if fmt, ok := tc.testedSuite.fmt.(storageFormatter); ok {
- fmt.setStorage(tc.testedSuite.storage)
+ fmt.SetStorage(tc.testedSuite.storage)
}
- testRunStarted := testRunStarted{StartedAt: timeNowFunc()}
- tc.testedSuite.storage.mustInsertTestRunStarted(testRunStarted)
+ testRunStarted := models.TestRunStarted{StartedAt: utils.TimeNowFunc()}
+ tc.testedSuite.storage.MustInsertTestRunStarted(testRunStarted)
tc.testedSuite.fmt.TestRunStarted()
for _, f := range tc.testSuiteContext.beforeSuiteHandlers {
@@ -214,9 +218,9 @@ func (tc *godogFeaturesScenario) iRunFeatureSuiteWithTagsAndFormatter(filter str
}
for _, ft := range tc.features {
- tc.testedSuite.fmt.Feature(ft.GherkinDocument, ft.Uri, ft.content)
+ tc.testedSuite.fmt.Feature(ft.GherkinDocument, ft.Uri, ft.Content)
- for _, pickle := range ft.pickles {
+ for _, pickle := range ft.Pickles {
if tc.testedSuite.stopOnFailure && tc.testedSuite.failed {
continue
}
@@ -278,16 +282,16 @@ func (tc *godogFeaturesScenario) cleanupSnippet(snip string) string {
}
func (tc *godogFeaturesScenario) theUndefinedStepSnippetsShouldBe(body *DocString) error {
- f, ok := tc.testedSuite.fmt.(*basefmt)
+ f, ok := tc.testedSuite.fmt.(*formatters.Basefmt)
if !ok {
- return fmt.Errorf("this step requires *basefmt, but there is: %T", tc.testedSuite.fmt)
+ return fmt.Errorf("this step requires *formatters.Basefmt, but there is: %T", tc.testedSuite.fmt)
}
- actual := tc.cleanupSnippet(f.snippets())
+ actual := tc.cleanupSnippet(f.Snippets())
expected := tc.cleanupSnippet(body.Content)
if actual != expected {
- return fmt.Errorf("snippets do not match actual: %s", f.snippets())
+ return fmt.Errorf("snippets do not match actual: %s", f.Snippets())
}
return nil
@@ -297,35 +301,32 @@ func (tc *godogFeaturesScenario) followingStepsShouldHave(status string, steps *
var expected = strings.Split(steps.Content, "\n")
var actual, unmatched, matched []string
- f, ok := tc.testedSuite.fmt.(*basefmt)
- if !ok {
- return fmt.Errorf("this step requires *basefmt, but there is: %T", tc.testedSuite.fmt)
- }
+ storage := tc.testedSuite.storage
switch status {
case "passed":
- for _, st := range f.storage.mustGetPickleStepResultsByStatus(passed) {
- pickleStep := f.storage.mustGetPickleStep(st.PickleStepID)
+ for _, st := range storage.MustGetPickleStepResultsByStatus(models.Passed) {
+ pickleStep := storage.MustGetPickleStep(st.PickleStepID)
actual = append(actual, pickleStep.Text)
}
case "failed":
- for _, st := range f.storage.mustGetPickleStepResultsByStatus(failed) {
- pickleStep := f.storage.mustGetPickleStep(st.PickleStepID)
+ for _, st := range storage.MustGetPickleStepResultsByStatus(models.Failed) {
+ pickleStep := storage.MustGetPickleStep(st.PickleStepID)
actual = append(actual, pickleStep.Text)
}
case "skipped":
- for _, st := range f.storage.mustGetPickleStepResultsByStatus(skipped) {
- pickleStep := f.storage.mustGetPickleStep(st.PickleStepID)
+ for _, st := range storage.MustGetPickleStepResultsByStatus(models.Skipped) {
+ pickleStep := storage.MustGetPickleStep(st.PickleStepID)
actual = append(actual, pickleStep.Text)
}
case "undefined":
- for _, st := range f.storage.mustGetPickleStepResultsByStatus(undefined) {
- pickleStep := f.storage.mustGetPickleStep(st.PickleStepID)
+ for _, st := range storage.MustGetPickleStepResultsByStatus(models.Undefined) {
+ pickleStep := storage.MustGetPickleStep(st.PickleStepID)
actual = append(actual, pickleStep.Text)
}
case "pending":
- for _, st := range f.storage.mustGetPickleStepResultsByStatus(pending) {
- pickleStep := f.storage.mustGetPickleStep(st.PickleStepID)
+ for _, st := range storage.MustGetPickleStepResultsByStatus(models.Pending) {
+ pickleStep := storage.MustGetPickleStep(st.PickleStepID)
actual = append(actual, pickleStep.Text)
}
default:
@@ -406,7 +407,7 @@ func (tc *godogFeaturesScenario) aFeatureFile(path string, body *DocString) erro
gd.Uri = path
pickles := gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId)
- tc.features = append(tc.features, &feature{GherkinDocument: gd, pickles: pickles})
+ tc.features = append(tc.features, &models.Feature{GherkinDocument: gd, Pickles: pickles})
return err
}
@@ -486,7 +487,7 @@ func (tc *godogFeaturesScenario) iRunFeatureSuite() error {
func (tc *godogFeaturesScenario) numScenariosRegistered(expected int) (err error) {
var num int
for _, ft := range tc.features {
- num += len(ft.pickles)
+ num += len(ft.Pickles)
}
if num != expected {
@@ -571,12 +572,12 @@ func (tc *godogFeaturesScenario) theRenderJSONWillBe(docstring *DocString) error
actualString := tc.out.String()
actualString = actualSuiteCtxReg.ReplaceAllString(actualString, `suite_context_test.go:0`)
- var expected []cukeFeatureJSON
+ var expected []formatters.CukeFeatureJSON
if err := json.Unmarshal([]byte(expectedString), &expected); err != nil {
return err
}
- var actual []cukeFeatureJSON
+ var actual []formatters.CukeFeatureJSON
if err := json.Unmarshal([]byte(actualString), &actual); err != nil {
return err
}
@@ -614,12 +615,12 @@ func (tc *godogFeaturesScenario) theRenderXMLWillBe(docstring *DocString) error
expectedString := docstring.Content
actualString := tc.out.String()
- var expected junitPackageSuite
+ var expected formatters.JunitPackageSuite
if err := xml.Unmarshal([]byte(expectedString), &expected); err != nil {
return err
}
- var actual junitPackageSuite
+ var actual formatters.JunitPackageSuite
if err := xml.Unmarshal([]byte(actualString), &actual); err != nil {
return err
}
@@ -642,3 +643,11 @@ type asserter struct {
func (a *asserter) Errorf(format string, args ...interface{}) {
a.err = fmt.Errorf(format, args...)
}
+
+func trimAllLines(s string) string {
+ var lines []string
+ for _, ln := range strings.Split(strings.TrimSpace(s), "\n") {
+ lines = append(lines, strings.TrimSpace(ln))
+ }
+ return strings.Join(lines, "\n")
+}
diff --git a/test_context.go b/test_context.go
index c209d1b..03554c4 100644
--- a/test_context.go
+++ b/test_context.go
@@ -7,6 +7,8 @@ import (
"github.com/cucumber/godog/internal/builder"
"github.com/cucumber/messages-go/v10"
+
+ "github.com/cucumber/godog/internal/models"
)
// Scenario represents the executed scenario
@@ -15,6 +17,32 @@ type Scenario = messages.Pickle
// Step represents the executed step
type Step = messages.Pickle_PickleStep
+// Steps allows to nest steps
+// instead of returning an error in step func
+// it is possible to return combined steps:
+//
+// func multistep(name string) godog.Steps {
+// return godog.Steps{
+// fmt.Sprintf(`an user named "%s"`, name),
+// fmt.Sprintf(`user "%s" is authenticated`, name),
+// }
+// }
+//
+// These steps will be matched and executed in
+// sequential order. The first one which fails
+// will result in main step failure.
+type Steps []string
+
+// StepDefinition is a registered step definition
+// contains a StepHandler and regexp which
+// is used to match a step. Args which
+// were matched by last executed step
+//
+// This structure is passed to the formatter
+// when step is matched and is either failed
+// or successful
+type StepDefinition = models.StepDefinition
+
// DocString represents the DocString argument made to a step definition
type DocString = messages.PickleStepArgument_PickleDocString
@@ -152,9 +180,9 @@ func (ctx *ScenarioContext) Step(expr, stepFunc interface{}) {
}
def := &StepDefinition{
- Handler: stepFunc,
- Expr: regex,
- hv: v,
+ Handler: stepFunc,
+ Expr: regex,
+ HandlerValue: v,
}
typ = typ.Out(0)
@@ -167,7 +195,7 @@ func (ctx *ScenarioContext) Step(expr, stepFunc interface{}) {
if typ.Elem().Kind() != reflect.String {
panic(fmt.Sprintf("expected handler to return []string for multistep, but got: []%s", typ.Kind()))
}
- def.nested = true
+ def.Nested = true
default:
panic(fmt.Sprintf("expected handler to return an error or []string, but got: %s", typ.Kind()))
}
diff --git a/utils.go b/utils.go
deleted file mode 100644
index 584394b..0000000
--- a/utils.go
+++ /dev/null
@@ -1,60 +0,0 @@
-package godog
-
-import (
- "strconv"
- "strings"
- "time"
-
- "github.com/cucumber/godog/colors"
- "github.com/cucumber/messages-go/v10"
-)
-
-var (
- red = colors.Red
- redb = colors.Bold(colors.Red)
- green = colors.Green
- blackb = colors.Bold(colors.Black)
- yellow = colors.Yellow
- cyan = colors.Cyan
- cyanb = colors.Bold(colors.Cyan)
- whiteb = colors.Bold(colors.White)
-)
-
-// repeats a space n times
-func s(n int) string {
- if n < 0 {
- n = 1
- }
- return strings.Repeat(" ", n)
-}
-
-var timeNowFunc = func() time.Time {
- return time.Now()
-}
-
-func trimAllLines(s string) string {
- var lines []string
- for _, ln := range strings.Split(strings.TrimSpace(s), "\n") {
- lines = append(lines, strings.TrimSpace(ln))
- }
- return strings.Join(lines, "\n")
-}
-
-type sortPicklesByID []*messages.Pickle
-
-func (s sortPicklesByID) Len() int { return len(s) }
-func (s sortPicklesByID) Less(i, j int) bool {
- iID := mustConvertStringToInt(s[i].Id)
- jID := mustConvertStringToInt(s[j].Id)
- return iID < jID
-}
-func (s sortPicklesByID) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
-
-func mustConvertStringToInt(s string) int {
- i, err := strconv.Atoi(s)
- if err != nil {
- panic(err)
- }
-
- return i
-}
diff --git a/utils_test.go b/utils_test.go
index dda2d74..a8ebe13 100644
--- a/utils_test.go
+++ b/utils_test.go
@@ -3,19 +3,21 @@ package godog
import (
"testing"
"time"
+
+ "github.com/cucumber/godog/internal/utils"
)
// this zeroes the time throughout whole test suite
// and makes it easier to assert output
// activated only when godog tests are being run
func init() {
- timeNowFunc = func() time.Time {
+ utils.TimeNowFunc = func() time.Time {
return time.Time{}
}
}
func TestTimeNowFunc(t *testing.T) {
- now := timeNowFunc()
+ now := utils.TimeNowFunc()
if !now.IsZero() {
t.Fatalf("expected zeroed time, but got: %s", now.Format(time.RFC3339))
}