Pretty Print when using rules (#440) (#480)

* Pretty Print when using rules (#440)

* Pretty Print when using rules (#440)

* fix a few formatting mistakes (#440)

* added test with rule and scenario outline (#440)
Этот коммит содержится в:
Brian Hnat 2022-06-17 10:32:07 -04:00 коммит произвёл GitHub
родитель c5a86a4e56
коммит 5d705e5b8e
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
8 изменённых файлов: 368 добавлений и 19 удалений

Просмотреть файл

@ -449,3 +449,102 @@ Feature: pretty formatter
16 steps (16 passed)
0s
"""
Scenario: Support of Feature Plus Rule
Given a feature "features/simple.feature" file:
"""
Feature: simple feature with a rule
simple feature description
Rule: simple rule
simple rule description
Example: simple scenario
simple scenario description
Given passing step
"""
When I run feature suite with formatter "pretty"
Then the rendered output will be as follows:
"""
Feature: simple feature with a rule
simple feature description
Example: simple scenario # features/simple.feature:5
Given passing step # suite_context.go:0 -> SuiteContext.func2
1 scenarios (1 passed)
1 steps (1 passed)
0s
"""
Scenario: Support of Feature Plus Rule with Background
Given a feature "features/simple.feature" file:
"""
Feature: simple feature with a rule with Background
simple feature description
Rule: simple rule
simple rule description
Background:
Given passing step
Example: simple scenario
simple scenario description
Given passing step
"""
When I run feature suite with formatter "pretty"
Then the rendered output will be as follows:
"""
Feature: simple feature with a rule with Background
simple feature description
Background:
Given passing step # suite_context.go:0 -> SuiteContext.func2
Example: simple scenario # features/simple.feature:7
Given passing step # suite_context.go:0 -> SuiteContext.func2
1 scenarios (1 passed)
2 steps (2 passed)
0s
"""
Scenario: Support of Feature Plus Rule with Scenario Outline
Given a feature "features/simple.feature" file:
"""
Feature: simple feature with a rule with Scenario Outline
simple feature description
Rule: simple rule
simple rule description
Scenario Outline: simple scenario
simple scenario description
Given <status> step
Examples: simple examples
| status |
| passing |
| failing |
"""
When I run feature suite with formatter "pretty"
Then the rendered output will be as follows:
"""
Feature: simple feature with a rule with Scenario Outline
simple feature description
Scenario Outline: simple scenario # features/simple.feature:5
Given <status> step # suite_context.go:0 -> SuiteContext.func2
Examples: simple examples
| status |
| passing |
| failing |
intentional failure
--- Failed steps:
Scenario Outline: simple scenario # features/simple.feature:5
Given failing step # features/simple.feature:8
Error: intentional failure
2 scenarios (1 passed, 1 failed)
2 steps (1 passed, 1 failed)
0s
"""

Просмотреть файл

@ -12,6 +12,7 @@ import (
"github.com/cucumber/godog/colors"
"github.com/cucumber/godog/formatters"
"github.com/cucumber/godog/internal/models"
)
func init() {
@ -350,15 +351,38 @@ func (f *Pretty) printTableHeader(row *messages.TableRow, max []int) {
f.printTableRow(row, max, cyan)
}
func isFirstScenarioInRule(rule *messages.Rule, scenario *messages.Scenario) bool {
if rule == nil || scenario == nil {
return false
}
var firstScenario *messages.Scenario
for _, c := range rule.Children {
if c.Scenario != nil {
firstScenario = c.Scenario
break
}
}
return firstScenario != nil && firstScenario.Id == scenario.Id
}
func isFirstPickleAndNoRule(feature *models.Feature, pickle *messages.Pickle, rule *messages.Rule) bool {
if rule != nil {
return false
}
return feature.Pickles[0].Id == pickle.Id
}
func (f *Pretty) printStep(pickle *messages.Pickle, pickleStep *messages.PickleStep) {
feature := f.Storage.MustGetFeature(pickle.Uri)
astBackground := feature.FindBackground(pickle.AstNodeIds[0])
astScenario := feature.FindScenario(pickle.AstNodeIds[0])
astRule := feature.FindRule(pickle.AstNodeIds[0])
astStep := feature.FindStep(pickleStep.AstNodeIds[0])
var astBackgroundStep bool
var firstExecutedBackgroundStep bool
var backgroundSteps int
if astBackground != nil {
backgroundSteps = len(astBackground.Steps)
@ -371,7 +395,7 @@ func (f *Pretty) printStep(pickle *messages.Pickle, pickleStep *messages.PickleS
}
}
firstPickle := feature.Pickles[0].Id == pickle.Id
firstPickle := isFirstPickleAndNoRule(feature, pickle, astRule) || isFirstScenarioInRule(astRule, astScenario)
if astBackgroundStep && !firstPickle {
return

Просмотреть файл

@ -0,0 +1,30 @@
Feature: rules with examples with backgrounds
Rule: first rule
Background: for first rule
Given passing step
And passing step
Example: rule 1 example 1
When passing step
Then passing step
Example: rule 1 example 2
When passing step
Then passing step
Rule: second rule
Background: for second rule
Given passing step
And passing step
Example: rule 1 example 1
When passing step
Then passing step
Example: rule 2 example 2
When passing step
Then passing step

Просмотреть файл

@ -0,0 +1,29 @@
<bold-white>Feature:</bold-white> rules with examples with backgrounds
<bold-white>Background:</bold-white> for first rule
<green>Given</green> <green>passing step</green> <bold-black># fmt_output_test.go:101 -> github.com/cucumber/godog/internal/formatters_test.passingStepDef</bold-black>
<green>And</green> <green>passing step</green> <bold-black># fmt_output_test.go:101 -> github.com/cucumber/godog/internal/formatters_test.passingStepDef</bold-black>
<bold-white>Example:</bold-white> rule 1 example 1 <bold-black># formatter-tests/features/rules_with_examples_with_backgrounds.feature:9</bold-black>
<green>When</green> <green>passing step</green> <bold-black># fmt_output_test.go:101 -> github.com/cucumber/godog/internal/formatters_test.passingStepDef</bold-black>
<green>Then</green> <green>passing step</green> <bold-black># fmt_output_test.go:101 -> github.com/cucumber/godog/internal/formatters_test.passingStepDef</bold-black>
<bold-white>Example:</bold-white> rule 1 example 2 <bold-black># formatter-tests/features/rules_with_examples_with_backgrounds.feature:13</bold-black>
<green>When</green> <green>passing step</green> <bold-black># fmt_output_test.go:101 -> github.com/cucumber/godog/internal/formatters_test.passingStepDef</bold-black>
<green>Then</green> <green>passing step</green> <bold-black># fmt_output_test.go:101 -> github.com/cucumber/godog/internal/formatters_test.passingStepDef</bold-black>
<bold-white>Background:</bold-white> for second rule
<green>Given</green> <green>passing step</green> <bold-black># fmt_output_test.go:101 -> github.com/cucumber/godog/internal/formatters_test.passingStepDef</bold-black>
<green>And</green> <green>passing step</green> <bold-black># fmt_output_test.go:101 -> github.com/cucumber/godog/internal/formatters_test.passingStepDef</bold-black>
<bold-white>Example:</bold-white> rule 1 example 1 <bold-black># formatter-tests/features/rules_with_examples_with_backgrounds.feature:24</bold-black>
<green>When</green> <green>passing step</green> <bold-black># fmt_output_test.go:101 -> github.com/cucumber/godog/internal/formatters_test.passingStepDef</bold-black>
<green>Then</green> <green>passing step</green> <bold-black># fmt_output_test.go:101 -> github.com/cucumber/godog/internal/formatters_test.passingStepDef</bold-black>
<bold-white>Example:</bold-white> rule 2 example 2 <bold-black># formatter-tests/features/rules_with_examples_with_backgrounds.feature:28</bold-black>
<green>When</green> <green>passing step</green> <bold-black># fmt_output_test.go:101 -> github.com/cucumber/godog/internal/formatters_test.passingStepDef</bold-black>
<green>Then</green> <green>passing step</green> <bold-black># fmt_output_test.go:101 -> github.com/cucumber/godog/internal/formatters_test.passingStepDef</bold-black>
4 scenarios (<green>4 passed</green>)
16 steps (<green>16 passed</green>)
0s

Просмотреть файл

@ -13,12 +13,35 @@ type Feature struct {
Content []byte
}
// FindScenario ...
// FindRule returns the rule to which the given scenario belongs
func (f Feature) FindRule(astScenarioID string) *messages.Rule {
for _, child := range f.GherkinDocument.Feature.Children {
if ru := child.Rule; ru != nil {
if rc := child.Rule; rc != nil {
for _, rcc := range rc.Children {
if sc := rcc.Scenario; sc != nil && sc.Id == astScenarioID {
return ru
}
}
}
}
}
return nil
}
// FindScenario returns the scenario in the feature or in a rule in the feature
func (f Feature) FindScenario(astScenarioID string) *messages.Scenario {
for _, child := range f.GherkinDocument.Feature.Children {
if sc := child.Scenario; sc != nil && sc.Id == astScenarioID {
return sc
}
if rc := child.Rule; rc != nil {
for _, rcc := range rc.Children {
if sc := rcc.Scenario; sc != nil && sc.Id == astScenarioID {
return sc
}
}
}
}
return nil
@ -36,6 +59,18 @@ func (f Feature) FindBackground(astScenarioID string) *messages.Background {
if sc := child.Scenario; sc != nil && sc.Id == astScenarioID {
return bg
}
if ru := child.Rule; ru != nil {
for _, rc := range ru.Children {
if tmp := rc.Background; tmp != nil {
bg = tmp
}
if sc := rc.Scenario; sc != nil && sc.Id == astScenarioID {
return bg
}
}
}
}
return nil
@ -53,6 +88,19 @@ func (f Feature) FindExample(exampleAstID string) (*messages.Examples, *messages
}
}
}
if ru := child.Rule; ru != nil {
for _, rc := range ru.Children {
if sc := rc.Scenario; sc != nil {
for _, example := range sc.Examples {
for _, row := range example.TableBody {
if row.Id == exampleAstID {
return example, row
}
}
}
}
}
}
}
return nil, nil
@ -61,6 +109,27 @@ func (f Feature) FindExample(exampleAstID string) (*messages.Examples, *messages
// FindStep ...
func (f Feature) FindStep(astStepID string) *messages.Step {
for _, child := range f.GherkinDocument.Feature.Children {
if ru := child.Rule; ru != nil {
for _, ch := range ru.Children {
if sc := ch.Scenario; sc != nil {
for _, step := range sc.Steps {
if step.Id == astStepID {
return step
}
}
}
if bg := ch.Background; bg != nil {
for _, step := range bg.Steps {
if step.Id == astStepID {
return step
}
}
}
}
}
if sc := child.Scenario; sc != nil {
for _, step := range sc.Steps {
if step.Id == astStepID {

Просмотреть файл

@ -3,6 +3,7 @@ package models_test
import (
"testing"
"github.com/cucumber/godog/internal/models"
"github.com/cucumber/godog/internal/testutils"
"github.com/stretchr/testify/assert"
)
@ -32,29 +33,76 @@ func Test_Find(t *testing.T) {
assert.NotNilf(t, step, "expected step to not be nil")
}
})
t.Run("rule", func(t *testing.T) {
sc := ft.FindRule(ft.Pickles[0].AstNodeIds[0])
assert.Nilf(t, sc, "expected rule to be nil")
})
}
func Test_NotFind(t *testing.T) {
ft := testutils.BuildTestFeature(t)
func Test_FindInRule(t *testing.T) {
ft := testutils.BuildTestFeatureWithRules(t)
t.Run("rule", func(t *testing.T) {
sc := ft.FindRule(ft.Pickles[0].AstNodeIds[0])
assert.NotNilf(t, sc, "expected rule to not be nil")
})
t.Run("scenario", func(t *testing.T) {
sc := ft.FindScenario("-")
assert.Nilf(t, sc, "expected scenario to be nil")
sc := ft.FindScenario(ft.Pickles[0].AstNodeIds[0])
assert.NotNilf(t, sc, "expected scenario to not be nil")
})
t.Run("background", func(t *testing.T) {
bg := ft.FindBackground("-")
assert.Nilf(t, bg, "expected background to be nil")
bg := ft.FindBackground(ft.Pickles[0].AstNodeIds[0])
assert.NotNilf(t, bg, "expected background to not be nil")
})
t.Run("example", func(t *testing.T) {
example, row := ft.FindExample("-")
assert.Nilf(t, example, "expected example to be nil")
assert.Nilf(t, row, "expected table row to be nil")
example, row := ft.FindExample(ft.Pickles[1].AstNodeIds[1])
assert.NotNilf(t, example, "expected example to not be nil")
assert.NotNilf(t, row, "expected table row to not be nil")
})
t.Run("step", func(t *testing.T) {
step := ft.FindStep("-")
assert.Nilf(t, step, "expected step to be nil")
for _, ps := range ft.Pickles[0].Steps {
step := ft.FindStep(ps.AstNodeIds[0])
assert.NotNilf(t, step, "expected step to not be nil")
}
})
}
func Test_NotFind(t *testing.T) {
testCases := []struct {
Feature models.Feature
}{
{testutils.BuildTestFeature(t)},
{testutils.BuildTestFeatureWithRules(t)},
}
for _, tc := range testCases {
ft := tc.Feature
t.Run("scenario", func(t *testing.T) {
sc := ft.FindScenario("-")
assert.Nilf(t, sc, "expected scenario to be nil")
})
t.Run("background", func(t *testing.T) {
bg := ft.FindBackground("-")
assert.Nilf(t, bg, "expected background to be nil")
})
t.Run("example", func(t *testing.T) {
example, row := ft.FindExample("-")
assert.Nilf(t, example, "expected example to be nil")
assert.Nilf(t, row, "expected table row to be nil")
})
t.Run("step", func(t *testing.T) {
step := ft.FindStep("-")
assert.Nilf(t, step, "expected step to be nil")
})
}
}

Просмотреть файл

@ -58,3 +58,53 @@ Scenario Outline: Eat <dec> out of <beginning>
Examples:
| begin | dec | remain |
| 12 | 5 | 7 |`
// BuildTestFeature creates a feature with rules for testing purpose.
//
// The created feature includes:
// - a background
// - one normal scenario with three steps
// - one outline scenario with one example and three steps
func BuildTestFeatureWithRules(t *testing.T) models.Feature {
newIDFunc := (&messages.Incrementing{}).NewId
gherkinDocument, err := gherkin.ParseGherkinDocument(strings.NewReader(featureWithRuleContent), newIDFunc)
require.NoError(t, err)
path := t.Name()
gherkinDocument.Uri = path
pickles := gherkin.Pickles(*gherkinDocument, path, newIDFunc)
ft := models.Feature{GherkinDocument: gherkinDocument, Pickles: pickles, Content: []byte(featureWithRuleContent)}
require.Len(t, ft.Pickles, 2)
require.Len(t, ft.Pickles[0].AstNodeIds, 1)
require.Len(t, ft.Pickles[0].Steps, 3)
require.Len(t, ft.Pickles[1].AstNodeIds, 2)
require.Len(t, ft.Pickles[1].Steps, 3)
return ft
}
const featureWithRuleContent = `Feature: eat godogs
In order to be happy
As a hungry gopher
I need to be able to eat godogs
Rule: eating godogs
Background:
Given there are <begin> godogs
Scenario: Eat 5 out of 12
When I eat 5
Then there should be 7 remaining
Scenario Outline: Eat <dec> out of <beginning>
When I eat <dec>
Then there should be <remain> remaining
Examples:
| begin | dec | remain |
| 12 | 5 | 7 |`

Просмотреть файл

@ -432,11 +432,11 @@ func Test_AllFeaturesRun(t *testing.T) {
...................................................................... 140
...................................................................... 210
...................................................................... 280
........................................ 320
................................................. 329
83 scenarios (83 passed)
320 steps (320 passed)
86 scenarios (86 passed)
329 steps (329 passed)
0s
`
@ -459,11 +459,11 @@ func Test_AllFeaturesRunAsSubtests(t *testing.T) {
...................................................................... 140
...................................................................... 210
...................................................................... 280
........................................ 320
................................................. 329
83 scenarios (83 passed)
320 steps (320 passed)
86 scenarios (86 passed)
329 steps (329 passed)
0s
`