Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support rule keyword #441

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
175 changes: 175 additions & 0 deletions features/formatter/pretty.feature
Expand Up @@ -23,6 +23,181 @@ Feature: pretty formatter
0s
"""

Scenario: Support of Feature Plus Background and Scenario Node
Given a feature "features/simple.feature" file:
"""
Feature: simple feature
simple feature description
Background: simple background
Given passing step
And passing step
Scenario: simple scenario
simple scenario description
"""
When I run feature suite with formatter "pretty"
Then the rendered output will be as follows:
"""
Feature: simple feature
simple feature description

Background: simple background
Given passing step
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused by this indentation - do we indent the steps by 4 characters within a Background?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or is this about having a universal fixed indentation for every step irrespective of whether it's inside a Rule, Background, Scenario etc?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the indentation is actually ignored in the tests. I changed indentation and it did not caused the tests to fail.

And passing step

Scenario: simple scenario # features/simple.feature:6

1 scenarios (1 undefined)
No steps
0s
"""

Scenario: Support of Feature Plus Rule Node
Given a feature "features/simple.feature" file:
"""
Feature: simple feature
simple feature description
Rule: simple rule
Scenario: simple scenario
simple scenario description
"""
When I run feature suite with formatter "pretty"
Then the rendered output will be as follows:
"""
Feature: simple feature
simple feature description

Rule: simple rule

Scenario: simple scenario # features/simple.feature:4

1 scenarios (1 undefined)
No steps
0s
"""

Scenario: Support of Feature Plus Rule Node with Background
Given a feature "features/simple.feature" file:
"""
Feature: simple feature
simple feature description

Rule: simple rule

Background: simple background
Given something
And another thing

Scenario: simple scenario
simple scenario description
"""
When I run feature suite with formatter "pretty"
Then the rendered output will be as follows:
"""
Feature: simple feature
simple feature description

Rule: simple rule

Background: simple background
Given something
And another thing

Scenario: simple scenario # features/simple.feature:10

1 scenarios (1 undefined)
No steps
0s
"""

Scenario: Support of Feature Plus Rule Node with multiple scenarios
Given a feature "features/simple.feature" file:
"""
Feature: simple feature
simple feature description

Rule: simple rule

Scenario: simple scenario
simple scenario description

Given passing step
Then passing step

Scenario: simple second scenario
simple second scenario description

Given passing step
Then passing step
"""
When I run feature suite with formatter "pretty"
Then the rendered output will be as follows:
"""
Feature: simple feature
simple feature description

Rule: simple rule

Scenario: simple scenario # features/simple.feature:6
Given passing step # suite_context_test.go:0 -> InitializeScenario.func2
Then passing step # suite_context_test.go:0 -> InitializeScenario.func2

Scenario: simple second scenario # features/simple.feature:12
Given passing step # suite_context_test.go:0 -> InitializeScenario.func2
Then passing step # suite_context_test.go:0 -> InitializeScenario.func2

2 scenarios (2 passed)
4 steps (4 passed)
0s
"""

Scenario: Support of Feature Plus Rule Node with Background and multiple scenarios
Given a feature "features/simple.feature" file:
"""
Feature: simple feature
simple feature description
Rule: simple rule

Background: simple background
Given passing step
And passing step

Scenario: simple scenario
simple scenario description

Given passing step
Then passing step

Scenario: simple second scenario
simple second scenario description

Given passing step
Then passing step
"""
When I run feature suite with formatter "pretty"
Then the rendered output will be as follows:
"""
Feature: simple feature
simple feature description

Rule: simple rule

Background: simple background
Given passing step # suite_context_test.go:0 -> InitializeScenario.func2
And passing step # suite_context_test.go:0 -> InitializeScenario.func2

Scenario: simple scenario # features/simple.feature:9
Given passing step # suite_context_test.go:0 -> InitializeScenario.func2
Then passing step # suite_context_test.go:0 -> InitializeScenario.func2

Scenario: simple second scenario # features/simple.feature:15
Given passing step # suite_context_test.go:0 -> InitializeScenario.func2
Then passing step # suite_context_test.go:0 -> InitializeScenario.func2

