From 156613f767a907c4ba29d7cd1a5739d8ad28970a Mon Sep 17 00:00:00 2001 From: gedi Date: Mon, 15 Jun 2015 14:42:40 +0300 Subject: [PATCH] update step print matches --- formatter.go | 51 +++++++++++++++++++++++++++++++++++-------- gherkin/gherkin.go | 25 +-------------------- gherkin/steps_test.go | 20 ----------------- suite.go | 48 ++++++++++++++++++++++------------------ suite_test.go | 4 +--- 5 files changed, 71 insertions(+), 77 deletions(-) diff --git a/formatter.go b/formatter.go index 7d046eb..7a54d29 100644 --- a/formatter.go +++ b/formatter.go @@ -2,6 +2,7 @@ package godog import ( "fmt" + "math" "strings" "github.com/DATA-DOG/godog/gherkin" @@ -14,8 +15,8 @@ func init() { // Formatter is an interface for feature runner output type Formatter interface { Node(interface{}) - Failed(*gherkin.Step, error) - Passed(*gherkin.Step) + Failed(*gherkin.Step, *stepMatchHandler, error) + Passed(*gherkin.Step, *stepMatchHandler) Skipped(*gherkin.Step) Pending(*gherkin.Step) } @@ -65,14 +66,48 @@ func (f *pretty) Node(node interface{}) { f.background = t fmt.Println("\n" + bcl("Background:", white)) case *gherkin.Scenario: + f.scenario = t fmt.Println("\n"+strings.Repeat(" ", t.Token.Indent)+bcl("Scenario: ", white)+t.Title, f.line(t.Token)) } } -func (f *pretty) Passed(step *gherkin.Step) { - if f.canPrintStep(step) { - fmt.Println(cl(step.Token.Text, green)) +func (f *pretty) printMatchedStep(step *gherkin.Step, match *stepMatchHandler, c color) { + if !f.canPrintStep(step) { + return } + var text string + if m := (match.expr.FindStringSubmatchIndex(step.Text))[2:]; len(m) > 0 { + var pos, i int + for pos, i = 0, 0; i < len(m); i++ { + if math.Mod(float64(i), 2) == 0 { + text += cl(step.Text[pos:m[i]], c) + } else { + text += bcl(step.Text[pos:m[i]], c) + } + pos = m[i] + } + text += cl(step.Text[pos:len(step.Text)], c) + } else { + text = cl(step.Text, c) + } + + switch step.Token.Type { + case gherkin.GIVEN: + text = cl("Given", c) + " " + text + case gherkin.WHEN: + text = cl("When", c) + " " + text + case gherkin.THEN: + text = cl("Then", c) + " " + text + case gherkin.AND: + text = cl("And", c) + " " + text + case gherkin.BUT: + text = cl("But", c) + " " + text + } + fmt.Println(strings.Repeat(" ", step.Token.Indent) + text) +} + +func (f *pretty) Passed(step *gherkin.Step, match *stepMatchHandler) { + f.printMatchedStep(step, match, green) } func (f *pretty) Skipped(step *gherkin.Step) { @@ -87,8 +122,6 @@ func (f *pretty) Pending(step *gherkin.Step) { } } -func (f *pretty) Failed(step *gherkin.Step, err error) { - if f.canPrintStep(step) { - fmt.Println(cl(step.Token.Text, red)) - } +func (f *pretty) Failed(step *gherkin.Step, match *stepMatchHandler, err error) { + f.printMatchedStep(step, match, red) } diff --git a/gherkin/gherkin.go b/gherkin/gherkin.go index 6ca6ac7..ade38c9 100644 --- a/gherkin/gherkin.go +++ b/gherkin/gherkin.go @@ -111,20 +111,11 @@ type Background struct { Steps []*Step } -// StepType is a general type of step -type StepType string - -const ( - Given StepType = "Given" - When StepType = "When" - Then StepType = "Then" -) - // Step describes a Scenario or Background step type Step struct { *Token Text string - Type StepType + Type string PyString *PyString Table *Table } @@ -309,20 +300,6 @@ 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, Token: tok} - switch tok.Type { - case GIVEN: - step.Type = Given - case WHEN: - step.Type = When - case THEN: - step.Type = Then - case AND, BUT: - if len(steps) > 0 { - step.Type = steps[len(steps)-1].Type - } else { - step.Type = Given - } - } p.next() // have read a peeked step if step.Text[len(step.Text)-1] == ':' { diff --git a/gherkin/steps_test.go b/gherkin/steps_test.go index 45ae577..88b798c 100644 --- a/gherkin/steps_test.go +++ b/gherkin/steps_test.go @@ -44,12 +44,6 @@ var testStepSamples = map[string]string{ Then I expect the result`, } -func (s *Step) assertType(typ StepType, t *testing.T) { - if s.Type != typ { - t.Fatalf("expected step '%s' type to be '%s', but got '%s'", s.Text, typ, s.Type) - } -} - func (s *Step) assertText(text string, t *testing.T) { if s.Text != text { t.Fatalf("expected step text to be '%s', but got '%s'", text, s.Text) @@ -102,7 +96,6 @@ func Test_parse_basic_given_step(t *testing.T) { t.Fatalf("expected one step to be parsed") } - steps[0].assertType(Given, t) steps[0].assertText("I'm a step", t) p.next() // step over to eof @@ -126,7 +119,6 @@ func Test_parse_step_with_comment(t *testing.T) { t.Fatalf("expected one step to be parsed") } - steps[0].assertType(Given, t) steps[0].assertText("I'm an admin", t) steps[0].assertComment("sets admin permissions", t) @@ -151,7 +143,6 @@ func Test_parse_hash_table_given_step(t *testing.T) { t.Fatalf("expected one step to be parsed") } - steps[0].assertType(Given, t) steps[0].assertText("there are users:", t) steps[0].assertTableRow(t, 0, "name", "John Doe") @@ -177,7 +168,6 @@ func Test_parse_table_given_step(t *testing.T) { t.Fatalf("expected one step to be parsed") } - steps[0].assertType(Given, t) steps[0].assertText("there are users:", t) steps[0].assertTableRow(t, 0, "name", "lastname") steps[0].assertTableRow(t, 1, "John", "Doe") @@ -207,7 +197,6 @@ func Test_parse_pystring_step(t *testing.T) { t.Fatalf("expected one step to be parsed") } - steps[0].assertType(Then, t) steps[0].assertText("there should be text:", t) steps[0].assertPyString(strings.Join([]string{ indent(4, "Some text"), @@ -239,7 +228,6 @@ func Test_parse_empty_pystring_step(t *testing.T) { t.Fatalf("expected one step to be parsed") } - steps[0].assertType(When, t) steps[0].assertText("I do request with body:", t) steps[0].assertPyString("", t) @@ -285,13 +273,9 @@ func Test_parse_step_group(t *testing.T) { t.Fatalf("expected four steps to be parsed, but got: %d", len(steps)) } - steps[0].assertType(Given, t) steps[0].assertText("there are conditions", t) - steps[1].assertType(Given, t) steps[1].assertText("there are more conditions", t) - steps[2].assertType(When, t) steps[2].assertText("I do something", t) - steps[3].assertType(Then, t) steps[3].assertText("something should happen", t) p.next() // step over to eof @@ -318,13 +302,9 @@ func Test_parse_another_step_group(t *testing.T) { t.Fatalf("expected four steps to be parsed, but got: %d", len(steps)) } - steps[0].assertType(Given, t) steps[0].assertText(`an admin user "John Doe"`, t) - steps[1].assertType(Given, t) steps[1].assertText(`user "John Doe" belongs to user group "editors"`, t) - steps[2].assertType(When, t) steps[2].assertText("I do something", t) - steps[3].assertType(Then, t) steps[3].assertText("I expect the result", t) p.next() // step over to eof diff --git a/suite.go b/suite.go index 7a804be..39a8424 100644 --- a/suite.go +++ b/suite.go @@ -66,6 +66,11 @@ func (f StepHandlerFunc) HandleStep(args ...Arg) error { var errPending = fmt.Errorf("pending step") +type stepMatchHandler struct { + handler StepHandler + expr *regexp.Regexp +} + // Suite is an interface which allows various contexts // to register step definitions and event handlers type Suite interface { @@ -73,7 +78,7 @@ type Suite interface { } type suite struct { - steps map[*regexp.Regexp]StepHandler + steps []*stepMatchHandler features []*gherkin.Feature fmt Formatter } @@ -82,9 +87,7 @@ type suite struct { // interface. The instance is passed around to all // context initialization functions from *_test.go files func New() *suite { - return &suite{ - steps: make(map[*regexp.Regexp]StepHandler), - } + return &suite{} } // Step allows to register a StepHandler in Godog @@ -97,8 +100,11 @@ func New() *suite { // // If none of the StepHandlers are matched, then a pending // step error will be raised. -func (s *suite) Step(exp *regexp.Regexp, h StepHandler) { - s.steps[exp] = h +func (s *suite) Step(expr *regexp.Regexp, h StepHandler) { + s.steps = append(s.steps, &stepMatchHandler{ + handler: h, + expr: expr, + }) } // Run - runs a godog feature suite @@ -122,33 +128,33 @@ func (s *suite) Run() { } func (s *suite) runStep(step *gherkin.Step) (err error) { - defer func() { - if e := recover(); e != nil { - err = e.(error) - s.fmt.Failed(step, err) - } - }() - - var handler StepHandler + var match *stepMatchHandler var args []Arg - for r, h := range s.steps { - if m := r.FindStringSubmatch(step.Text); len(m) > 0 { - handler = h + for _, h := range s.steps { + if m := h.expr.FindStringSubmatch(step.Text); len(m) > 0 { + match = h for _, a := range m[1:] { args = append(args, Arg(a)) } break } } - if handler == nil { + if match == nil { s.fmt.Pending(step) return errPending } - if err = handler.HandleStep(args...); err != nil { - s.fmt.Failed(step, err) + defer func() { + if e := recover(); e != nil { + err = e.(error) + s.fmt.Failed(step, match, err) + } + }() + + if err = match.handler.HandleStep(args...); err != nil { + s.fmt.Failed(step, match, err) } else { - s.fmt.Passed(step) + s.fmt.Passed(step, match) } return } diff --git a/suite_test.go b/suite_test.go index fec0ada..d8f8edf 100644 --- a/suite_test.go +++ b/suite_test.go @@ -28,9 +28,7 @@ func (s *suiteFeature) numParsed(args ...Arg) (err error) { func SuiteContext(g Suite) { s := &suiteFeature{ - suite: suite{ - steps: make(map[*regexp.Regexp]StepHandler), - }, + suite: suite{}, } g.Step(