diff --git a/fmt.go b/fmt.go
index 5327d01..ee4dab4 100644
--- a/fmt.go
+++ b/fmt.go
@@ -75,17 +75,6 @@ type Formatter interface {
Summary()
}
-// ConcurrentFormatter is an interface for a Concurrent
-// version of the Formatter interface.
-//
-// Deprecated: The formatters need to handle concurrency
-// instead of being copied and synchronized for each thread.
-type ConcurrentFormatter interface {
- Formatter
- Copy(ConcurrentFormatter)
- Sync(ConcurrentFormatter)
-}
-
type storageFormatter interface {
setStorage(*storage)
}
diff --git a/fmt_output_test.go b/fmt_output_test.go
index 4d7e835..329676e 100644
--- a/fmt_output_test.go
+++ b/fmt_output_test.go
@@ -60,13 +60,6 @@ func listFmtOutputTestsFeatureFiles() (featureFiles []string, err error) {
}
func fmtOutputTest(fmtName, testName, featureFilePath string) func(*testing.T) {
- fmtOutputSuiteInitializer := func(s *godog.Suite) {
- s.Step(`^(?:a )?failing step`, failingStepDef)
- s.Step(`^(?:a )?pending step$`, pendingStepDef)
- s.Step(`^(?:a )?passing step$`, passingStepDef)
- s.Step(`^odd (\d+) and even (\d+) number$`, oddEvenStepDef)
- }
-
fmtOutputScenarioInitializer := func(ctx *godog.ScenarioContext) {
ctx.Step(`^(?:a )?failing step`, failingStepDef)
ctx.Step(`^(?:a )?pending step$`, pendingStepDef)
@@ -93,22 +86,14 @@ func fmtOutputTest(fmtName, testName, featureFilePath string) func(*testing.T) {
Output: out,
}
- godog.RunWithOptions(fmtName, fmtOutputSuiteInitializer, opts)
-
- expected := string(expectedOutput)
- actual := buf.String()
- assert.Equalf(t, expected, actual, "path: %s", expectOutputPath)
-
- buf.Reset()
-
godog.TestSuite{
Name: fmtName,
ScenarioInitializer: fmtOutputScenarioInitializer,
Options: &opts,
}.Run()
- expected = string(expectedOutput)
- actual = buf.String()
+ expected := string(expectedOutput)
+ actual := buf.String()
assert.Equalf(t, expected, actual, "path: %s", expectOutputPath)
}
}
diff --git a/fmt_progress_test.go b/fmt_progress_test.go
index 454d232..a2b281c 100644
--- a/fmt_progress_test.go
+++ b/fmt_progress_test.go
@@ -21,7 +21,7 @@ Feature: basic
Then two
`
-func TestProgressFormatterWhenStepPanics(t *testing.T) {
+func Test_ProgressFormatterWhenStepPanics(t *testing.T) {
const path = "any.feature"
gd, err := gherkin.ParseGherkinDocument(strings.NewReader(basicGherkinFeature), (&messages.Incrementing{}).NewId)
@@ -36,9 +36,9 @@ func TestProgressFormatterWhenStepPanics(t *testing.T) {
r := runner{
fmt: progressFunc("progress", w),
features: []*feature{&ft},
- initializer: func(s *Suite) {
- s.Step(`^one$`, func() error { return nil })
- s.Step(`^two$`, func() error { panic("omg") })
+ scenarioInitializer: func(ctx *ScenarioContext) {
+ ctx.Step(`^one$`, func() error { return nil })
+ ctx.Step(`^two$`, func() error { panic("omg") })
},
}
@@ -48,14 +48,14 @@ func TestProgressFormatterWhenStepPanics(t *testing.T) {
r.storage.mustInsertPickle(pickle)
}
- failed := r.concurrent(1, func() Formatter { return progressFunc("progress", w) })
+ failed := r.concurrent(1)
require.True(t, failed)
actual := buf.String()
assert.Contains(t, actual, "godog/fmt_progress_test.go:41")
}
-func TestProgressFormatterWithPanicInMultistep(t *testing.T) {
+func Test_ProgressFormatterWithPanicInMultistep(t *testing.T) {
const path = "any.feature"
gd, err := gherkin.ParseGherkinDocument(strings.NewReader(basicGherkinFeature), (&messages.Incrementing{}).NewId)
@@ -70,12 +70,12 @@ func TestProgressFormatterWithPanicInMultistep(t *testing.T) {
r := runner{
fmt: progressFunc("progress", w),
features: []*feature{&ft},
- initializer: func(s *Suite) {
- s.Step(`^sub1$`, func() error { return nil })
- s.Step(`^sub-sub$`, func() error { return nil })
- s.Step(`^sub2$`, func() []string { return []string{"sub-sub", "sub1", "one"} })
- s.Step(`^one$`, func() error { return nil })
- s.Step(`^two$`, func() []string { return []string{"sub1", "sub2"} })
+ scenarioInitializer: func(ctx *ScenarioContext) {
+ ctx.Step(`^sub1$`, func() error { return nil })
+ ctx.Step(`^sub-sub$`, func() error { return nil })
+ ctx.Step(`^sub2$`, func() []string { return []string{"sub-sub", "sub1", "one"} })
+ ctx.Step(`^one$`, func() error { return nil })
+ ctx.Step(`^two$`, func() []string { return []string{"sub1", "sub2"} })
},
}
@@ -85,11 +85,11 @@ func TestProgressFormatterWithPanicInMultistep(t *testing.T) {
r.storage.mustInsertPickle(pickle)
}
- failed := r.concurrent(1, func() Formatter { return progressFunc("progress", w) })
+ failed := r.concurrent(1)
require.True(t, failed)
}
-func TestProgressFormatterMultistepTemplates(t *testing.T) {
+func Test_ProgressFormatterMultistepTemplates(t *testing.T) {
const path = "any.feature"
gd, err := gherkin.ParseGherkinDocument(strings.NewReader(basicGherkinFeature), (&messages.Incrementing{}).NewId)
@@ -104,11 +104,11 @@ func TestProgressFormatterMultistepTemplates(t *testing.T) {
r := runner{
fmt: progressFunc("progress", w),
features: []*feature{&ft},
- initializer: func(s *Suite) {
- s.Step(`^sub-sub$`, func() error { return nil })
- s.Step(`^substep$`, func() Steps { return Steps{"sub-sub", `unavailable "John" cost 5`, "one", "three"} })
- s.Step(`^one$`, func() error { return nil })
- s.Step(`^(t)wo$`, func(s string) Steps { return Steps{"undef", "substep"} })
+ 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"} })
+ ctx.Step(`^one$`, func() error { return nil })
+ ctx.Step(`^(t)wo$`, func(s string) Steps { return Steps{"undef", "substep"} })
},
}
@@ -118,7 +118,7 @@ func TestProgressFormatterMultistepTemplates(t *testing.T) {
r.storage.mustInsertPickle(pickle)
}
- failed := r.concurrent(1, func() Formatter { return progressFunc("progress", w) })
+ failed := r.concurrent(1)
require.False(t, failed)
expected := `.U 2
@@ -154,7 +154,7 @@ func FeatureContext(s *godog.Suite) {
assert.Equal(t, expected, actual)
}
-func TestProgressFormatterWhenMultiStepHasArgument(t *testing.T) {
+func Test_ProgressFormatterWhenMultiStepHasArgument(t *testing.T) {
const path = "any.feature"
var featureSource = `
@@ -180,9 +180,9 @@ Feature: basic
r := runner{
fmt: progressFunc("progress", w),
features: []*feature{&ft},
- initializer: func(s *Suite) {
- s.Step(`^one$`, func() error { return nil })
- s.Step(`^two:$`, func(doc *messages.PickleStepArgument_PickleDocString) Steps { return Steps{"one"} })
+ scenarioInitializer: func(ctx *ScenarioContext) {
+ ctx.Step(`^one$`, func() error { return nil })
+ ctx.Step(`^two:$`, func(doc *messages.PickleStepArgument_PickleDocString) Steps { return Steps{"one"} })
},
}
@@ -192,11 +192,11 @@ Feature: basic
r.storage.mustInsertPickle(pickle)
}
- failed := r.concurrent(1, func() Formatter { return progressFunc("progress", w) })
+ failed := r.concurrent(1)
require.False(t, failed)
}
-func TestProgressFormatterWhenMultiStepHasStepWithArgument(t *testing.T) {
+func Test_ProgressFormatterWhenMultiStepHasStepWithArgument(t *testing.T) {
const path = "any.feature"
var featureSource = `
@@ -223,10 +223,10 @@ Feature: basic
r := runner{
fmt: progressFunc("progress", w),
features: []*feature{&ft},
- initializer: func(s *Suite) {
- s.Step(`^one$`, func() error { return nil })
- s.Step(`^two$`, func() Steps { return Steps{subStep} })
- s.Step(`^three:$`, func(doc *messages.PickleStepArgument_PickleDocString) error { return nil })
+ scenarioInitializer: func(ctx *ScenarioContext) {
+ ctx.Step(`^one$`, func() error { return nil })
+ ctx.Step(`^two$`, func() Steps { return Steps{subStep} })
+ ctx.Step(`^three:$`, func(doc *messages.PickleStepArgument_PickleDocString) error { return nil })
},
}
@@ -236,7 +236,7 @@ Feature: basic
r.storage.mustInsertPickle(pickle)
}
- failed := r.concurrent(1, func() Formatter { return progressFunc("progress", w) })
+ failed := r.concurrent(1)
require.True(t, failed)
expected := `.F 2
diff --git a/formatter-tests/cucumber/scenario_outline b/formatter-tests/cucumber/scenario_outline
index 295188c..5b44252 100644
--- a/formatter-tests/cucumber/scenario_outline
+++ b/formatter-tests/cucumber/scenario_outline
@@ -48,7 +48,7 @@
"name": "passing step",
"line": 13,
"match": {
- "location": "fmt_output_test.go:116"
+ "location": "fmt_output_test.go:101"
},
"result": {
"status": "passed",
@@ -60,7 +60,7 @@
"name": "passing step",
"line": 13,
"match": {
- "location": "fmt_output_test.go:116"
+ "location": "fmt_output_test.go:101"
},
"result": {
"status": "passed",
@@ -72,7 +72,7 @@
"name": "odd 1 and even 2 number",
"line": 13,
"match": {
- "location": "fmt_output_test.go:118"
+ "location": "fmt_output_test.go:103"
},
"result": {
"status": "passed",
@@ -112,7 +112,7 @@
"name": "passing step",
"line": 14,
"match": {
- "location": "fmt_output_test.go:116"
+ "location": "fmt_output_test.go:101"
},
"result": {
"status": "passed",
@@ -124,7 +124,7 @@
"name": "passing step",
"line": 14,
"match": {
- "location": "fmt_output_test.go:116"
+ "location": "fmt_output_test.go:101"
},
"result": {
"status": "passed",
@@ -136,7 +136,7 @@
"name": "odd 2 and even 0 number",
"line": 14,
"match": {
- "location": "fmt_output_test.go:118"
+ "location": "fmt_output_test.go:103"
},
"result": {
"status": "failed",
@@ -177,7 +177,7 @@
"name": "passing step",
"line": 15,
"match": {
- "location": "fmt_output_test.go:116"
+ "location": "fmt_output_test.go:101"
},
"result": {
"status": "passed",
@@ -189,7 +189,7 @@
"name": "passing step",
"line": 15,
"match": {
- "location": "fmt_output_test.go:116"
+ "location": "fmt_output_test.go:101"
},
"result": {
"status": "passed",
@@ -201,7 +201,7 @@
"name": "odd 3 and even 11 number",
"line": 15,
"match": {
- "location": "fmt_output_test.go:118"
+ "location": "fmt_output_test.go:103"
},
"result": {
"status": "failed",
@@ -242,7 +242,7 @@
"name": "passing step",
"line": 20,
"match": {
- "location": "fmt_output_test.go:116"
+ "location": "fmt_output_test.go:101"
},
"result": {
"status": "passed",
@@ -254,7 +254,7 @@
"name": "passing step",
"line": 20,
"match": {
- "location": "fmt_output_test.go:116"
+ "location": "fmt_output_test.go:101"
},
"result": {
"status": "passed",
@@ -266,7 +266,7 @@
"name": "odd 1 and even 14 number",
"line": 20,
"match": {
- "location": "fmt_output_test.go:118"
+ "location": "fmt_output_test.go:103"
},
"result": {
"status": "passed",
@@ -306,7 +306,7 @@
"name": "passing step",
"line": 21,
"match": {
- "location": "fmt_output_test.go:116"
+ "location": "fmt_output_test.go:101"
},
"result": {
"status": "passed",
@@ -318,7 +318,7 @@
"name": "passing step",
"line": 21,
"match": {
- "location": "fmt_output_test.go:116"
+ "location": "fmt_output_test.go:101"
},
"result": {
"status": "passed",
@@ -330,7 +330,7 @@
"name": "odd 3 and even 9 number",
"line": 21,
"match": {
- "location": "fmt_output_test.go:118"
+ "location": "fmt_output_test.go:103"
},
"result": {
"status": "failed",
diff --git a/formatter-tests/cucumber/scenario_with_background b/formatter-tests/cucumber/scenario_with_background
index f5362ef..b6248db 100644
--- a/formatter-tests/cucumber/scenario_with_background
+++ b/formatter-tests/cucumber/scenario_with_background
@@ -20,7 +20,7 @@
"name": "passing step",
"line": 4,
"match": {
- "location": "fmt_output_test.go:116"
+ "location": "fmt_output_test.go:101"
},
"result": {
"status": "passed",
@@ -32,7 +32,7 @@
"name": "passing step",
"line": 5,
"match": {
- "location": "fmt_output_test.go:116"
+ "location": "fmt_output_test.go:101"
},
"result": {
"status": "passed",
@@ -44,7 +44,7 @@
"name": "passing step",
"line": 8,
"match": {
- "location": "fmt_output_test.go:116"
+ "location": "fmt_output_test.go:101"
},
"result": {
"status": "passed",
@@ -56,7 +56,7 @@
"name": "passing step",
"line": 9,
"match": {
- "location": "fmt_output_test.go:116"
+ "location": "fmt_output_test.go:101"
},
"result": {
"status": "passed",
diff --git a/formatter-tests/cucumber/single_scenario_with_passing_step b/formatter-tests/cucumber/single_scenario_with_passing_step
index f1a676e..0a98c04 100644
--- a/formatter-tests/cucumber/single_scenario_with_passing_step
+++ b/formatter-tests/cucumber/single_scenario_with_passing_step
@@ -20,7 +20,7 @@
"name": "a passing step",
"line": 7,
"match": {
- "location": "fmt_output_test.go:116"
+ "location": "fmt_output_test.go:101"
},
"result": {
"status": "passed",
diff --git a/formatter-tests/cucumber/some_scenarions_including_failing b/formatter-tests/cucumber/some_scenarions_including_failing
index 0254428..a1864e1 100644
--- a/formatter-tests/cucumber/some_scenarions_including_failing
+++ b/formatter-tests/cucumber/some_scenarions_including_failing
@@ -20,7 +20,7 @@
"name": "passing step",
"line": 4,
"match": {
- "location": "fmt_output_test.go:116"
+ "location": "fmt_output_test.go:101"
},
"result": {
"status": "passed",
@@ -32,7 +32,7 @@
"name": "failing step",
"line": 5,
"match": {
- "location": "fmt_output_test.go:132"
+ "location": "fmt_output_test.go:117"
},
"result": {
"status": "failed",
@@ -45,7 +45,7 @@
"name": "passing step",
"line": 6,
"match": {
- "location": "fmt_output_test.go:116"
+ "location": "fmt_output_test.go:101"
},
"result": {
"status": "skipped"
@@ -77,7 +77,7 @@
"name": "passing step",
"line": 10,
"match": {
- "location": "fmt_output_test.go:116"
+ "location": "fmt_output_test.go:101"
},
"result": {
"status": "skipped"
@@ -109,7 +109,7 @@
"name": "passing step",
"line": 14,
"match": {
- "location": "fmt_output_test.go:116"
+ "location": "fmt_output_test.go:101"
},
"result": {
"status": "skipped"
diff --git a/formatter-tests/cucumber/two_scenarios_with_background_fail b/formatter-tests/cucumber/two_scenarios_with_background_fail
index 52b99f8..d4f25c3 100644
--- a/formatter-tests/cucumber/two_scenarios_with_background_fail
+++ b/formatter-tests/cucumber/two_scenarios_with_background_fail
@@ -20,7 +20,7 @@
"name": "passing step",
"line": 4,
"match": {
- "location": "fmt_output_test.go:116"
+ "location": "fmt_output_test.go:101"
},
"result": {
"status": "passed",
@@ -32,7 +32,7 @@
"name": "failing step",
"line": 5,
"match": {
- "location": "fmt_output_test.go:132"
+ "location": "fmt_output_test.go:117"
},
"result": {
"status": "failed",
@@ -45,7 +45,7 @@
"name": "passing step",
"line": 8,
"match": {
- "location": "fmt_output_test.go:116"
+ "location": "fmt_output_test.go:101"
},
"result": {
"status": "skipped"
@@ -56,7 +56,7 @@
"name": "passing step",
"line": 9,
"match": {
- "location": "fmt_output_test.go:116"
+ "location": "fmt_output_test.go:101"
},
"result": {
"status": "skipped"
@@ -77,7 +77,7 @@
"name": "passing step",
"line": 4,
"match": {
- "location": "fmt_output_test.go:116"
+ "location": "fmt_output_test.go:101"
},
"result": {
"status": "passed",
@@ -89,7 +89,7 @@
"name": "failing step",
"line": 5,
"match": {
- "location": "fmt_output_test.go:132"
+ "location": "fmt_output_test.go:117"
},
"result": {
"status": "failed",
@@ -102,7 +102,7 @@
"name": "passing step",
"line": 12,
"match": {
- "location": "fmt_output_test.go:116"
+ "location": "fmt_output_test.go:101"
},
"result": {
"status": "skipped"
diff --git a/formatter-tests/events/scenario_outline b/formatter-tests/events/scenario_outline
index 54271a6..bfb9dc6 100644
--- a/formatter-tests/events/scenario_outline
+++ b/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:116 -\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_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:116 -\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_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:118 -\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_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:116 -\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_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:116 -\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_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:118 -\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_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:116 -\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_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:116 -\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_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:118 -\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_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:116 -\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_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:116 -\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_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:118 -\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_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:116 -\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_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:116 -\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_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:118 -\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_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/formatter-tests/events/scenario_with_background
index 815b88d..629e76a 100644
--- a/formatter-tests/events/scenario_with_background
+++ b/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:116 -\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_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:116 -\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_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:116 -\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_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:116 -\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_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/single_scenario_with_passing_step b/formatter-tests/events/single_scenario_with_passing_step
index db1b578..b487e60 100644
--- a/formatter-tests/events/single_scenario_with_passing_step
+++ b/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:116 -\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_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/formatter-tests/events/some_scenarions_including_failing
index e159e22..d4bee67 100644
--- a/formatter-tests/events/some_scenarions_including_failing
+++ b/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:116 -\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_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:132 -\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_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:116 -\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_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:130 -\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_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:116 -\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_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:116 -\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_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/formatter-tests/events/two_scenarios_with_background_fail
index 8973ac9..faa05b6 100644
--- a/formatter-tests/events/two_scenarios_with_background_fail
+++ b/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:116 -\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_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:132 -\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_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:116 -\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_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:116 -\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_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:116 -\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_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:132 -\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_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:116 -\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_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/pretty/scenario_outline b/formatter-tests/pretty/scenario_outline
index f66bd5d..372412e 100644
--- a/formatter-tests/pretty/scenario_outline
+++ b/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:116 -> github.com/cucumber/godog_test.passingStepDef
- When passing step # fmt_output_test.go:116 -> github.com/cucumber/godog_test.passingStepDef
- Then odd and even number # fmt_output_test.go:118 -> github.com/cucumber/godog_test.oddEvenStepDef
+ 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
Examples: tagged
| odd | even |
diff --git a/formatter-tests/pretty/scenario_with_background b/formatter-tests/pretty/scenario_with_background
index f65cc06..22c75e3 100644
--- a/formatter-tests/pretty/scenario_with_background
+++ b/formatter-tests/pretty/scenario_with_background
@@ -1,12 +1,12 @@
Feature: single scenario with background
Background: named
- Given passing step # fmt_output_test.go:116 -> github.com/cucumber/godog_test.passingStepDef
- And passing step # fmt_output_test.go:116 -> github.com/cucumber/godog_test.passingStepDef
+ 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
Scenario: scenario # formatter-tests/features/scenario_with_background.feature:7
- When passing step # fmt_output_test.go:116 -> github.com/cucumber/godog_test.passingStepDef
- Then passing step # fmt_output_test.go:116 -> github.com/cucumber/godog_test.passingStepDef
+ 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
1 scenarios (1 passed)
4 steps (4 passed)
diff --git a/formatter-tests/pretty/single_scenario_with_passing_step b/formatter-tests/pretty/single_scenario_with_passing_step
index a21eca3..d24086f 100644
--- a/formatter-tests/pretty/single_scenario_with_passing_step
+++ b/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:116 -> github.com/cucumber/godog_test.passingStepDef
+ Given a passing step # fmt_output_test.go:101 -> github.com/cucumber/godog_test.passingStepDef
1 scenarios (1 passed)
1 steps (1 passed)
diff --git a/formatter-tests/pretty/some_scenarions_including_failing b/formatter-tests/pretty/some_scenarions_including_failing
index 1a634ca..689e4d4 100644
--- a/formatter-tests/pretty/some_scenarions_including_failing
+++ b/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:116 -> github.com/cucumber/godog_test.passingStepDef
- When failing step # fmt_output_test.go:132 -> github.com/cucumber/godog_test.failingStepDef
+ 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
step failed
- Then passing step # fmt_output_test.go:116 -> github.com/cucumber/godog_test.passingStepDef
+ Then passing step # fmt_output_test.go:101 -> github.com/cucumber/godog_test.passingStepDef
Scenario: pending # formatter-tests/features/some_scenarions_including_failing.feature:8
- When pending step # fmt_output_test.go:130 -> github.com/cucumber/godog_test.pendingStepDef
+ When pending step # fmt_output_test.go:115 -> github.com/cucumber/godog_test.pendingStepDef
TODO: write pending definition
- Then passing step # fmt_output_test.go:116 -> github.com/cucumber/godog_test.passingStepDef
+ Then passing step # fmt_output_test.go:101 -> github.com/cucumber/godog_test.passingStepDef
Scenario: undefined # formatter-tests/features/some_scenarions_including_failing.feature:12
When undefined
- Then passing step # fmt_output_test.go:116 -> github.com/cucumber/godog_test.passingStepDef
+ Then passing step # fmt_output_test.go:101 -> github.com/cucumber/godog_test.passingStepDef
--- Failed steps:
diff --git a/formatter-tests/pretty/two_scenarios_with_background_fail b/formatter-tests/pretty/two_scenarios_with_background_fail
index 4f9959c..643c910 100644
--- a/formatter-tests/pretty/two_scenarios_with_background_fail
+++ b/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:116 -> github.com/cucumber/godog_test.passingStepDef
- And failing step # fmt_output_test.go:132 -> github.com/cucumber/godog_test.failingStepDef
+ 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
step failed
Scenario: one # formatter-tests/features/two_scenarios_with_background_fail.feature:7
- When passing step # fmt_output_test.go:116 -> github.com/cucumber/godog_test.passingStepDef
- Then passing step # fmt_output_test.go:116 -> github.com/cucumber/godog_test.passingStepDef
+ 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
Scenario: two # formatter-tests/features/two_scenarios_with_background_fail.feature:11
- Then passing step # fmt_output_test.go:116 -> github.com/cucumber/godog_test.passingStepDef
+ Then passing step # fmt_output_test.go:101 -> github.com/cucumber/godog_test.passingStepDef
--- Failed steps:
diff --git a/run.go b/run.go
index 580142d..2d95a84 100644
--- a/run.go
+++ b/run.go
@@ -21,7 +21,6 @@ const (
exitOptionError
)
-type initializer func(*Suite)
type testSuiteInitializer func(*TestSuiteContext)
type scenarioInitializer func(*ScenarioContext)
@@ -31,7 +30,6 @@ type runner struct {
features []*feature
- initializer initializer
testSuiteInitializer testSuiteInitializer
scenarioInitializer scenarioInitializer
@@ -39,104 +37,7 @@ type runner struct {
fmt Formatter
}
-func (r *runner) concurrent(rate int, formatterFn func() Formatter) (failed bool) {
- var useFmtCopy bool
- var copyLock sync.Mutex
-
- // special mode for concurrent-formatter
- if _, ok := r.fmt.(ConcurrentFormatter); ok {
- useFmtCopy = true
- }
-
- if fmt, ok := r.fmt.(storageFormatter); ok {
- fmt.setStorage(r.storage)
- }
-
- testRunStarted := testRunStarted{StartedAt: timeNowFunc()}
- r.storage.mustInsertTestRunStarted(testRunStarted)
- r.fmt.TestRunStarted()
-
- queue := make(chan int, rate)
- for i, ft := range r.features {
- queue <- i // reserve space in queue
- ft := *ft
-
- go func(fail *bool, feat *feature) {
- var fmtCopy Formatter
-
- defer func() {
- <-queue // free a space in queue
- }()
-
- if r.stopOnFailure && *fail {
- return
- }
-
- suite := &Suite{
- fmt: r.fmt,
- randomSeed: r.randomSeed,
- stopOnFailure: r.stopOnFailure,
- strict: r.strict,
- features: []*feature{feat},
- storage: r.storage,
- }
-
- if useFmtCopy {
- fmtCopy = formatterFn()
- suite.fmt = fmtCopy
-
- concurrentDestFmt, dOk := fmtCopy.(ConcurrentFormatter)
- concurrentSourceFmt, sOk := r.fmt.(ConcurrentFormatter)
-
- if dOk && sOk {
- concurrentDestFmt.Sync(concurrentSourceFmt)
- }
- }
-
- if fmt, ok := suite.fmt.(storageFormatter); ok {
- fmt.setStorage(r.storage)
- }
-
- r.initializer(suite)
-
- suite.run()
-
- if suite.failed {
- copyLock.Lock()
- *fail = true
- copyLock.Unlock()
- }
-
- if useFmtCopy {
- copyLock.Lock()
-
- concurrentDestFmt, dOk := r.fmt.(ConcurrentFormatter)
- concurrentSourceFmt, sOk := fmtCopy.(ConcurrentFormatter)
-
- if dOk && sOk {
- concurrentDestFmt.Copy(concurrentSourceFmt)
- } else if !dOk {
- panic("cant cast dest formatter to progress-typed")
- } else if !sOk {
- panic("cant cast source formatter to progress-typed")
- }
-
- copyLock.Unlock()
- }
- }(&failed, &ft)
- }
- // wait until last are processed
- for i := 0; i < rate; i++ {
- queue <- i
- }
- close(queue)
-
- // print summary
- r.fmt.Summary()
- return
-}
-
-func (r *runner) scenarioConcurrent(rate int) (failed bool) {
+func (r *runner) concurrent(rate int) (failed bool) {
var copyLock sync.Mutex
if fmt, ok := r.fmt.(storageFormatter); ok {
@@ -188,7 +89,7 @@ func (r *runner) scenarioConcurrent(rate int) (failed bool) {
return
}
- suite := &Suite{
+ suite := &suite{
fmt: r.fmt,
randomSeed: r.randomSeed,
strict: r.strict,
@@ -227,38 +128,7 @@ func (r *runner) scenarioConcurrent(rate int) (failed bool) {
return
}
-// RunWithOptions is same as Run function, except
-// it uses Options provided in order to run the
-// test suite without parsing flags
-//
-// This method is useful in case if you run
-// godog in for example TestMain function together
-// with go tests
-//
-// The exit codes may vary from:
-// 0 - success
-// 1 - failed
-// 2 - command line usage error
-// 128 - or higher, os signal related error exit codes
-//
-// If there are flag related errors they will
-// be directed to os.Stderr
-//
-// Deprecated: The current Suite initializer will be removed and replaced by
-// two initializers, one for the Test Suite and one for the Scenarios.
-// Use:
-// godog.TestSuite{
-// Name: name,
-// TestSuiteInitializer: testSuiteInitializer,
-// ScenarioInitializer: scenarioInitializer,
-// Options: &opts,
-// }.Run()
-// instead.
-func RunWithOptions(suite string, initializer func(*Suite), opt Options) int {
- return runWithOptions(suite, runner{initializer: initializer}, opt)
-}
-
-func runWithOptions(suite string, runner runner, opt Options) int {
+func runWithOptions(suiteName string, runner runner, opt Options) int {
var output io.Writer = os.Stdout
if nil != opt.Output {
output = opt.Output
@@ -271,8 +141,9 @@ func runWithOptions(suite string, runner runner, opt Options) int {
}
if opt.ShowStepDefinitions {
- s := &Suite{}
- runner.initializer(s)
+ s := suite{}
+ sc := ScenarioContext{suite: &s}
+ runner.scenarioInitializer(&sc)
printStepDefinitions(s.steps, output)
return exitOptionError
}
@@ -301,7 +172,7 @@ func runWithOptions(suite string, runner runner, opt Options) int {
))
return exitOptionError
}
- runner.fmt = formatter(suite, output)
+ runner.fmt = formatter(suiteName, output)
var err error
if runner.features, err = parseFeatures(opt.Tags, opt.Paths); err != nil {
@@ -333,12 +204,7 @@ func runWithOptions(suite string, runner runner, opt Options) int {
_, filename, _, _ := runtime.Caller(1)
os.Setenv("GODOG_TESTED_PACKAGE", runsFromPackage(filename))
- var failed bool
- if runner.initializer != nil {
- failed = runner.concurrent(opt.Concurrency, func() Formatter { return formatter(suite, output) })
- } else {
- failed = runner.scenarioConcurrent(opt.Concurrency)
- }
+ failed := runner.concurrent(opt.Concurrency)
// @TODO: should prevent from having these
os.Setenv("GODOG_SEED", "")
@@ -360,52 +226,6 @@ func runsFromPackage(fp string) string {
return dir
}
-// Run creates and runs the feature suite.
-// Reads all configuration options from flags.
-// uses contextInitializer to register contexts
-//
-// the concurrency option allows runner to
-// initialize a number of suites to be run
-// separately. Only progress formatter
-// is supported when concurrency level is
-// higher than 1
-//
-// contextInitializer must be able to register
-// the step definitions and event handlers.
-//
-// The exit codes may vary from:
-// 0 - success
-// 1 - failed
-// 2 - command line usage error
-// 128 - or higher, os signal related error exit codes
-//
-// If there are flag related errors they will
-// be directed to os.Stderr
-//
-// Deprecated: The current Suite initializer will be removed and replaced by
-// two initializers, one for the Test Suite and one for the Scenarios.
-// Use:
-// godog.TestSuite{
-// Name: name,
-// TestSuiteInitializer: testSuiteInitializer,
-// ScenarioInitializer: scenarioInitializer,
-// }.Run()
-// instead.
-func Run(suite string, initializer func(*Suite)) int {
- var opt Options
- opt.Output = colors.Colored(os.Stdout)
-
- flagSet := FlagSet(&opt)
- if err := flagSet.Parse(os.Args[1:]); err != nil {
- fmt.Fprintln(os.Stderr, err)
- return exitOptionError
- }
-
- opt.Paths = flagSet.Args()
-
- return RunWithOptions(suite, initializer, opt)
-}
-
// TestSuite allows for configuration
// of the Test Suite Execution
type TestSuite struct {
diff --git a/run_test.go b/run_test.go
index ab3611b..78a77be 100644
--- a/run_test.go
+++ b/run_test.go
@@ -26,7 +26,8 @@ func okStep() error {
func TestPrintsStepDefinitions(t *testing.T) {
var buf bytes.Buffer
w := colors.Uncolored(&buf)
- s := &Suite{}
+ s := suite{}
+ ctx := ScenarioContext{suite: &s}
steps := []string{
"^passing step$",
@@ -34,7 +35,7 @@ func TestPrintsStepDefinitions(t *testing.T) {
}
for _, step := range steps {
- s.Step(step, okStep)
+ ctx.Step(step, okStep)
}
printStepDefinitions(s.steps, w)
@@ -54,7 +55,7 @@ func TestPrintsStepDefinitions(t *testing.T) {
func TestPrintsNoStepDefinitionsIfNoneFound(t *testing.T) {
var buf bytes.Buffer
w := colors.Uncolored(&buf)
- s := &Suite{}
+ s := &suite{}
printStepDefinitions(s.steps, w)
@@ -62,7 +63,7 @@ func TestPrintsNoStepDefinitionsIfNoneFound(t *testing.T) {
assert.Equal(t, "there were no contexts registered, could not find any step definition..", out)
}
-func TestFailsOrPassesBasedOnStrictModeWhenHasPendingSteps(t *testing.T) {
+func Test_FailsOrPassesBasedOnStrictModeWhenHasPendingSteps(t *testing.T) {
const path = "any.feature"
gd, err := gherkin.ParseGherkinDocument(strings.NewReader(basicGherkinFeature), (&messages.Incrementing{}).NewId)
@@ -75,9 +76,9 @@ func TestFailsOrPassesBasedOnStrictModeWhenHasPendingSteps(t *testing.T) {
r := runner{
fmt: progressFunc("progress", ioutil.Discard),
features: []*feature{&ft},
- initializer: func(s *Suite) {
- s.Step(`^one$`, func() error { return nil })
- s.Step(`^two$`, func() error { return ErrPending })
+ scenarioInitializer: func(ctx *ScenarioContext) {
+ ctx.Step(`^one$`, func() error { return nil })
+ ctx.Step(`^two$`, func() error { return ErrPending })
},
}
@@ -87,15 +88,15 @@ func TestFailsOrPassesBasedOnStrictModeWhenHasPendingSteps(t *testing.T) {
r.storage.mustInsertPickle(pickle)
}
- failed := r.concurrent(1, func() Formatter { return progressFunc("progress", ioutil.Discard) })
+ failed := r.concurrent(1)
require.False(t, failed)
r.strict = true
- failed = r.concurrent(1, func() Formatter { return progressFunc("progress", ioutil.Discard) })
+ failed = r.concurrent(1)
require.True(t, failed)
}
-func TestFailsOrPassesBasedOnStrictModeWhenHasUndefinedSteps(t *testing.T) {
+func Test_FailsOrPassesBasedOnStrictModeWhenHasUndefinedSteps(t *testing.T) {
const path = "any.feature"
gd, err := gherkin.ParseGherkinDocument(strings.NewReader(basicGherkinFeature), (&messages.Incrementing{}).NewId)
@@ -108,8 +109,8 @@ func TestFailsOrPassesBasedOnStrictModeWhenHasUndefinedSteps(t *testing.T) {
r := runner{
fmt: progressFunc("progress", ioutil.Discard),
features: []*feature{&ft},
- initializer: func(s *Suite) {
- s.Step(`^one$`, func() error { return nil })
+ scenarioInitializer: func(ctx *ScenarioContext) {
+ ctx.Step(`^one$`, func() error { return nil })
// two - is undefined
},
}
@@ -120,15 +121,15 @@ func TestFailsOrPassesBasedOnStrictModeWhenHasUndefinedSteps(t *testing.T) {
r.storage.mustInsertPickle(pickle)
}
- failed := r.concurrent(1, func() Formatter { return progressFunc("progress", ioutil.Discard) })
+ failed := r.concurrent(1)
require.False(t, failed)
r.strict = true
- failed = r.concurrent(1, func() Formatter { return progressFunc("progress", ioutil.Discard) })
+ failed = r.concurrent(1)
require.True(t, failed)
}
-func TestShouldFailOnError(t *testing.T) {
+func Test_ShouldFailOnError(t *testing.T) {
const path = "any.feature"
gd, err := gherkin.ParseGherkinDocument(strings.NewReader(basicGherkinFeature), (&messages.Incrementing{}).NewId)
@@ -141,9 +142,9 @@ func TestShouldFailOnError(t *testing.T) {
r := runner{
fmt: progressFunc("progress", ioutil.Discard),
features: []*feature{&ft},
- initializer: func(s *Suite) {
- s.Step(`^one$`, func() error { return nil })
- s.Step(`^two$`, func() error { return fmt.Errorf("error") })
+ scenarioInitializer: func(ctx *ScenarioContext) {
+ ctx.Step(`^one$`, func() error { return nil })
+ ctx.Step(`^two$`, func() error { return fmt.Errorf("error") })
},
}
@@ -153,22 +154,27 @@ func TestShouldFailOnError(t *testing.T) {
r.storage.mustInsertPickle(pickle)
}
- failed := r.concurrent(1, func() Formatter { return progressFunc("progress", ioutil.Discard) })
+ failed := r.concurrent(1)
require.True(t, failed)
}
-func TestFailsWithUnknownFormatterOptionError(t *testing.T) {
+func Test_FailsWithUnknownFormatterOptionError(t *testing.T) {
stderr, closer := bufErrorPipe(t)
defer closer()
defer stderr.Close()
- opt := Options{
+ opts := Options{
Format: "unknown",
Paths: []string{"features/load:6"},
Output: ioutil.Discard,
}
- status := RunWithOptions("fails", func(_ *Suite) {}, opt)
+ status := TestSuite{
+ Name: "fails",
+ ScenarioInitializer: func(_ *ScenarioContext) {},
+ Options: &opts,
+ }.Run()
+
require.Equal(t, exitOptionError, status)
closer()
@@ -180,18 +186,23 @@ func TestFailsWithUnknownFormatterOptionError(t *testing.T) {
assert.Contains(t, out, `unregistered formatter name: "unknown", use one of`)
}
-func TestFailsWithOptionErrorWhenLookingForFeaturesInUnavailablePath(t *testing.T) {
+func Test_FailsWithOptionErrorWhenLookingForFeaturesInUnavailablePath(t *testing.T) {
stderr, closer := bufErrorPipe(t)
defer closer()
defer stderr.Close()
- opt := Options{
+ opts := Options{
Format: "progress",
Paths: []string{"unavailable"},
Output: ioutil.Discard,
}
- status := RunWithOptions("fails", func(_ *Suite) {}, opt)
+ status := TestSuite{
+ Name: "fails",
+ ScenarioInitializer: func(_ *ScenarioContext) {},
+ Options: &opts,
+ }.Run()
+
require.Equal(t, exitOptionError, status)
closer()
@@ -203,19 +214,29 @@ func TestFailsWithOptionErrorWhenLookingForFeaturesInUnavailablePath(t *testing.
assert.Equal(t, `feature path "unavailable" is not available`, out)
}
-func TestByDefaultRunsFeaturesPath(t *testing.T) {
- opt := Options{
+func Test_ByDefaultRunsFeaturesPath(t *testing.T) {
+ opts := Options{
Format: "progress",
Output: ioutil.Discard,
Strict: true,
}
- status := RunWithOptions("fails", func(_ *Suite) {}, opt)
+ status := TestSuite{
+ Name: "fails",
+ ScenarioInitializer: func(_ *ScenarioContext) {},
+ Options: &opts,
+ }.Run()
+
// should fail in strict mode due to undefined steps
assert.Equal(t, exitFailure, status)
- opt.Strict = false
- status = RunWithOptions("succeeds", func(_ *Suite) {}, opt)
+ opts.Strict = false
+ status = TestSuite{
+ Name: "succeeds",
+ ScenarioInitializer: func(_ *ScenarioContext) {},
+ Options: &opts,
+ }.Run()
+
// should succeed in non strict mode due to undefined steps
assert.Equal(t, exitSuccess, status)
}
@@ -232,7 +253,7 @@ func bufErrorPipe(t *testing.T) (io.ReadCloser, func()) {
}
}
-func TestFeatureFilePathParser(t *testing.T) {
+func Test_FeatureFilePathParser(t *testing.T) {
type Case struct {
input string
@@ -327,19 +348,8 @@ func Test_AllFeaturesRun(t *testing.T) {
0s
`
- fmtOutputSuiteInitializer := func(s *Suite) { SuiteContext(s) }
- fmtOutputScenarioInitializer := InitializeScenario
-
- actualStatus, actualOutput := testRunWithOptions(t,
- fmtOutputSuiteInitializer,
- format, concurrency, []string{"features"},
- )
-
- assert.Equal(t, exitSuccess, actualStatus)
- assert.Equal(t, expected, actualOutput)
-
- actualStatus, actualOutput = testRun(t,
- fmtOutputScenarioInitializer,
+ actualStatus, actualOutput := testRun(t,
+ InitializeScenario,
format, concurrency,
noRandomFlag, []string{"features"},
)
@@ -348,7 +358,7 @@ func Test_AllFeaturesRun(t *testing.T) {
assert.Equal(t, expected, actualOutput)
}
-func TestFormatterConcurrencyRun(t *testing.T) {
+func Test_FormatterConcurrencyRun(t *testing.T) {
formatters := []string{
"progress",
"junit",
@@ -363,13 +373,6 @@ func TestFormatterConcurrencyRun(t *testing.T) {
const noRandomFlag = 0
const noConcurrency = 1
- fmtOutputSuiteInitializer := func(s *Suite) {
- s.Step(`^(?:a )?failing step`, failingStepDef)
- s.Step(`^(?:a )?pending step$`, pendingStepDef)
- s.Step(`^(?:a )?passing step$`, passingStepDef)
- s.Step(`^odd (\d+) and even (\d+) number$`, oddEvenStepDef)
- }
-
fmtOutputScenarioInitializer := func(ctx *ScenarioContext) {
ctx.Step(`^(?:a )?failing step`, failingStepDef)
ctx.Step(`^(?:a )?pending step$`, pendingStepDef)
@@ -381,24 +384,12 @@ func TestFormatterConcurrencyRun(t *testing.T) {
t.Run(
fmt.Sprintf("%s/concurrency/%d", formatter, concurrency),
func(t *testing.T) {
- expectedStatus, expectedOutput := testRunWithOptions(t,
- fmtOutputSuiteInitializer,
- formatter, noConcurrency, featurePaths,
- )
- actualStatus, actualOutput := testRunWithOptions(t,
- fmtOutputSuiteInitializer,
- formatter, concurrency, featurePaths,
- )
-
- assert.Equal(t, expectedStatus, actualStatus)
- assertOutput(t, formatter, expectedOutput, actualOutput)
-
- expectedStatus, expectedOutput = testRun(t,
+ expectedStatus, expectedOutput := testRun(t,
fmtOutputScenarioInitializer,
formatter, noConcurrency,
noRandomFlag, featurePaths,
)
- actualStatus, actualOutput = testRun(t,
+ actualStatus, actualOutput := testRun(t,
fmtOutputScenarioInitializer,
formatter, concurrency,
noRandomFlag, featurePaths,
@@ -411,25 +402,6 @@ func TestFormatterConcurrencyRun(t *testing.T) {
}
}
-func testRunWithOptions(t *testing.T, initializer func(*Suite), format string, concurrency int, featurePaths []string) (int, string) {
- output := new(bytes.Buffer)
-
- opts := Options{
- Format: format,
- NoColors: true,
- Paths: featurePaths,
- Concurrency: concurrency,
- Output: output,
- }
-
- status := RunWithOptions("succeed", initializer, opts)
-
- actual, err := ioutil.ReadAll(output)
- require.NoError(t, err)
-
- return status, string(actual)
-}
-
func testRun(
t *testing.T,
scenarioInitializer func(*ScenarioContext),
diff --git a/suite.go b/suite.go
index ff13788..7ce2f26 100644
--- a/suite.go
+++ b/suite.go
@@ -2,9 +2,7 @@ package godog
import (
"fmt"
- "math/rand"
"reflect"
- "regexp"
"strings"
"github.com/cucumber/messages-go/v10"
@@ -20,24 +18,8 @@ var ErrUndefined = fmt.Errorf("step is undefined")
// step implementation is pending
var ErrPending = fmt.Errorf("step implementation is pending")
-// Suite allows various contexts
-// to register steps and event handlers.
-//
-// When running a test suite, the instance of Suite
-// is passed to all functions (contexts), which
-// have it as a first and only argument.
-//
-// Note that all event hooks does not catch panic errors
-// in order to have a trace information. Only step
-// executions are catching panic error since it may
-// be a context specific error.
-//
-// Deprecated: The current Suite initializer will be removed and replaced by
-// two initializers, one for the Test Suite and one for the Scenarios.
-// This struct will therefore not be exported in the future.
-type Suite struct {
- steps []*StepDefinition
- features []*feature
+type suite struct {
+ steps []*StepDefinition
fmt Formatter
storage *storage
@@ -48,175 +30,13 @@ type Suite struct {
strict bool
// suite event handlers
- beforeSuiteHandlers []func()
- beforeScenarioHandlers []func(*messages.Pickle)
- beforeStepHandlers []func(*messages.Pickle_PickleStep)
- afterStepHandlers []func(*messages.Pickle_PickleStep, error)
- afterScenarioHandlers []func(*messages.Pickle, error)
- afterSuiteHandlers []func()
+ beforeScenarioHandlers []func(*Scenario)
+ beforeStepHandlers []func(*Step)
+ afterStepHandlers []func(*Step, error)
+ afterScenarioHandlers []func(*Scenario, error)
}
-// Step allows to register a *StepDefinition in Godog
-// feature suite, the definition will be applied
-// to all steps matching the given Regexp expr.
-//
-// It will panic if expr is not a valid regular
-// expression or stepFunc is not a valid step
-// handler.
-//
-// Note that if there are two definitions which may match
-// the same step, then only the first matched handler
-// will be applied.
-//
-// If none of the *StepDefinition is matched, then
-// ErrUndefined error will be returned when
-// running steps.
-//
-// Deprecated: The current Suite initializer will be removed and replaced by
-// two initializers, one for the Test Suite and one for the Scenarios. Use
-// func (ctx *ScenarioContext) Step instead.
-func (s *Suite) Step(expr interface{}, stepFunc interface{}) {
- var regex *regexp.Regexp
-
- switch t := expr.(type) {
- case *regexp.Regexp:
- regex = t
- case string:
- regex = regexp.MustCompile(t)
- case []byte:
- regex = regexp.MustCompile(string(t))
- default:
- panic(fmt.Sprintf("expecting expr to be a *regexp.Regexp or a string, got type: %T", expr))
- }
-
- v := reflect.ValueOf(stepFunc)
- typ := v.Type()
- if typ.Kind() != reflect.Func {
- panic(fmt.Sprintf("expected handler to be func, but got: %T", stepFunc))
- }
-
- if typ.NumOut() != 1 {
- panic(fmt.Sprintf("expected handler to return only one value, but it has: %d", typ.NumOut()))
- }
-
- def := &StepDefinition{
- Handler: stepFunc,
- Expr: regex,
- hv: v,
- }
-
- typ = typ.Out(0)
- switch typ.Kind() {
- case reflect.Interface:
- if !typ.Implements(errorInterface) {
- panic(fmt.Sprintf("expected handler to return an error, but got: %s", typ.Kind()))
- }
- case reflect.Slice:
- if typ.Elem().Kind() != reflect.String {
- panic(fmt.Sprintf("expected handler to return []string for multistep, but got: []%s", typ.Kind()))
- }
- def.nested = true
- default:
- panic(fmt.Sprintf("expected handler to return an error or []string, but got: %s", typ.Kind()))
- }
-
- s.steps = append(s.steps, def)
-}
-
-// BeforeSuite registers a function or method
-// to be run once before suite runner.
-//
-// Use it to prepare the test suite for a spin.
-// Connect and prepare database for instance...
-//
-// Deprecated: The current Suite initializer will be removed and replaced by
-// two initializers, one for the Test Suite and one for the Scenarios. Use
-// func (ctx *TestSuiteContext) BeforeSuite instead.
-func (s *Suite) BeforeSuite(fn func()) {
- s.beforeSuiteHandlers = append(s.beforeSuiteHandlers, fn)
-}
-
-// BeforeScenario registers a function or method
-// to be run before every pickle.
-//
-// It is a good practice to restore the default state
-// before every scenario so it would be isolated from
-// any kind of state.
-//
-// Deprecated: The current Suite initializer will be removed and replaced by
-// two initializers, one for the Test Suite and one for the Scenarios. Use
-// func (ctx *ScenarioContext) BeforeScenario instead.
-func (s *Suite) BeforeScenario(fn func(*messages.Pickle)) {
- s.beforeScenarioHandlers = append(s.beforeScenarioHandlers, fn)
-}
-
-// BeforeStep registers a function or method
-// to be run before every step.
-//
-// Deprecated: The current Suite initializer will be removed and replaced by
-// two initializers, one for the Test Suite and one for the Scenarios. Use
-// func (ctx *ScenarioContext) BeforeStep instead.
-func (s *Suite) BeforeStep(fn func(*messages.Pickle_PickleStep)) {
- s.beforeStepHandlers = append(s.beforeStepHandlers, fn)
-}
-
-// AfterStep registers an function or method
-// to be run after every step.
-//
-// It may be convenient to return a different kind of error
-// in order to print more state details which may help
-// in case of step failure
-//
-// In some cases, for example when running a headless
-// browser, to take a screenshot after failure.
-//
-// Deprecated: The current Suite initializer will be removed and replaced by
-// two initializers, one for the Test Suite and one for the Scenarios. Use
-// func (ctx *ScenarioContext) AfterStep instead.
-func (s *Suite) AfterStep(fn func(*messages.Pickle_PickleStep, error)) {
- s.afterStepHandlers = append(s.afterStepHandlers, fn)
-}
-
-// AfterScenario registers an function or method
-// to be run after every pickle.
-//
-// Deprecated: The current Suite initializer will be removed and replaced by
-// two initializers, one for the Test Suite and one for the Scenarios. Use
-// func (ctx *ScenarioContext) AfterScenario instead.
-func (s *Suite) AfterScenario(fn func(*messages.Pickle, error)) {
- s.afterScenarioHandlers = append(s.afterScenarioHandlers, fn)
-}
-
-// AfterSuite registers a function or method
-// to be run once after suite runner
-//
-// Deprecated: The current Suite initializer will be removed and replaced by
-// two initializers, one for the Test Suite and one for the Scenarios. Use
-// func (ctx *TestSuiteContext) AfterSuite instead.
-func (s *Suite) AfterSuite(fn func()) {
- s.afterSuiteHandlers = append(s.afterSuiteHandlers, fn)
-}
-
-func (s *Suite) run() {
- // run before suite handlers
- for _, f := range s.beforeSuiteHandlers {
- f()
- }
- // run features
- for _, f := range s.features {
- s.runFeature(f)
- if s.failed && s.stopOnFailure {
- // stop on first failure
- break
- }
- }
- // run after suite handlers
- for _, f := range s.afterSuiteHandlers {
- f()
- }
-}
-
-func (s *Suite) matchStep(step *messages.Pickle_PickleStep) *StepDefinition {
+func (s *suite) matchStep(step *messages.Pickle_PickleStep) *StepDefinition {
def := s.matchStepText(step.Text)
if def != nil && step.Argument != nil {
def.args = append(def.args, step.Argument)
@@ -224,7 +44,7 @@ func (s *Suite) matchStep(step *messages.Pickle_PickleStep) *StepDefinition {
return def
}
-func (s *Suite) runStep(pickle *messages.Pickle, step *messages.Pickle_PickleStep, prevStepErr error) (err error) {
+func (s *suite) runStep(pickle *messages.Pickle, step *messages.Pickle_PickleStep, prevStepErr error) (err error) {
// run before step handlers
for _, f := range s.beforeStepHandlers {
f(step)
@@ -312,7 +132,7 @@ func (s *Suite) runStep(pickle *messages.Pickle, step *messages.Pickle_PickleSte
return
}
-func (s *Suite) maybeUndefined(text string, arg interface{}) ([]string, error) {
+func (s *suite) maybeUndefined(text string, arg interface{}) ([]string, error) {
step := s.matchStepText(text)
if nil == step {
return []string{text}, nil
@@ -345,7 +165,7 @@ func (s *Suite) maybeUndefined(text string, arg interface{}) ([]string, error) {
return undefined, nil
}
-func (s *Suite) maybeSubSteps(result interface{}) error {
+func (s *suite) maybeSubSteps(result interface{}) error {
if nil == result {
return nil
}
@@ -369,7 +189,7 @@ func (s *Suite) maybeSubSteps(result interface{}) error {
return nil
}
-func (s *Suite) matchStepText(text string) *StepDefinition {
+func (s *suite) matchStepText(text string) *StepDefinition {
for _, h := range s.steps {
if m := h.Expr.FindStringSubmatch(text); len(m) > 0 {
var args []interface{}
@@ -391,7 +211,7 @@ func (s *Suite) matchStepText(text string) *StepDefinition {
return nil
}
-func (s *Suite) runSteps(pickle *messages.Pickle, steps []*messages.Pickle_PickleStep) (err error) {
+func (s *suite) runSteps(pickle *messages.Pickle, steps []*messages.Pickle_PickleStep) (err error) {
for _, step := range steps {
stepErr := s.runStep(pickle, step, err)
switch stepErr {
@@ -410,7 +230,7 @@ func (s *Suite) runSteps(pickle *messages.Pickle, steps []*messages.Pickle_Pickl
return
}
-func (s *Suite) shouldFail(err error) bool {
+func (s *suite) shouldFail(err error) bool {
if err == nil {
return false
}
@@ -422,31 +242,6 @@ func (s *Suite) shouldFail(err error) bool {
return true
}
-func (s *Suite) runFeature(f *feature) {
- s.fmt.Feature(f.GherkinDocument, f.Uri, f.content)
-
- pickles := make([]*messages.Pickle, len(f.pickles))
- if s.randomSeed != 0 {
- r := rand.New(rand.NewSource(s.randomSeed))
- perm := r.Perm(len(f.pickles))
- for i, v := range perm {
- pickles[v] = f.pickles[i]
- }
- } else {
- copy(pickles, f.pickles)
- }
-
- for _, pickle := range pickles {
- err := s.runPickle(pickle)
- if s.shouldFail(err) {
- s.failed = true
- if s.stopOnFailure {
- return
- }
- }
- }
-}
-
func isEmptyFeature(pickles []*messages.Pickle) bool {
for _, pickle := range pickles {
if len(pickle.Steps) > 0 {
@@ -457,7 +252,7 @@ func isEmptyFeature(pickles []*messages.Pickle) bool {
return true
}
-func (s *Suite) runPickle(pickle *messages.Pickle) (err error) {
+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)
diff --git a/suite_context.go b/suite_context.go
deleted file mode 100644
index 94079c8..0000000
--- a/suite_context.go
+++ /dev/null
@@ -1,632 +0,0 @@
-package godog
-
-import (
- "bytes"
- "encoding/json"
- "encoding/xml"
- "fmt"
- "path/filepath"
- "regexp"
- "strconv"
- "strings"
-
- "github.com/cucumber/gherkin-go/v11"
- "github.com/cucumber/messages-go/v10"
- "github.com/stretchr/testify/assert"
-
- "github.com/cucumber/godog/colors"
-)
-
-// SuiteContext provides steps for godog suite execution and
-// can be used for meta-testing of godog features/steps themselves.
-//
-// Beware, steps or their definitions might change without backward
-// compatibility guarantees. A typical user of the godog library should never
-// need this, rather it is provided for those developing add-on libraries for godog.
-//
-// For an example of how to use, see godog's own `features/` and `suite_test.go`.
-//
-// Deprecated: Use InitializeScenario instead.
-func SuiteContext(s *Suite, additionalContextInitializers ...func(suite *Suite)) {
- c := &suiteContext{
- extraCIs: additionalContextInitializers,
- }
-
- // apply any additional context intializers to modify the context that the
- // meta-tests will be run in
- for _, ci := range additionalContextInitializers {
- ci(s)
- }
-
- s.BeforeScenario(c.ResetBeforeEachScenario)
-
- s.Step(`^(?:a )?feature path "([^"]*)"$`, c.featurePath)
- s.Step(`^I parse features$`, c.parseFeatures)
- s.Step(`^I'm listening to suite events$`, c.iAmListeningToSuiteEvents)
- s.Step(`^I run feature suite$`, c.iRunFeatureSuite)
- s.Step(`^I run feature suite with tags "([^"]*)"$`, c.iRunFeatureSuiteWithTags)
- s.Step(`^I run feature suite with formatter "([^"]*)"$`, c.iRunFeatureSuiteWithFormatter)
- s.Step(`^(?:I )(allow|disable) variable injection`, c.iSetVariableInjectionTo)
- s.Step(`^(?:a )?feature "([^"]*)"(?: file)?:$`, c.aFeatureFile)
- s.Step(`^the suite should have (passed|failed)$`, c.theSuiteShouldHave)
-
- s.Step(`^I should have ([\d]+) features? files?:$`, c.iShouldHaveNumFeatureFiles)
- s.Step(`^I should have ([\d]+) scenarios? registered$`, c.numScenariosRegistered)
- s.Step(`^there (was|were) ([\d]+) "([^"]*)" events? fired$`, c.thereWereNumEventsFired)
- s.Step(`^there was event triggered before scenario "([^"]*)"$`, c.thereWasEventTriggeredBeforeScenario)
- s.Step(`^these events had to be fired for a number of times:$`, c.theseEventsHadToBeFiredForNumberOfTimes)
-
- s.Step(`^(?:a )?failing step`, c.aFailingStep)
- s.Step(`^this step should fail`, c.aFailingStep)
- s.Step(`^the following steps? should be (passed|failed|skipped|undefined|pending):`, c.followingStepsShouldHave)
- s.Step(`^the undefined step snippets should be:$`, c.theUndefinedStepSnippetsShouldBe)
-
- // event stream
- s.Step(`^the following events should be fired:$`, c.thereShouldBeEventsFired)
-
- // lt
- s.Step(`^savybių aplankas "([^"]*)"$`, c.featurePath)
- s.Step(`^aš išskaitau savybes$`, c.parseFeatures)
- s.Step(`^aš turėčiau turėti ([\d]+) savybių failus:$`, c.iShouldHaveNumFeatureFiles)
-
- s.Step(`^(?:a )?pending step$`, func() error {
- return ErrPending
- })
- s.Step(`^(?:a )?passing step$`, func() error {
- return nil
- })
-
- // Introduced to test formatter/cucumber.feature
- s.Step(`^the rendered json will be as follows:$`, c.theRenderJSONWillBe)
-
- // Introduced to test formatter/pretty.feature
- s.Step(`^the rendered output will be as follows:$`, c.theRenderOutputWillBe)
-
- // Introduced to test formatter/junit.feature
- s.Step(`^the rendered xml will be as follows:$`, c.theRenderXMLWillBe)
-
- s.Step(`^(?:a )?failing multistep$`, func() Steps {
- return Steps{"passing step", "failing step"}
- })
-
- s.Step(`^(?:a |an )?undefined multistep$`, func() Steps {
- return Steps{"passing step", "undefined step", "passing step"}
- })
-
- s.Step(`^(?:a )?passing multistep$`, func() Steps {
- return Steps{"passing step", "passing step", "passing step"}
- })
-
- s.Step(`^(?:a )?failing nested multistep$`, func() Steps {
- return Steps{"passing step", "passing multistep", "failing multistep"}
- })
- // Default recovery step
- s.Step(`Ignore.*`, func() error {
- return nil
- })
-
- s.BeforeStep(c.inject)
-}
-
-func (s *suiteContext) inject(step *Step) {
- if !s.allowInjection {
- return
- }
-
- step.Text = injectAll(step.Text)
-
- if table := step.Argument.GetDataTable(); table != nil {
- for i := 0; i < len(table.Rows); i++ {
- for n, cell := range table.Rows[i].Cells {
- table.Rows[i].Cells[n].Value = injectAll(cell.Value)
- }
- }
- }
-
- if doc := step.Argument.GetDocString(); doc != nil {
- doc.Content = injectAll(doc.Content)
- }
-}
-
-func injectAll(src string) string {
- re := regexp.MustCompile(`{{[^{}]+}}`)
- return re.ReplaceAllStringFunc(
- src,
- func(key string) string {
- injectRegex := regexp.MustCompile(`^{{.+}}$`)
-
- if injectRegex.MatchString(key) {
- return "someverylonginjectionsoweacanbesureitsurpasstheinitiallongeststeplenghtanditwillhelptestsmethodsafety"
- }
-
- return key
- },
- )
-}
-
-type firedEvent struct {
- name string
- args []interface{}
-}
-
-type suiteContext struct {
- paths []string
- testedSuite *Suite
- extraCIs []func(suite *Suite)
- events []*firedEvent
- out bytes.Buffer
- allowInjection bool
-}
-
-func (s *suiteContext) ResetBeforeEachScenario(*Scenario) {
- // reset whole suite with the state
- s.out.Reset()
- s.paths = []string{}
- s.testedSuite = &Suite{}
- // our tested suite will have the same context registered
- SuiteContext(s.testedSuite, s.extraCIs...)
- // reset all fired events
- s.events = []*firedEvent{}
- s.allowInjection = false
-}
-
-func (s *suiteContext) iSetVariableInjectionTo(to string) error {
- s.allowInjection = to == "allow"
- return nil
-}
-
-func (s *suiteContext) iRunFeatureSuiteWithTags(tags string) error {
- if err := s.parseFeatures(); err != nil {
- return err
- }
-
- for _, feat := range s.testedSuite.features {
- feat.pickles = applyTagFilter(tags, feat.pickles)
- }
-
- s.testedSuite.storage = newStorage()
- for _, feat := range s.testedSuite.features {
- s.testedSuite.storage.mustInsertFeature(feat)
-
- for _, pickle := range feat.pickles {
- s.testedSuite.storage.mustInsertPickle(pickle)
- }
- }
-
- fmt := newBaseFmt("godog", &s.out)
- fmt.setStorage(s.testedSuite.storage)
- s.testedSuite.fmt = fmt
-
- testRunStarted := testRunStarted{StartedAt: timeNowFunc()}
- s.testedSuite.storage.mustInsertTestRunStarted(testRunStarted)
-
- s.testedSuite.fmt.TestRunStarted()
- s.testedSuite.run()
- s.testedSuite.fmt.Summary()
-
- return nil
-}
-
-func (s *suiteContext) iRunFeatureSuiteWithFormatter(name string) error {
- if err := s.parseFeatures(); err != nil {
- return err
- }
-
- f := FindFmt(name)
- if f == nil {
- return fmt.Errorf(`formatter "%s" is not available`, name)
- }
-
- s.testedSuite.storage = newStorage()
- for _, feat := range s.testedSuite.features {
- s.testedSuite.storage.mustInsertFeature(feat)
-
- for _, pickle := range feat.pickles {
- s.testedSuite.storage.mustInsertPickle(pickle)
- }
- }
-
- s.testedSuite.fmt = f("godog", colors.Uncolored(&s.out))
- if fmt, ok := s.testedSuite.fmt.(storageFormatter); ok {
- fmt.setStorage(s.testedSuite.storage)
- }
-
- testRunStarted := testRunStarted{StartedAt: timeNowFunc()}
- s.testedSuite.storage.mustInsertTestRunStarted(testRunStarted)
-
- s.testedSuite.fmt.TestRunStarted()
- s.testedSuite.run()
- s.testedSuite.fmt.Summary()
-
- return nil
-}
-
-func (s *suiteContext) thereShouldBeEventsFired(doc *DocString) error {
- actual := strings.Split(strings.TrimSpace(s.out.String()), "\n")
- expect := strings.Split(strings.TrimSpace(doc.Content), "\n")
-
- if len(expect) != len(actual) {
- return fmt.Errorf("expected %d events, but got %d", len(expect), len(actual))
- }
-
- type ev struct {
- Event string
- }
-
- for i, event := range actual {
- exp := strings.TrimSpace(expect[i])
- var act ev
-
- if err := json.Unmarshal([]byte(event), &act); err != nil {
- return fmt.Errorf("failed to read event data: %v", err)
- }
-
- if act.Event != exp {
- return fmt.Errorf(`expected event: "%s" at position: %d, but actual was "%s"`, exp, i, act.Event)
- }
- }
-
- return nil
-}
-
-func (s *suiteContext) cleanupSnippet(snip string) string {
- lines := strings.Split(strings.TrimSpace(snip), "\n")
- for i := 0; i < len(lines); i++ {
- lines[i] = strings.TrimSpace(lines[i])
- }
-
- return strings.Join(lines, "\n")
-}
-
-func (s *suiteContext) theUndefinedStepSnippetsShouldBe(body *DocString) error {
- f, ok := s.testedSuite.fmt.(*basefmt)
- if !ok {
- return fmt.Errorf("this step requires *basefmt, but there is: %T", s.testedSuite.fmt)
- }
-
- actual := s.cleanupSnippet(f.snippets())
- expected := s.cleanupSnippet(body.Content)
-
- if actual != expected {
- return fmt.Errorf("snippets do not match actual: %s", f.snippets())
- }
-
- return nil
-}
-
-func (s *suiteContext) followingStepsShouldHave(status string, steps *DocString) error {
- var expected = strings.Split(steps.Content, "\n")
- var actual, unmatched, matched []string
-
- f, ok := s.testedSuite.fmt.(*basefmt)
- if !ok {
- return fmt.Errorf("this step requires *basefmt, but there is: %T", s.testedSuite.fmt)
- }
-
- switch status {
- case "passed":
- for _, st := range f.storage.mustGetPickleStepResultsByStatus(passed) {
- pickleStep := f.storage.mustGetPickleStep(st.PickleStepID)
- actual = append(actual, pickleStep.Text)
- }
- case "failed":
- for _, st := range f.storage.mustGetPickleStepResultsByStatus(failed) {
- pickleStep := f.storage.mustGetPickleStep(st.PickleStepID)
- actual = append(actual, pickleStep.Text)
- }
- case "skipped":
- for _, st := range f.storage.mustGetPickleStepResultsByStatus(skipped) {
- pickleStep := f.storage.mustGetPickleStep(st.PickleStepID)
- actual = append(actual, pickleStep.Text)
- }
- case "undefined":
- for _, st := range f.storage.mustGetPickleStepResultsByStatus(undefined) {
- pickleStep := f.storage.mustGetPickleStep(st.PickleStepID)
- actual = append(actual, pickleStep.Text)
- }
- case "pending":
- for _, st := range f.storage.mustGetPickleStepResultsByStatus(pending) {
- pickleStep := f.storage.mustGetPickleStep(st.PickleStepID)
- actual = append(actual, pickleStep.Text)
- }
- default:
- return fmt.Errorf("unexpected step status wanted: %s", status)
- }
-
- if len(expected) > len(actual) {
- return fmt.Errorf("number of expected %s steps: %d is less than actual %s steps: %d", status, len(expected), status, len(actual))
- }
-
- for _, a := range actual {
- for _, e := range expected {
- if a == e {
- matched = append(matched, e)
- break
- }
- }
- }
-
- if len(matched) >= len(expected) {
- return nil
- }
-
- for _, s := range expected {
- var found bool
- for _, m := range matched {
- if s == m {
- found = true
- break
- }
- }
-
- if !found {
- unmatched = append(unmatched, s)
- }
- }
-
- return fmt.Errorf("the steps: %s - are not %s", strings.Join(unmatched, ", "), status)
-}
-
-func (s *suiteContext) iAmListeningToSuiteEvents() error {
- s.testedSuite.BeforeSuite(func() {
- s.events = append(s.events, &firedEvent{"BeforeSuite", []interface{}{}})
- })
-
- s.testedSuite.AfterSuite(func() {
- s.events = append(s.events, &firedEvent{"AfterSuite", []interface{}{}})
- })
-
- s.testedSuite.BeforeScenario(func(pickle *Scenario) {
- s.events = append(s.events, &firedEvent{"BeforeScenario", []interface{}{pickle}})
- })
-
- s.testedSuite.AfterScenario(func(pickle *Scenario, err error) {
- s.events = append(s.events, &firedEvent{"AfterScenario", []interface{}{pickle, err}})
- })
-
- s.testedSuite.BeforeStep(func(step *Step) {
- s.events = append(s.events, &firedEvent{"BeforeStep", []interface{}{step}})
- })
-
- s.testedSuite.AfterStep(func(step *Step, err error) {
- s.events = append(s.events, &firedEvent{"AfterStep", []interface{}{step, err}})
- })
-
- return nil
-}
-
-func (s *suiteContext) aFailingStep() error {
- return fmt.Errorf("intentional failure")
-}
-
-// parse a given feature file body as a feature
-func (s *suiteContext) aFeatureFile(path string, body *DocString) error {
- gd, err := gherkin.ParseGherkinDocument(strings.NewReader(body.Content), (&messages.Incrementing{}).NewId)
- gd.Uri = path
-
- pickles := gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId)
- s.testedSuite.features = append(s.testedSuite.features, &feature{GherkinDocument: gd, pickles: pickles})
-
- return err
-}
-
-func (s *suiteContext) featurePath(path string) error {
- s.paths = append(s.paths, path)
- return nil
-}
-
-func (s *suiteContext) parseFeatures() error {
- fts, err := parseFeatures("", s.paths)
- if err != nil {
- return err
- }
-
- s.testedSuite.features = append(s.testedSuite.features, fts...)
-
- return nil
-}
-
-func (s *suiteContext) theSuiteShouldHave(state string) error {
- if s.testedSuite.failed && state == "passed" {
- return fmt.Errorf("the feature suite has failed")
- }
-
- if !s.testedSuite.failed && state == "failed" {
- return fmt.Errorf("the feature suite has passed")
- }
-
- return nil
-}
-
-func (s *suiteContext) iShouldHaveNumFeatureFiles(num int, files *DocString) error {
- if len(s.testedSuite.features) != num {
- return fmt.Errorf("expected %d features to be parsed, but have %d", num, len(s.testedSuite.features))
- }
-
- expected := strings.Split(files.Content, "\n")
-
- var actual []string
-
- for _, ft := range s.testedSuite.features {
- actual = append(actual, ft.Uri)
- }
-
- if len(expected) != len(actual) {
- return fmt.Errorf("expected %d feature paths to be parsed, but have %d", len(expected), len(actual))
- }
-
- for i := 0; i < len(expected); i++ {
- var matched bool
- split := strings.Split(expected[i], "/")
- exp := filepath.Join(split...)
-
- for j := 0; j < len(actual); j++ {
- split = strings.Split(actual[j], "/")
- act := filepath.Join(split...)
-
- if exp == act {
- matched = true
- break
- }
- }
-
- if !matched {
- return fmt.Errorf(`expected feature path "%s" at position: %d, was not parsed, actual are %+v`, exp, i, actual)
- }
- }
-
- return nil
-}
-
-func (s *suiteContext) iRunFeatureSuite() error {
- return s.iRunFeatureSuiteWithTags("")
-}
-
-func (s *suiteContext) numScenariosRegistered(expected int) (err error) {
- var num int
- for _, ft := range s.testedSuite.features {
- num += len(ft.pickles)
- }
-
- if num != expected {
- err = fmt.Errorf("expected %d scenarios to be registered, but got %d", expected, num)
- }
-
- return
-}
-
-func (s *suiteContext) thereWereNumEventsFired(_ string, expected int, typ string) error {
- var num int
- for _, event := range s.events {
- if event.name == typ {
- num++
- }
- }
-
- if num != expected {
- return fmt.Errorf("expected %d %s events to be fired, but got %d", expected, typ, num)
- }
-
- return nil
-}
-
-func (s *suiteContext) thereWasEventTriggeredBeforeScenario(expected string) error {
- var found []string
- for _, event := range s.events {
- if event.name != "BeforeScenario" {
- continue
- }
-
- var name string
- switch t := event.args[0].(type) {
- case *Scenario:
- name = t.Name
- }
-
- if name == expected {
- return nil
- }
-
- found = append(found, name)
- }
-
- if len(found) == 0 {
- return fmt.Errorf("before scenario event was never triggered or listened")
- }
-
- return fmt.Errorf(`expected "%s" scenario, but got these fired %s`, expected, `"`+strings.Join(found, `", "`)+`"`)
-}
-
-func (s *suiteContext) theseEventsHadToBeFiredForNumberOfTimes(tbl *Table) error {
- if len(tbl.Rows[0].Cells) != 2 {
- return fmt.Errorf("expected two columns for event table row, got: %d", len(tbl.Rows[0].Cells))
- }
-
- for _, row := range tbl.Rows {
- num, err := strconv.ParseInt(row.Cells[1].Value, 10, 0)
- if err != nil {
- return err
- }
-
- if err := s.thereWereNumEventsFired("", int(num), row.Cells[0].Value); err != nil {
- return err
- }
- }
-
- return nil
-}
-
-func (s *suiteContext) theRenderJSONWillBe(docstring *DocString) error {
- suiteCtxReg := regexp.MustCompile(`suite_context.go:\d+`)
-
- expectedString := docstring.Content
- expectedString = suiteCtxReg.ReplaceAllString(expectedString, `suite_context.go:0`)
-
- actualString := s.out.String()
- actualString = suiteCtxReg.ReplaceAllString(actualString, `suite_context.go:0`)
-
- var expected []cukeFeatureJSON
- if err := json.Unmarshal([]byte(expectedString), &expected); err != nil {
- return err
- }
-
- var actual []cukeFeatureJSON
- if err := json.Unmarshal([]byte(actualString), &actual); err != nil {
- return err
- }
-
- return assertExpectedAndActual(assert.Equal, expected, actual)
-}
-
-func (s *suiteContext) theRenderOutputWillBe(docstring *DocString) error {
- suiteCtxReg := regexp.MustCompile(`suite_context.go:\d+`)
- suiteCtxFuncReg := regexp.MustCompile(`github.com/cucumber/godog.SuiteContext.func(\d+)`)
-
- expected := docstring.Content
- expected = trimAllLines(expected)
- expected = suiteCtxReg.ReplaceAllString(expected, "suite_context.go:0")
- expected = suiteCtxFuncReg.ReplaceAllString(expected, "SuiteContext.func$1")
-
- actual := s.out.String()
- actual = trimAllLines(actual)
- actual = suiteCtxReg.ReplaceAllString(actual, "suite_context.go:0")
- actual = suiteCtxFuncReg.ReplaceAllString(actual, "SuiteContext.func$1")
-
- expectedRows := strings.Split(expected, "\n")
- actualRows := strings.Split(actual, "\n")
-
- return assertExpectedAndActual(assert.ElementsMatch, expectedRows, actualRows)
-}
-
-func (s *suiteContext) theRenderXMLWillBe(docstring *DocString) error {
- expectedString := docstring.Content
- actualString := s.out.String()
-
- var expected junitPackageSuite
- if err := xml.Unmarshal([]byte(expectedString), &expected); err != nil {
- return err
- }
-
- var actual junitPackageSuite
- if err := xml.Unmarshal([]byte(actualString), &actual); err != nil {
- return err
- }
-
- return assertExpectedAndActual(assert.Equal, expected, actual)
-}
-
-func assertExpectedAndActual(a expectedAndActualAssertion, expected, actual interface{}, msgAndArgs ...interface{}) error {
- var t asserter
- a(&t, expected, actual, msgAndArgs...)
- return t.err
-}
-
-type expectedAndActualAssertion func(t assert.TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool
-
-type asserter struct {
- err error
-}
-
-func (a *asserter) Errorf(format string, args ...interface{}) {
- a.err = fmt.Errorf(format, args...)
-}
diff --git a/suite_context_test.go b/suite_context_test.go
index 507eb2d..bc91b00 100644
--- a/suite_context_test.go
+++ b/suite_context_test.go
@@ -117,9 +117,31 @@ func (tc *godogFeaturesScenario) inject(step *Step) {
}
}
+func injectAll(src string) string {
+ re := regexp.MustCompile(`{{[^{}]+}}`)
+ return re.ReplaceAllStringFunc(
+ src,
+ func(key string) string {
+ injectRegex := regexp.MustCompile(`^{{.+}}$`)
+
+ if injectRegex.MatchString(key) {
+ return "someverylonginjectionsoweacanbesureitsurpasstheinitiallongeststeplenghtanditwillhelptestsmethodsafety"
+ }
+
+ return key
+ },
+ )
+}
+
+type firedEvent struct {
+ name string
+ args []interface{}
+}
+
type godogFeaturesScenario struct {
paths []string
- testedSuite *Suite
+ features []*feature
+ testedSuite *suite
testSuiteContext TestSuiteContext
events []*firedEvent
out bytes.Buffer
@@ -131,7 +153,8 @@ func (tc *godogFeaturesScenario) ResetBeforeEachScenario(*Scenario) {
tc.out.Reset()
tc.paths = []string{}
- tc.testedSuite = &Suite{}
+ tc.features = []*feature{}
+ tc.testedSuite = &suite{}
tc.testSuiteContext = TestSuiteContext{}
// reset all fired events
@@ -162,12 +185,12 @@ func (tc *godogFeaturesScenario) iRunFeatureSuiteWithTagsAndFormatter(tags strin
return err
}
- for _, feat := range tc.testedSuite.features {
+ for _, feat := range tc.features {
feat.pickles = applyTagFilter(tags, feat.pickles)
}
tc.testedSuite.storage = newStorage()
- for _, feat := range tc.testedSuite.features {
+ for _, feat := range tc.features {
tc.testedSuite.storage.mustInsertFeature(feat)
for _, pickle := range feat.pickles {
@@ -188,7 +211,7 @@ func (tc *godogFeaturesScenario) iRunFeatureSuiteWithTagsAndFormatter(tags strin
f()
}
- for _, ft := range tc.testedSuite.features {
+ for _, ft := range tc.features {
tc.testedSuite.fmt.Feature(ft.GherkinDocument, ft.Uri, ft.content)
for _, pickle := range ft.pickles {
@@ -350,19 +373,21 @@ func (tc *godogFeaturesScenario) iAmListeningToSuiteEvents() error {
tc.events = append(tc.events, &firedEvent{"AfterSuite", []interface{}{}})
})
- tc.testedSuite.BeforeScenario(func(pickle *Scenario) {
+ scenarioContext := ScenarioContext{suite: tc.testedSuite}
+
+ scenarioContext.BeforeScenario(func(pickle *Scenario) {
tc.events = append(tc.events, &firedEvent{"BeforeScenario", []interface{}{pickle}})
})
- tc.testedSuite.AfterScenario(func(pickle *Scenario, err error) {
+ scenarioContext.AfterScenario(func(pickle *Scenario, err error) {
tc.events = append(tc.events, &firedEvent{"AfterScenario", []interface{}{pickle, err}})
})
- tc.testedSuite.BeforeStep(func(step *Step) {
+ scenarioContext.BeforeStep(func(step *Step) {
tc.events = append(tc.events, &firedEvent{"BeforeStep", []interface{}{step}})
})
- tc.testedSuite.AfterStep(func(step *Step, err error) {
+ scenarioContext.AfterStep(func(step *Step, err error) {
tc.events = append(tc.events, &firedEvent{"AfterStep", []interface{}{step, err}})
})
@@ -379,7 +404,7 @@ func (tc *godogFeaturesScenario) aFeatureFile(path string, body *DocString) erro
gd.Uri = path
pickles := gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId)
- tc.testedSuite.features = append(tc.testedSuite.features, &feature{GherkinDocument: gd, pickles: pickles})
+ tc.features = append(tc.features, &feature{GherkinDocument: gd, pickles: pickles})
return err
}
@@ -395,7 +420,7 @@ func (tc *godogFeaturesScenario) parseFeatures() error {
return err
}
- tc.testedSuite.features = append(tc.testedSuite.features, fts...)
+ tc.features = append(tc.features, fts...)
return nil
}
@@ -413,15 +438,15 @@ func (tc *godogFeaturesScenario) theSuiteShouldHave(state string) error {
}
func (tc *godogFeaturesScenario) iShouldHaveNumFeatureFiles(num int, files *DocString) error {
- if len(tc.testedSuite.features) != num {
- return fmt.Errorf("expected %d features to be parsed, but have %d", num, len(tc.testedSuite.features))
+ if len(tc.features) != num {
+ return fmt.Errorf("expected %d features to be parsed, but have %d", num, len(tc.features))
}
expected := strings.Split(files.Content, "\n")
var actual []string
- for _, ft := range tc.testedSuite.features {
+ for _, ft := range tc.features {
actual = append(actual, ft.Uri)
}
@@ -458,7 +483,7 @@ func (tc *godogFeaturesScenario) iRunFeatureSuite() error {
func (tc *godogFeaturesScenario) numScenariosRegistered(expected int) (err error) {
var num int
- for _, ft := range tc.testedSuite.features {
+ for _, ft := range tc.features {
num += len(ft.pickles)
}
@@ -599,3 +624,19 @@ func (tc *godogFeaturesScenario) theRenderXMLWillBe(docstring *DocString) error
return assertExpectedAndActual(assert.Equal, expected, actual)
}
+
+func assertExpectedAndActual(a expectedAndActualAssertion, expected, actual interface{}, msgAndArgs ...interface{}) error {
+ var t asserter
+ a(&t, expected, actual, msgAndArgs...)
+ return t.err
+}
+
+type expectedAndActualAssertion func(t assert.TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool
+
+type asserter struct {
+ err error
+}
+
+func (a *asserter) Errorf(format string, args ...interface{}) {
+ a.err = fmt.Errorf(format, args...)
+}
diff --git a/suite_test.go b/suite_test.go
deleted file mode 100644
index 9d2f71d..0000000
--- a/suite_test.go
+++ /dev/null
@@ -1,6 +0,0 @@
-package godog
-
-// needed in order to use godog cli
-func GodogContext(s *Suite) {
- SuiteContext(s)
-}
diff --git a/test_context.go b/test_context.go
index dfe3e61..d67a31e 100644
--- a/test_context.go
+++ b/test_context.go
@@ -1,6 +1,12 @@
package godog
-import "github.com/cucumber/messages-go/v10"
+import (
+ "fmt"
+ "reflect"
+ "regexp"
+
+ "github.com/cucumber/messages-go/v10"
+)
// Scenario represents the executed scenario
type Scenario = messages.Pickle
@@ -55,7 +61,7 @@ func (ctx *TestSuiteContext) AfterSuite(fn func()) {
// executions are catching panic error since it may
// be a context specific error.
type ScenarioContext struct {
- suite *Suite
+ suite *suite
}
// BeforeScenario registers a function or method
@@ -65,19 +71,19 @@ type ScenarioContext struct {
// before every scenario so it would be isolated from
// any kind of state.
func (ctx *ScenarioContext) BeforeScenario(fn func(sc *Scenario)) {
- ctx.suite.BeforeScenario(fn)
+ ctx.suite.beforeScenarioHandlers = append(ctx.suite.beforeScenarioHandlers, fn)
}
// AfterScenario registers an function or method
// to be run after every scenario.
func (ctx *ScenarioContext) AfterScenario(fn func(sc *Scenario, err error)) {
- ctx.suite.AfterScenario(fn)
+ ctx.suite.afterScenarioHandlers = append(ctx.suite.afterScenarioHandlers, fn)
}
// BeforeStep registers a function or method
// to be run before every step.
func (ctx *ScenarioContext) BeforeStep(fn func(st *Step)) {
- ctx.suite.BeforeStep(fn)
+ ctx.suite.beforeStepHandlers = append(ctx.suite.beforeStepHandlers, fn)
}
// AfterStep registers an function or method
@@ -90,7 +96,7 @@ func (ctx *ScenarioContext) BeforeStep(fn func(st *Step)) {
// In some cases, for example when running a headless
// browser, to take a screenshot after failure.
func (ctx *ScenarioContext) AfterStep(fn func(st *Step, err error)) {
- ctx.suite.AfterStep(fn)
+ ctx.suite.afterStepHandlers = append(ctx.suite.afterStepHandlers, fn)
}
// Step allows to register a *StepDefinition in the
@@ -121,5 +127,49 @@ func (ctx *ScenarioContext) AfterStep(fn func(st *Step, err error)) {
// ErrUndefined error will be returned when
// running steps.
func (ctx *ScenarioContext) Step(expr, stepFunc interface{}) {
- ctx.suite.Step(expr, stepFunc)
+ var regex *regexp.Regexp
+
+ switch t := expr.(type) {
+ case *regexp.Regexp:
+ regex = t
+ case string:
+ regex = regexp.MustCompile(t)
+ case []byte:
+ regex = regexp.MustCompile(string(t))
+ default:
+ panic(fmt.Sprintf("expecting expr to be a *regexp.Regexp or a string, got type: %T", expr))
+ }
+
+ v := reflect.ValueOf(stepFunc)
+ typ := v.Type()
+ if typ.Kind() != reflect.Func {
+ panic(fmt.Sprintf("expected handler to be func, but got: %T", stepFunc))
+ }
+
+ if typ.NumOut() != 1 {
+ panic(fmt.Sprintf("expected handler to return only one value, but it has: %d", typ.NumOut()))
+ }
+
+ def := &StepDefinition{
+ Handler: stepFunc,
+ Expr: regex,
+ hv: v,
+ }
+
+ typ = typ.Out(0)
+ switch typ.Kind() {
+ case reflect.Interface:
+ if !typ.Implements(errorInterface) {
+ panic(fmt.Sprintf("expected handler to return an error, but got: %s", typ.Kind()))
+ }
+ case reflect.Slice:
+ if typ.Elem().Kind() != reflect.String {
+ panic(fmt.Sprintf("expected handler to return []string for multistep, but got: []%s", typ.Kind()))
+ }
+ def.nested = true
+ default:
+ panic(fmt.Sprintf("expected handler to return an error or []string, but got: %s", typ.Kind()))
+ }
+
+ ctx.suite.steps = append(ctx.suite.steps, def)
}