2 scenarios (2 passed)
8 steps (8 passed)
0s
"""

Scenario: Support of Feature Plus Scenario Node With Tags
Given a feature "features/simple.feature" file:
"""
Expand Down
61 changes: 58 additions & 3 deletions internal/formatters/fmt_pretty.go
Expand Up @@ -123,6 +123,7 @@ func (f *Pretty) Pending(pickle *messages.Pickle, step *messages.PickleStep, mat
f.printStep(pickle, step)
}

// printFeature prints given feature (with title and description) to f.out.
func (f *Pretty) printFeature(feature *messages.Feature) {
fmt.Fprintln(f.out, keywordAndName(feature.Keyword, feature.Name))
if strings.TrimSpace(feature.Description) != "" {
Expand All @@ -132,6 +133,7 @@ func (f *Pretty) printFeature(feature *messages.Feature) {
}
}

// keywordAndName returns formatted keyword and name.
func keywordAndName(keyword, name string) string {
title := whiteb(keyword + ":")
if len(name) > 0 {
Expand All @@ -140,8 +142,11 @@ func keywordAndName(keyword, name string) string {
return title
}

// scenarioLengths returns the length of the scenario header, and the maximum
// length of all steps.
func (f *Pretty) scenarioLengths(pickle *messages.Pickle) (scenarioHeaderLength int, maxLength int) {
feature := f.Storage.MustGetFeature(pickle.Uri)
astRule := feature.FindRule(pickle.AstNodeIds[0])
astScenario := feature.FindScenario(pickle.AstNodeIds[0])
astBackground := feature.FindBackground(pickle.AstNodeIds[0])

Expand All @@ -151,24 +156,42 @@ func (f *Pretty) scenarioLengths(pickle *messages.Pickle) (scenarioHeaderLength
if astBackground != nil {
maxLength = f.longestStep(astBackground.Steps, maxLength)
}
if astRule != nil {
for _, rc := range astRule.Children {
if rc.Scenario != nil {
scenarioHeaderLength = f.lengthPickle(astScenario.Keyword, astScenario.Name)
maxLength = f.longestStep(rc.Scenario.Steps, maxLength)
} else if rc.Background != nil {
maxLength = f.longestStep(rc.Background.Steps, maxLength)
}
}
}

return scenarioHeaderLength, maxLength
}

// printScenarioHeader prints scenario header (keyword/name) with feature line
// reference. The scenario is prefixed with whitespace equal to spaceFilling.
func (f *Pretty) printScenarioHeader(pickle *messages.Pickle, astScenario *messages.Scenario, spaceFilling int) {
feature := f.Storage.MustGetFeature(pickle.Uri)
text := s(f.indent) + keywordAndName(astScenario.Keyword, astScenario.Name)
text += s(spaceFilling) + line(feature.Uri, astScenario.Location)
fmt.Fprintln(f.out, "\n"+text)
}

// printUndefinedPickle prints pickles that are not defined yet.
func (f *Pretty) printUndefinedPickle(pickle *messages.Pickle) {
feature := f.Storage.MustGetFeature(pickle.Uri)
astRule := feature.FindRule(pickle.AstNodeIds[0])
astScenario := feature.FindScenario(pickle.AstNodeIds[0])
astBackground := feature.FindBackground(pickle.AstNodeIds[0])

scenarioHeaderLength, maxLength := f.scenarioLengths(pickle)

if astRule != nil {
fmt.Fprintln(f.out, "\n"+s(f.indent)+keywordAndName(astRule.Keyword, astRule.Name))
}

if astBackground != nil {
fmt.Fprintln(f.out, "\n"+s(f.indent)+keywordAndName(astBackground.Keyword, astBackground.Name))
for _, step := range astBackground.Steps {
Expand Down Expand Up @@ -352,6 +375,7 @@ func (f *Pretty) printTableHeader(row *messages.TableRow, max []int) {

func (f *Pretty) printStep(pickle *messages.Pickle, pickleStep *messages.PickleStep) {
feature := f.Storage.MustGetFeature(pickle.Uri)
astRule := feature.FindRule(pickle.AstNodeIds[0])
astBackground := feature.FindBackground(pickle.AstNodeIds[0])
astScenario := feature.FindScenario(pickle.AstNodeIds[0])
astStep := feature.FindStep(pickleStep.AstNodeIds[0])
Expand All @@ -378,6 +402,9 @@ func (f *Pretty) printStep(pickle *messages.Pickle, pickleStep *messages.PickleS
}

if astBackgroundStep && firstExecutedBackgroundStep {
if astRule != nil {
fmt.Fprintln(f.out, "\n"+s(f.indent)+keywordAndName(astRule.Keyword, astRule.Name))
}
fmt.Fprintln(f.out, "\n"+s(f.indent)+keywordAndName(astBackground.Keyword, astBackground.Name))
}

Expand All @@ -391,6 +418,28 @@ func (f *Pretty) printStep(pickle *messages.Pickle, pickleStep *messages.PickleS

firstExecutedScenarioStep := astScenario.Steps[0].Id == pickleStep.AstNodeIds[0]
if !astBackgroundStep && firstExecutedScenarioStep {
// The first scenario step is responsible for printing the rule unless
// it has already been printed by the background.
if astRule != nil {
var firstScenarioOfRule bool
var ruleHasBackground bool
for _, rc := range astRule.Children {
if sc := rc.Scenario; sc != nil {
firstScenarioOfRule = sc.Id == astScenario.Id
break
}
}
for _, rc := range astRule.Children {
if bc := rc.Background; bc != nil {
ruleHasBackground = true
break
}
}
if firstScenarioOfRule && !ruleHasBackground {
fmt.Fprintln(f.out, "\n"+s(f.indent)+keywordAndName(astRule.Keyword, astRule.Name))
}
}

f.printScenarioHeader(pickle, astScenario, maxLength-scenarioHeaderLength)
}

Expand Down Expand Up @@ -421,6 +470,7 @@ func (f *Pretty) printStep(pickle *messages.Pickle, pickleStep *messages.PickleS
}
}

// printDocString prints a formatted docString to f.out.
func (f *Pretty) printDocString(docString *messages.DocString) {
var ct string

Expand All @@ -437,7 +487,7 @@ func (f *Pretty) printDocString(docString *messages.DocString) {
fmt.Fprintln(f.out, s(f.indent*3)+cyan(docString.Delimiter))
}

// print table with aligned table cells
// printTable prints table with aligned table cells
// @TODO: need to make example header cells bold
func (f *Pretty) printTable(t *messages.PickleTable, c colors.ColorFunc) {
maxColLengths := maxColLengths(t, c)
Expand All @@ -454,7 +504,7 @@ func (f *Pretty) printTable(t *messages.PickleTable, c colors.ColorFunc) {
}
}

// longest gives a list of longest columns of all rows in Table
// maxColLengths returns a list of longest columns of all rows in Table
func maxColLengths(t *messages.PickleTable, clrs ...colors.ColorFunc) []int {
if t == nil {
return []int{}
Expand All @@ -480,6 +530,7 @@ func maxColLengths(t *messages.PickleTable, clrs ...colors.ColorFunc) []int {
return longest
}

// longestExampleRow returns a list of longest example rows
func longestExampleRow(t *messages.Examples, clrs ...colors.ColorFunc) []int {
if t == nil {
return []int{}
Expand Down Expand Up @@ -519,6 +570,8 @@ func longestExampleRow(t *messages.Examples, clrs ...colors.ColorFunc) []int {
return longest
}

// longestStep returns the length of the longest step in given steps, or
// pickleLength if that is greater.
func (f *Pretty) longestStep(steps []*messages.Step, pickleLength int) int {
max := pickleLength

Expand All @@ -532,14 +585,16 @@ func (f *Pretty) longestStep(steps []*messages.Step, pickleLength int) int {
return max
}

// a line number representation in feature file
// line returns a line number representation in feature file
func line(path string, loc *messages.Location) string {
// Path can contain a line number already.
// This line number has to be trimmed to avoid duplication.
path = strings.TrimSuffix(path, fmt.Sprintf(":%d", loc.Line))
return " " + blackb(fmt.Sprintf("# %s:%d", path, loc.Line))
}

// lengthPickleStep returns the length of a pickle step. The length is
// calculated based on indent, keyword, and associated text.
func (f *Pretty) lengthPickleStep(keyword, text string) int {
return f.indent*2 + utf8.RuneCountInString(strings.TrimSpace(keyword)+" "+text)
}
Expand Down