From 3a2a357bd0d20ad21ccb3b2694362f8fe1dd0417 Mon Sep 17 00:00:00 2001 From: gedi Date: Sun, 14 Jun 2015 00:32:47 +0300 Subject: [PATCH] an initial formatter impl --- colors.go | 8 +++- config.go | 4 ++ features/suite.feature | 2 +- formatter.go | 27 ++++++++++++++ gherkin/gherkin.go | 23 +++++++----- gherkin/steps_test.go | 4 +- suite.go | 85 +++++++++++++++++++++--------------------- 7 files changed, 96 insertions(+), 57 deletions(-) create mode 100644 formatter.go diff --git a/colors.go b/colors.go index 42ba30b..a6634a1 100644 --- a/colors.go +++ b/colors.go @@ -4,6 +4,8 @@ import "fmt" type color int +const ansiEscape = "\x1b" + const ( black color = iota + 30 red @@ -16,5 +18,9 @@ const ( ) func cl(s interface{}, c color) string { - return fmt.Sprintf("\033[%dm%v\033[0m", c, s) + return fmt.Sprintf("%s[%dm%v%s[0m", ansiEscape, c, s, ansiEscape) +} + +func bcl(s interface{}, c color) string { + return fmt.Sprintf("%s[1;%dm%v%s[0m", ansiEscape, c, s, ansiEscape) } diff --git a/config.go b/config.go index 0dca616..feb57bd 100644 --- a/config.go +++ b/config.go @@ -46,6 +46,10 @@ func (c config) features() (lst []*gherkin.Feature, err error) { }) } +func (c config) formatter() formatter { + return &pretty{} +} + func fatal(err error) { if err != nil { fmt.Println(err) diff --git a/features/suite.feature b/features/suite.feature index 59db83d..68f7180 100644 --- a/features/suite.feature +++ b/features/suite.feature @@ -3,7 +3,7 @@ Feature: godog bdd suite As a suite I need to be able to register and run features - Scenario: + Scenario: parses all features in path Given a feature path "features" When I parse features Then I should have 1 feature file diff --git a/formatter.go b/formatter.go new file mode 100644 index 0000000..12525e2 --- /dev/null +++ b/formatter.go @@ -0,0 +1,27 @@ +package godog + +import ( + "fmt" + + "github.com/DATA-DOG/godog/gherkin" +) + +type formatter interface { + node(interface{}) +} + +type pretty struct{} + +func (f *pretty) node(node interface{}) { + switch t := node.(type) { + case *gherkin.Feature: + fmt.Println(bcl("Feature: ", white) + t.Title) + fmt.Println(t.Description + "\n") + case *gherkin.Background: + fmt.Println(bcl("Background:", white)) + case *gherkin.Scenario: + fmt.Println(bcl("Scenario: ", white) + t.Title) + case *gherkin.Step: + fmt.Println(bcl(t.Token.Text, green)) + } +} diff --git a/gherkin/gherkin.go b/gherkin/gherkin.go index 21fefe6..6ca6ac7 100644 --- a/gherkin/gherkin.go +++ b/gherkin/gherkin.go @@ -77,6 +77,7 @@ type Tag string // Tags is an array of tags type Tags []Tag +// Has checks whether the tag list has a tag func (t Tags) Has(tag Tag) bool { for _, tg := range t { if tg == tag { @@ -97,17 +98,17 @@ func (t Tags) Has(tag Tag) bool { // be used to filter out or run specific // initialization tasks type Scenario struct { + *Token Title string Steps []*Step Tags Tags Examples *Table - Comment string } // Background steps are run before every scenario type Background struct { - Steps []*Step - Comment string + *Token + Steps []*Step } // StepType is a general type of step @@ -121,8 +122,8 @@ const ( // Step describes a Scenario or Background step type Step struct { + *Token Text string - Comment string Type StepType PyString *PyString Table *Table @@ -130,6 +131,7 @@ type Step struct { // Feature describes the whole feature type Feature struct { + *Token Path string Tags Tags Description string @@ -137,16 +139,17 @@ type Feature struct { Background *Background Scenarios []*Scenario AST *AST - Comment string } // PyString is a multiline text object used with step definition type PyString struct { + *Token Body string } // Table is a row group object used with step definition type Table struct { + *Token rows [][]string } @@ -229,11 +232,11 @@ func (p *parser) parseFeature() (ft *Feature, err error) { return ft, p.err("expected a file to begin with a feature definition, but got '"+tok.Type.String()+"' instead", tok.Line) } ft.Title = tok.Value - ft.Comment = tok.Comment + ft.Token = tok var desc []string for ; p.peek().Type == TEXT; tok = p.next() { - desc = append(desc, tok.Value) + desc = append(desc, p.peek().Text) } ft.Description = strings.Join(desc, "\n") @@ -244,7 +247,7 @@ func (p *parser) parseFeature() (ft *Feature, err error) { return ft, p.err("there can only be a single background section, but found another", tok.Line) } - ft.Background = &Background{Comment: tok.Comment} + ft.Background = &Background{Token: tok} p.next() // jump to background steps if ft.Background.Steps, err = p.parseSteps(); err != nil { return ft, err @@ -283,7 +286,7 @@ func (p *parser) parseFeature() (ft *Feature, err error) { func (p *parser) parseScenario() (s *Scenario, err error) { tok := p.next() - s = &Scenario{Title: tok.Value, Comment: tok.Comment} + s = &Scenario{Title: tok.Value, Token: tok} if s.Steps, err = p.parseSteps(); err != nil { return s, err } @@ -305,7 +308,7 @@ func (p *parser) parseScenario() (s *Scenario, err error) { func (p *parser) parseSteps() (steps []*Step, err error) { for tok := p.peek(); tok.OfType(allSteps...); tok = p.peek() { - step := &Step{Text: tok.Value, Comment: tok.Comment} + step := &Step{Text: tok.Value, Token: tok} switch tok.Type { case GIVEN: step.Type = Given diff --git a/gherkin/steps_test.go b/gherkin/steps_test.go index eb87e3d..45ae577 100644 --- a/gherkin/steps_test.go +++ b/gherkin/steps_test.go @@ -66,8 +66,8 @@ func (s *Step) assertPyString(text string, t *testing.T) { } func (s *Step) assertComment(comment string, t *testing.T) { - if s.Comment != comment { - t.Fatalf("expected step '%s' comment to be '%s', but got '%s'", s.Text, comment, s.Comment) + if s.Token.Comment != comment { + t.Fatalf("expected step '%s' comment to be '%s', but got '%s'", s.Text, comment, s.Token.Comment) } } diff --git a/suite.go b/suite.go index f0cf633..008c5fd 100644 --- a/suite.go +++ b/suite.go @@ -73,6 +73,7 @@ type Suite interface { type suite struct { steps map[*regexp.Regexp]StepHandler features []*gherkin.Feature + fmt formatter } // New initializes a suite which supports the Suite @@ -107,6 +108,7 @@ func (s *suite) Step(exp *regexp.Regexp, h StepHandler) { // Run - runs a godog feature suite func (s *suite) Run() { var err error + s.fmt = cfg.formatter() s.features, err = cfg.features() fatal(err) @@ -118,57 +120,54 @@ func (s *suite) Run() { } } +func (s *suite) runStep(step *gherkin.Step) { + var handler StepHandler + var args []Arg + for r, h := range s.steps { + if m := r.FindStringSubmatch(step.Text); len(m) > 0 { + handler = h + for _, a := range m[1:] { + args = append(args, Arg(a)) + } + break + } + } + if handler == nil { + fmt.Println("PENDING") + return + } + defer func() { + if e := recover(); e != nil { + fmt.Println("PANIC") + } + }() + if err := handler.HandleStep(args...); err != nil { + fmt.Println("ERR") + } else { + fmt.Println("OK") + } +} + func (s *suite) runFeature(f *gherkin.Feature) { + s.fmt.node(f) + var background bool for _, scenario := range f.Scenarios { if f.Background != nil { + if !background { + s.fmt.node(f.Background) + } for _, step := range f.Background.Steps { - var handler StepHandler - var args []Arg - for r, h := range s.steps { - if m := r.FindStringSubmatch(step.Text); len(m) > 0 { - handler = h - for _, a := range m[1:] { - args = append(args, Arg(a)) - } - break - } - } - if handler != nil { - if err := handler.HandleStep(args...); err != nil { - // @TODO: scenario fails, step failed - fmt.Println("ERR") - } else { - fmt.Println("OK") - } - // @TODO: handle panic and recover - } else { - fmt.Println("PENDING") + s.runStep(step) + if !background { + s.fmt.node(step) } } + background = true } + s.fmt.node(scenario) for _, step := range scenario.Steps { - var handler StepHandler - var args []Arg - for r, h := range s.steps { - if m := r.FindStringSubmatch(step.Text); len(m) > 0 { - handler = h - for _, a := range m[1:] { - args = append(args, Arg(a)) - } - break - } - } - if handler != nil { - if err := handler.HandleStep(args...); err != nil { - // @TODO: scenario fails, step failed - fmt.Println("ERR") - } else { - fmt.Println("OK") - } - // @TODO: handle panic and recover - } else { - fmt.Println("PENDING") - } + s.runStep(step) + s.fmt.node(step) } } }