diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a8b14e2..39e8c9b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,13 +51,13 @@ jobs: go test -v -covermode=count -coverprofile=coverage.out - name: Convert coverage to lcov - uses: jandelgado/gcov2lcov-action@v1.0.0 + uses: jandelgado/gcov2lcov-action@v1 with: infile: coverage.out outfile: coverage.lcov - name: Coveralls - uses: coverallsapp/github-action@v1.0.1 + uses: coverallsapp/github-action@v1 with: github-token: ${{ secrets.github_token }} path-to-lcov: coverage.lcov diff --git a/context/context.go b/context/context.go index 06369d3..71c17aa 100644 --- a/context/context.go +++ b/context/context.go @@ -1,7 +1,7 @@ package context import ( - "github.com/go-bdd/gobdd" + gobdd "github.com/go-bdd/gobdd" ) // Holds data from previously executed steps diff --git a/context_get.go b/context_get.go index 94cfdf4..eade772 100644 --- a/context_get.go +++ b/context_get.go @@ -1,12 +1,13 @@ -// Code generated .* DO NOT EDIT. +// Code generated .* DO NOT EDIT. package gobdd import "fmt" + func (ctx Context) GetString(key interface{}, defaultValue ...string) (string, error) { if len(defaultValue) > 1 { - return "", fmt.Errorf("allowed to pass only 1 default value but %d got", len(defaultValue)) - } + return "", fmt.Errorf("allowed to pass only 1 default value but %d got", len(defaultValue)) + } if _, ok := ctx.values[key]; !ok { if len(defaultValue) == 1 { @@ -24,8 +25,8 @@ func (ctx Context) GetString(key interface{}, defaultValue ...string) (string, e func (ctx Context) GetInt(key interface{}, defaultValue ...int) (int, error) { if len(defaultValue) > 1 { - return 0, fmt.Errorf("allowed to pass only 1 default value but %d got", len(defaultValue)) - } + return 0, fmt.Errorf("allowed to pass only 1 default value but %d got", len(defaultValue)) + } if _, ok := ctx.values[key]; !ok { if len(defaultValue) == 1 { @@ -43,8 +44,8 @@ func (ctx Context) GetInt(key interface{}, defaultValue ...int) (int, error) { func (ctx Context) GetInt8(key interface{}, defaultValue ...int8) (int8, error) { if len(defaultValue) > 1 { - return 0, fmt.Errorf("allowed to pass only 1 default value but %d got", len(defaultValue)) - } + return 0, fmt.Errorf("allowed to pass only 1 default value but %d got", len(defaultValue)) + } if _, ok := ctx.values[key]; !ok { if len(defaultValue) == 1 { @@ -62,8 +63,8 @@ func (ctx Context) GetInt8(key interface{}, defaultValue ...int8) (int8, error) func (ctx Context) GetInt16(key interface{}, defaultValue ...int16) (int16, error) { if len(defaultValue) > 1 { - return 0, fmt.Errorf("allowed to pass only 1 default value but %d got", len(defaultValue)) - } + return 0, fmt.Errorf("allowed to pass only 1 default value but %d got", len(defaultValue)) + } if _, ok := ctx.values[key]; !ok { if len(defaultValue) == 1 { @@ -81,8 +82,8 @@ func (ctx Context) GetInt16(key interface{}, defaultValue ...int16) (int16, erro func (ctx Context) GetInt32(key interface{}, defaultValue ...int32) (int32, error) { if len(defaultValue) > 1 { - return 0, fmt.Errorf("allowed to pass only 1 default value but %d got", len(defaultValue)) - } + return 0, fmt.Errorf("allowed to pass only 1 default value but %d got", len(defaultValue)) + } if _, ok := ctx.values[key]; !ok { if len(defaultValue) == 1 { @@ -100,8 +101,8 @@ func (ctx Context) GetInt32(key interface{}, defaultValue ...int32) (int32, erro func (ctx Context) GetInt64(key interface{}, defaultValue ...int64) (int64, error) { if len(defaultValue) > 1 { - return 0, fmt.Errorf("allowed to pass only 1 default value but %d got", len(defaultValue)) - } + return 0, fmt.Errorf("allowed to pass only 1 default value but %d got", len(defaultValue)) + } if _, ok := ctx.values[key]; !ok { if len(defaultValue) == 1 { @@ -119,8 +120,8 @@ func (ctx Context) GetInt64(key interface{}, defaultValue ...int64) (int64, erro func (ctx Context) GetFloat32(key interface{}, defaultValue ...float32) (float32, error) { if len(defaultValue) > 1 { - return 0, fmt.Errorf("allowed to pass only 1 default value but %d got", len(defaultValue)) - } + return 0, fmt.Errorf("allowed to pass only 1 default value but %d got", len(defaultValue)) + } if _, ok := ctx.values[key]; !ok { if len(defaultValue) == 1 { @@ -138,8 +139,8 @@ func (ctx Context) GetFloat32(key interface{}, defaultValue ...float32) (float32 func (ctx Context) GetFloat64(key interface{}, defaultValue ...float64) (float64, error) { if len(defaultValue) > 1 { - return 0, fmt.Errorf("allowed to pass only 1 default value but %d got", len(defaultValue)) - } + return 0, fmt.Errorf("allowed to pass only 1 default value but %d got", len(defaultValue)) + } if _, ok := ctx.values[key]; !ok { if len(defaultValue) == 1 { @@ -157,8 +158,8 @@ func (ctx Context) GetFloat64(key interface{}, defaultValue ...float64) (float64 func (ctx Context) GetBool(key interface{}, defaultValue ...bool) (bool, error) { if len(defaultValue) > 1 { - return false, fmt.Errorf("allowed to pass only 1 default value but %d got", len(defaultValue)) - } + return false, fmt.Errorf("allowed to pass only 1 default value but %d got", len(defaultValue)) + } if _, ok := ctx.values[key]; !ok { if len(defaultValue) == 1 { @@ -173,3 +174,4 @@ func (ctx Context) GetBool(key interface{}, defaultValue ...bool) (bool, error) } return value, nil } + diff --git a/context_get_test.go b/context_get_test.go index 432c8de..da1ea3f 100644 --- a/context_get_test.go +++ b/context_get_test.go @@ -1,4 +1,4 @@ -// Code generated .* DO NOT EDIT. +// Code generated .* DO NOT EDIT. package gobdd import "testing" @@ -17,6 +17,7 @@ func TestContext_GetError(t *testing.T) { } } + func TestContext_GetString(t *testing.T) { ctx := NewContext() expected := string("example text") @@ -45,7 +46,7 @@ func TestContext_GetString_WithDefaultValue(t *testing.T) { func TestContext_GetString_ShouldReturnErrorWhenMoreThanOneDefaultValue(t *testing.T) { ctx := NewContext() _, err := ctx.GetString("test", "example text", "example text") - if err == nil { + if err == nil { t.Error("the GetString should return an error") } } @@ -53,7 +54,7 @@ func TestContext_GetString_ShouldReturnErrorWhenMoreThanOneDefaultValue(t *testi func TestContext_GetString_ErrorOnNotFound(t *testing.T) { ctx := NewContext() _, err := ctx.GetString("test") - if err == nil { + if err == nil { t.Error("the GetString should return an error") } } @@ -86,7 +87,7 @@ func TestContext_GetInt_WithDefaultValue(t *testing.T) { func TestContext_GetInt_ShouldReturnErrorWhenMoreThanOneDefaultValue(t *testing.T) { ctx := NewContext() _, err := ctx.GetInt("test", 123, 123) - if err == nil { + if err == nil { t.Error("the GetInt should return an error") } } @@ -94,7 +95,7 @@ func TestContext_GetInt_ShouldReturnErrorWhenMoreThanOneDefaultValue(t *testing. func TestContext_GetInt_ErrorOnNotFound(t *testing.T) { ctx := NewContext() _, err := ctx.GetInt("test") - if err == nil { + if err == nil { t.Error("the GetInt should return an error") } } @@ -127,7 +128,7 @@ func TestContext_GetInt8_WithDefaultValue(t *testing.T) { func TestContext_GetInt8_ShouldReturnErrorWhenMoreThanOneDefaultValue(t *testing.T) { ctx := NewContext() _, err := ctx.GetInt8("test", 123, 123) - if err == nil { + if err == nil { t.Error("the GetInt8 should return an error") } } @@ -135,7 +136,7 @@ func TestContext_GetInt8_ShouldReturnErrorWhenMoreThanOneDefaultValue(t *testing func TestContext_GetInt8_ErrorOnNotFound(t *testing.T) { ctx := NewContext() _, err := ctx.GetInt8("test") - if err == nil { + if err == nil { t.Error("the GetInt8 should return an error") } } @@ -168,7 +169,7 @@ func TestContext_GetInt16_WithDefaultValue(t *testing.T) { func TestContext_GetInt16_ShouldReturnErrorWhenMoreThanOneDefaultValue(t *testing.T) { ctx := NewContext() _, err := ctx.GetInt16("test", 123, 123) - if err == nil { + if err == nil { t.Error("the GetInt16 should return an error") } } @@ -176,7 +177,7 @@ func TestContext_GetInt16_ShouldReturnErrorWhenMoreThanOneDefaultValue(t *testin func TestContext_GetInt16_ErrorOnNotFound(t *testing.T) { ctx := NewContext() _, err := ctx.GetInt16("test") - if err == nil { + if err == nil { t.Error("the GetInt16 should return an error") } } @@ -209,7 +210,7 @@ func TestContext_GetInt32_WithDefaultValue(t *testing.T) { func TestContext_GetInt32_ShouldReturnErrorWhenMoreThanOneDefaultValue(t *testing.T) { ctx := NewContext() _, err := ctx.GetInt32("test", 123, 123) - if err == nil { + if err == nil { t.Error("the GetInt32 should return an error") } } @@ -217,7 +218,7 @@ func TestContext_GetInt32_ShouldReturnErrorWhenMoreThanOneDefaultValue(t *testin func TestContext_GetInt32_ErrorOnNotFound(t *testing.T) { ctx := NewContext() _, err := ctx.GetInt32("test") - if err == nil { + if err == nil { t.Error("the GetInt32 should return an error") } } @@ -250,7 +251,7 @@ func TestContext_GetInt64_WithDefaultValue(t *testing.T) { func TestContext_GetInt64_ShouldReturnErrorWhenMoreThanOneDefaultValue(t *testing.T) { ctx := NewContext() _, err := ctx.GetInt64("test", 123, 123) - if err == nil { + if err == nil { t.Error("the GetInt64 should return an error") } } @@ -258,7 +259,7 @@ func TestContext_GetInt64_ShouldReturnErrorWhenMoreThanOneDefaultValue(t *testin func TestContext_GetInt64_ErrorOnNotFound(t *testing.T) { ctx := NewContext() _, err := ctx.GetInt64("test") - if err == nil { + if err == nil { t.Error("the GetInt64 should return an error") } } @@ -291,7 +292,7 @@ func TestContext_GetFloat32_WithDefaultValue(t *testing.T) { func TestContext_GetFloat32_ShouldReturnErrorWhenMoreThanOneDefaultValue(t *testing.T) { ctx := NewContext() _, err := ctx.GetFloat32("test", 123.5, 123.5) - if err == nil { + if err == nil { t.Error("the GetFloat32 should return an error") } } @@ -299,7 +300,7 @@ func TestContext_GetFloat32_ShouldReturnErrorWhenMoreThanOneDefaultValue(t *test func TestContext_GetFloat32_ErrorOnNotFound(t *testing.T) { ctx := NewContext() _, err := ctx.GetFloat32("test") - if err == nil { + if err == nil { t.Error("the GetFloat32 should return an error") } } @@ -332,7 +333,7 @@ func TestContext_GetFloat64_WithDefaultValue(t *testing.T) { func TestContext_GetFloat64_ShouldReturnErrorWhenMoreThanOneDefaultValue(t *testing.T) { ctx := NewContext() _, err := ctx.GetFloat64("test", 123.5, 123.5) - if err == nil { + if err == nil { t.Error("the GetFloat64 should return an error") } } @@ -340,7 +341,7 @@ func TestContext_GetFloat64_ShouldReturnErrorWhenMoreThanOneDefaultValue(t *test func TestContext_GetFloat64_ErrorOnNotFound(t *testing.T) { ctx := NewContext() _, err := ctx.GetFloat64("test") - if err == nil { + if err == nil { t.Error("the GetFloat64 should return an error") } } @@ -373,7 +374,7 @@ func TestContext_GetBool_WithDefaultValue(t *testing.T) { func TestContext_GetBool_ShouldReturnErrorWhenMoreThanOneDefaultValue(t *testing.T) { ctx := NewContext() _, err := ctx.GetBool("test", false, false) - if err == nil { + if err == nil { t.Error("the GetBool should return an error") } } @@ -381,7 +382,8 @@ func TestContext_GetBool_ShouldReturnErrorWhenMoreThanOneDefaultValue(t *testing func TestContext_GetBool_ErrorOnNotFound(t *testing.T) { ctx := NewContext() _, err := ctx.GetBool("test") - if err == nil { + if err == nil { t.Error("the GetBool should return an error") } } + diff --git a/features/background.feature b/features/background.feature index 40287d4..124abad 100644 --- a/features/background.feature +++ b/features/background.feature @@ -4,3 +4,11 @@ Feature: using background steps Scenario: the background step should be executed Then the result should equal 3 + + Rule: adding and concat + Background: concat + When I concat word Hello and text " World!" + + Scenario: the background steps should be executed + Then the result should equal 3 + Then the result should equal text "Hello World!" diff --git a/features/example.feature b/features/example.feature index 8053737..c6a9dc1 100644 --- a/features/example.feature +++ b/features/example.feature @@ -2,3 +2,4 @@ Feature: math operations Scenario: add two digits When I add 1 and 2 Then the result should equal 3 + diff --git a/features/example_rule.feature b/features/example_rule.feature new file mode 100644 index 0000000..3ae6b41 --- /dev/null +++ b/features/example_rule.feature @@ -0,0 +1,6 @@ +Feature: math operations + Rule: add things + Scenario: add two digits + When I add 1 and 2 + Then the result should equal 3 + diff --git a/features/ignored_rule_tags.feature b/features/ignored_rule_tags.feature new file mode 100644 index 0000000..eec09f1 --- /dev/null +++ b/features/ignored_rule_tags.feature @@ -0,0 +1,9 @@ + +Feature: ignored tags + @ignore + Rule: this rule should be ignored + Scenario: the scenario should be ignored + Then fail the test + Rule: this rule should run + Scenario: the scenario should pass + Then the test should pass diff --git a/features/ignored_tags.feature b/features/ignored_tags.feature index dbdfdd6..fae2f60 100644 --- a/features/ignored_tags.feature +++ b/features/ignored_tags.feature @@ -2,3 +2,5 @@ Feature: ignored tags @ignore Scenario: the scenario should be ignored Then fail the test + Scenario: the scenario should pass + Then the test should pass diff --git a/features/tags.feature b/features/tags.feature index 7f53005..0b87f25 100644 --- a/features/tags.feature +++ b/features/tags.feature @@ -7,4 +7,15 @@ Feature: ignored tags Then the test should pass Examples: Scenario: the test should never be executed - Then fail the test \ No newline at end of file + Then fail the test + + Rule: the rule should never be executed + Scenario: the test in ignored rule should never be executed + Then fail the test + + Rule: this rule should run + @tag + Scenario: the test in executed rule should pass + Then the test should pass + Scenario: the test in executed rule should never be executed + Then fail the test \ No newline at end of file diff --git a/go.mod b/go.mod index a04da9f..196bc52 100644 --- a/go.mod +++ b/go.mod @@ -4,10 +4,17 @@ go 1.17 require ( github.com/cucumber/gherkin/go/v28 v28.0.0 - github.com/cucumber/messages/go/v24 v24.0.1 - github.com/go-bdd/assert v0.0.0-20190820124234-20d47a68475d + github.com/cucumber/messages/go/v24 v24.1.0 + github.com/go-bdd/assert v0.0.0-20200713105154-236f01430281 + github.com/stretchr/testify v1.9.0 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/gofrs/uuid v4.4.0+incompatible // indirect github.com/kr/text v0.2.0 // indirect github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect - github.com/stretchr/testify v1.8.4 + github.com/pmezard/go-difflib v1.0.0 // indirect gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index fa4a012..71af307 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,14 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cucumber/gherkin/go/v28 v28.0.0 h1:SBqwscPOhe83JF0ukpEj+4QZ2ScOpPQByC0gD3cXBkg= github.com/cucumber/gherkin/go/v28 v28.0.0/go.mod h1:HVwDrzWvtsVbkxHw6KVZFA79x5uSLb+ajzS0BXuHiE8= -github.com/cucumber/messages/go/v24 v24.0.1 h1:jajAQDk3fPa4RhIANE+NOxGdCKQdi7RYjd8wdKXnOu4= github.com/cucumber/messages/go/v24 v24.0.1/go.mod h1:ns4Befq4c4n9/B5APpTlBu5kXL1DVE4+5bbe0vSV4fc= +github.com/cucumber/messages/go/v24 v24.1.0 h1:JMpspvV3IoGwcbEUbsKuxr0tRkFP7aqcQ5SvoanS/DA= +github.com/cucumber/messages/go/v24 v24.1.0/go.mod h1:PR0+ygYqqyT1/C4EmioDdvgR8YG9+SZEkxEKw2jfd8g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/go-bdd/assert v0.0.0-20190820124234-20d47a68475d h1:zQazu3kApPoajWmXj9zFpCNE+UDefwwFRijKjzvHNCM= -github.com/go-bdd/assert v0.0.0-20190820124234-20d47a68475d/go.mod h1:dOoqt7g2I/fpR7/Pyz0P19J3xjDj5lsHn3v9EaFLRjM= +github.com/go-bdd/assert v0.0.0-20200713105154-236f01430281 h1:jfonqldVSNVekjmLvHqelFzBba7SwylNIZ3fbM/lKVs= +github.com/go-bdd/assert v0.0.0-20200713105154-236f01430281/go.mod h1:dOoqt7g2I/fpR7/Pyz0P19J3xjDj5lsHn3v9EaFLRjM= github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -21,10 +22,12 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/gobdd.go b/gobdd.go index 8f85254..aab3987 100644 --- a/gobdd.go +++ b/gobdd.go @@ -172,10 +172,13 @@ type TestingT interface { // TestingTKey is used to store reference to current *testing.T instance type TestingTKey struct{} -// FeatureKey is used to store reference to current *msgs.GherkinDocument_Feature instance +// FeatureKey is used to store reference to current *msgs.Feature instance type FeatureKey struct{} -// ScenarioKey is used to store reference to current *msgs.GherkinDocument_Feature_Scenario instance +// RuleKey is used to store reference to current *msgs.Rule instance +type RuleKey struct{} + +// ScenarioKey is used to store reference to current *msgs.Scenario instance type ScenarioKey struct{} // Creates a new suites with given configuration and empty steps defined @@ -214,7 +217,7 @@ func (s *Suite) AddParameterTypes(from string, to []string) { for _, to := range to { _, err := regexp.Compile(to) if err != nil { - s.t.Fatalf(`the regular expresion for key %s doesn't compile: %s`, from, to) + s.t.Fatalf(`the regular expression for key %s doesn't compile: %s`, from, to) } s.parameterTypes[from] = append(s.parameterTypes[from], to) @@ -347,37 +350,29 @@ func (s *Suite) executeFeature(feature feature) error { } func (s *Suite) runFeature(feature *msgs.Feature) error { - for _, tag := range feature.Tags { - if contains(s.options.ignoreTags, tag.Name) { - s.t.Logf("the feature (%s) is ignored ", feature.Name) - return nil - } + if s.shouldSkipFeatureOrRule(feature.Tags) { + s.t.Logf("the feature (%s) is ignored ", feature.Name) + return nil } hasErrors := false s.t.Run(fmt.Sprintf("%s %s", strings.TrimSpace(feature.Keyword), feature.Name), func(t *testing.T) { - var bkgSteps *msgs.Background + backgrounds := []*msgs.Background{} for _, child := range feature.Children { if child.Background != nil { - bkgSteps = child.Background + backgrounds = append(backgrounds, child.Background) } - scenario := child.Scenario - if scenario == nil { - continue + if rule := child.Rule; rule != nil { + s.runRule(feature, rule, backgrounds, t) } - - if s.skipScenario(append(feature.Tags, scenario.Tags...)) { - t.Log(fmt.Sprintf("Skipping scenario %s", scenario.Name)) - continue + if scenario := child.Scenario; scenario != nil { + ctx := NewContext() + ctx.Set(FeatureKey{}, feature) + s.runScenario(ctx, scenario, backgrounds, t, feature.Tags) } - ctx := NewContext() - ctx.Set(FeatureKey{}, feature) - ctx.Set(ScenarioKey{}, scenario) - - s.runScenario(ctx, scenario, bkgSteps, t) } }) @@ -390,8 +385,7 @@ func (s *Suite) runFeature(feature *msgs.Feature) error { func (s *Suite) getOutlineStep( steps []*msgs.Step, - examples []*msgs.Examples, -) []*msgs.Step { + examples []*msgs.Examples) []*msgs.Step { stepsList := make([][]*msgs.Step, len(steps)) for i, outlineStep := range steps { @@ -419,19 +413,17 @@ func (s *Suite) getOutlineStep( func (s *Suite) stepsFromExamples( sourceStep *msgs.Step, - example *msgs.Examples, -) []*msgs.Step { + example *msgs.Examples) []*msgs.Step { steps := []*msgs.Step{} - placeholders := []*msgs.TableCell{} - if example.TableHeader != nil { - placeholders = example.TableHeader.Cells - } placeholdersValues := []string{} - for _, placeholder := range placeholders { - ph := "<" + placeholder.Value + ">" - placeholdersValues = append(placeholdersValues, ph) + if example.TableHeader != nil { + placeholders := example.TableHeader.Cells + for _, placeholder := range placeholders { + ph := "<" + placeholder.Value + ">" + placeholdersValues = append(placeholdersValues, ph) + } } text := sourceStep.Text @@ -451,9 +443,13 @@ func (s *Suite) stepsFromExamples( // clone a step step := &msgs.Step{ - Location: sourceStep.Location, - Keyword: sourceStep.Keyword, - Text: stepText, + Location: sourceStep.Location, + Keyword: sourceStep.Keyword, + Text: stepText, + KeywordType: sourceStep.KeywordType, + DocString: sourceStep.DocString, + DataTable: sourceStep.DataTable, + Id: sourceStep.Id, } steps = append(steps, step) @@ -464,8 +460,7 @@ func (s *Suite) stepsFromExamples( func (s *Suite) stepFromExample( stepName string, - row *msgs.TableRow, placeholders []string, -) (string, string) { + row *msgs.TableRow, placeholders []string) (string, string) { expr := stepName for i, ph := range placeholders { @@ -500,20 +495,51 @@ func (s *Suite) callAfterSteps(ctx Context) { f(ctx) } } +func (s *Suite) runRule(feature *msgs.Feature, rule *msgs.Rule, + backgrounds []*msgs.Background, t *testing.T) { + ruleTags := feature.Tags + ruleTags = append(ruleTags, rule.Tags...) + + if s.shouldSkipFeatureOrRule(ruleTags) { + s.t.Logf("the rule (%s) is ignored ", feature.Name) + return + } + + ruleBackgrounds := []*msgs.Background{} + ruleBackgrounds = append(ruleBackgrounds, backgrounds...) + t.Run(fmt.Sprintf("%s %s", strings.TrimSpace(rule.Keyword), rule.Name), func(t *testing.T) { + for _, ruleChild := range rule.Children { + if ruleChild.Background != nil { + ruleBackgrounds = append(ruleBackgrounds, ruleChild.Background) + } + if scenario := ruleChild.Scenario; scenario != nil { + ctx := NewContext() + ctx.Set(FeatureKey{}, feature) + ctx.Set(RuleKey{}, rule) + s.runScenario(ctx, scenario, ruleBackgrounds, t, ruleTags) + } + } + }) +} func (s *Suite) runScenario(ctx Context, scenario *msgs.Scenario, - bkg *msgs.Background, t *testing.T, -) { + backgrounds []*msgs.Background, t *testing.T, parentTags []*msgs.Tag) { + if s.shouldSkipScenario(append(parentTags, scenario.Tags...)) { + t.Logf("Skipping scenario %s", scenario.Name) + return + } + t.Run(fmt.Sprintf("%s %s", strings.TrimSpace(scenario.Keyword), scenario.Name), func(t *testing.T) { // NOTE consider passing t as argument to scenario hooks + ctx.Set(ScenarioKey{}, scenario) ctx.Set(TestingTKey{}, t) defer ctx.Set(TestingTKey{}, nil) s.callBeforeScenarios(ctx) defer s.callAfterScenarios(ctx) - if bkg != nil { - steps := s.getBackgroundSteps(bkg) + if len(backgrounds) > 0 { + steps := s.getBackgroundSteps(backgrounds) s.runSteps(ctx, t, steps) } steps := scenario.Steps @@ -642,7 +668,17 @@ func (s *Suite) findStepDef(text string) (stepDef, error) { return sd, nil } -func (s *Suite) skipScenario(scenarioTags []*msgs.Tag) bool { +func (s *Suite) shouldSkipFeatureOrRule(featureOrRuleTags []*msgs.Tag) bool { + for _, tag := range featureOrRuleTags { + if contains(s.options.ignoreTags, tag.Name) { + return true + } + } + + return false +} + +func (s *Suite) shouldSkipScenario(scenarioTags []*msgs.Tag) bool { for _, tag := range scenarioTags { if contains(s.options.ignoreTags, tag.Name) { return true @@ -662,8 +698,13 @@ func (s *Suite) skipScenario(scenarioTags []*msgs.Tag) bool { return true } -func (s *Suite) getBackgroundSteps(bkg *msgs.Background) []*msgs.Step { - return bkg.Steps +func (s *Suite) getBackgroundSteps(backgrounds []*msgs.Background) []*msgs.Step { + result := []*msgs.Step{} + for _, background := range backgrounds { + result = append(result, background.Steps...) + } + + return result } // contains tells whether a contains x. diff --git a/gobdd_test.go b/gobdd_test.go index f3a4a3c..46c1366 100644 --- a/gobdd_test.go +++ b/gobdd_test.go @@ -17,7 +17,15 @@ func TestScenarios(t *testing.T) { suite.AddRegexStep(compiled, add) compiled = regexp.MustCompile(`the result should equal (\d+)`) suite.AddRegexStep(compiled, check) + suite.Run() +} +func TestRule(t *testing.T) { + suite := NewSuite(t, WithFeaturesPath("features/example_rule.feature")) + compiled := regexp.MustCompile(`I add (\d+) and (\d+)`) + suite.AddRegexStep(compiled, add) + compiled = regexp.MustCompile(`the result should equal (\d+)`) + suite.AddRegexStep(compiled, check) suite.Run() } @@ -117,6 +125,8 @@ func TestStepFromExample(t *testing.T) { func TestBackground(t *testing.T) { suite := NewSuite(t, WithFeaturesPath("features/background.feature")) suite.AddStep(`I add (\d+) and (\d+)`, add) + suite.AddStep(`I concat word {word} and text {text}`, concat) + suite.AddStep(`the result should equal text {text}`, checkt) suite.AddStep(`the result should equal (\d+)`, check) suite.Run() @@ -175,14 +185,14 @@ func TestWithAfterStep(t *testing.T) { suite := NewSuite(t, WithFeaturesPath("features/background.feature"), WithAfterStep(func(ctx Context) { c++ - // feature should be *msgs.GherkinDocument_Feature + // feature should be *msgs.Feature feature, err := ctx.Get(FeatureKey{}) require.NoError(t, err) if _, ok := feature.(*msgs.Feature); !ok { t.Errorf("expected feature but got %T", feature) } - // scenario should be *msgs.GherkinDocument_Feature_Scenario + // scenario should be *msgs.Scenario scenario, err := ctx.Get(ScenarioKey{}) require.NoError(t, err) if _, ok := scenario.(*msgs.Scenario); !ok { @@ -191,10 +201,12 @@ func TestWithAfterStep(t *testing.T) { })) suite.AddStep(`I add (\d+) and (\d+)`, add) suite.AddStep(`the result should equal (\d+)`, check) + suite.AddStep(`I concat word {word} and text {text}`, concat) + suite.AddStep(`the result should equal text {text}`, checkt) suite.Run() - if err := assert.Equals(2, c); err != nil { + if err := assert.Equals(6, c); err != nil { t.Error(err) } } @@ -206,22 +218,19 @@ func TestWithBeforeStep(t *testing.T) { })) suite.AddStep(`I add (\d+) and (\d+)`, add) suite.AddStep(`the result should equal (\d+)`, check) + suite.AddStep(`I concat word {word} and text {text}`, concat) + suite.AddStep(`the result should equal text {text}`, checkt) suite.Run() - if err := assert.Equals(2, c); err != nil { + if err := assert.Equals(6, c); err != nil { t.Error(err) } } func TestIgnoredTags(t *testing.T) { - suite := NewSuite(t, WithFeaturesPath("features/ignored_tags.feature"), WithIgnoredTags("@ignore")) - suite.AddStep(`fail the test`, fail) - suite.Run() -} - -func TestIgnorFeatureWithTags(t *testing.T) { - suite := NewSuite(t, WithFeaturesPath("features/ignored_feature_tags.feature"), WithIgnoredTags("@ignore")) + suite := NewSuite(t, WithFeaturesPath("features/ignored_*tags.feature"), WithIgnoredTags("@ignore")) + suite.AddStep(`the test should pass`, pass) suite.AddStep(`fail the test`, fail) suite.Run() } diff --git a/steps_context_test.go b/steps_context_test.go index 8bf175d..8eeb9a5 100644 --- a/steps_context_test.go +++ b/steps_context_test.go @@ -3,7 +3,7 @@ package gobdd_test import ( "testing" - "github.com/go-bdd/gobdd" + gobdd "github.com/go-bdd/gobdd" "github.com/go-bdd/gobdd/context" )