Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelsauter committed Nov 16, 2021
1 parent 86a5635 commit ea16cbe
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 5 deletions.
24 changes: 24 additions & 0 deletions features/formatter/pretty.feature
Expand Up @@ -23,6 +23,30 @@ Feature: pretty formatter
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 Scenario Node With Tags
Given a feature "features/simple.feature" file:
"""
Expand Down
43 changes: 40 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.Scenario.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,10 +375,15 @@ 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])

// TODO: if this is the first scenario, and there is a rule, then the rule
// header must be printed.
// indent all if there is a rule.

var astBackgroundStep bool
var firstExecutedBackgroundStep bool
var backgroundSteps int
Expand Down Expand Up @@ -391,6 +419,9 @@ func (f *Pretty) printStep(pickle *messages.Pickle, pickleStep *messages.PickleS

firstExecutedScenarioStep := astScenario.Steps[0].Id == pickleStep.AstNodeIds[0]
if !astBackgroundStep && firstExecutedScenarioStep {
if astRule != nil {
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 +452,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 +469,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 +486,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 +512,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 +552,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 +567,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
50 changes: 48 additions & 2 deletions internal/models/feature.go
Expand Up @@ -13,18 +13,42 @@ type Feature struct {
Content []byte
}

// FindScenario ...
// FindRule returns the rule containing astScenarioID.
func (f Feature) FindRule(astScenarioID string) *messages.Rule {
for _, child := range f.GherkinDocument.Feature.Children {
if ru := child.Rule; ru != nil {
for _, ruc := range ru.Children {
if sc := ruc.Scenario; sc != nil && sc.Id == astScenarioID {
return ru
}
}
}
}

return nil
}

// FindScenario returns the scenario matching astScenarioID. The scenario
// might be a direct child of Feature, or a child of a Rule within a 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
}

// FindBackground ...
// TODO: must find children of rule!!!!
func (f Feature) FindBackground(astScenarioID string) *messages.Background {
var bg *messages.Background

Expand Down Expand Up @@ -58,7 +82,9 @@ func (f Feature) FindExample(exampleAstID string) (*messages.Examples, *messages
return nil, nil
}

// FindStep ...
// FindStep returns the step matching astStepID. The step
// might be a child of a Scenario or a Background (which might be contained
// inside a Rule).
func (f Feature) FindStep(astStepID string) *messages.Step {
for _, child := range f.GherkinDocument.Feature.Children {
if sc := child.Scenario; sc != nil {
Expand All @@ -76,6 +102,26 @@ func (f Feature) FindStep(astStepID string) *messages.Step {
}
}
}

if ru := child.Rule; ru != nil {
for _, ruc := range ru.Children {
if sc := ruc.Scenario; sc != nil {
for _, step := range sc.Steps {
if step.Id == astStepID {
return step
}
}
}

if bg := ruc.Background; bg != nil {
for _, step := range bg.Steps {
if step.Id == astStepID {
return step
}
}
}
}
}
}

return nil
Expand Down

0 comments on commit ea16cbe

Please sign in to comment.