From a3ef3361f8b0210fd3933f9c3b7649aa1e5b7154 Mon Sep 17 00:00:00 2001 From: gedi Date: Wed, 17 Jun 2015 16:44:42 +0300 Subject: [PATCH] some necessary command line options --- config.go | 15 +++++++++++++ features/hooks.feature | 10 ++++++++- formatter.go | 40 +++++++++++++++++++++++---------- godog.go | 2 ++ suite.go | 50 ++++++++++++++++++++++++++++++++++++++---- suite_test.go | 14 ++++++++++++ 6 files changed, 115 insertions(+), 16 deletions(-) diff --git a/config.go b/config.go index 6cfc82c..806b032 100644 --- a/config.go +++ b/config.go @@ -41,6 +41,11 @@ func init() { flag.StringVar(&cfg.format, "format", "pretty", "") flag.StringVar(&cfg.format, "f", "pretty", "") + flag.BoolVar(&cfg.definitions, "definitions", false, "") + flag.BoolVar(&cfg.definitions, "d", false, "") + flag.BoolVar(&cfg.stopOnFailure, "stop-on-failure", false, "") + flag.BoolVar(&cfg.version, "version", false, "") + flag.Usage = func() { // prints an option or argument with a description, or only description opt := func(name, desc string) string { @@ -66,11 +71,17 @@ func init() { // --- OPTIONS --- fmt.Println(cl("Options:", yellow)) + // --> step definitions + fmt.Println(opt("-d, --definitions", "Print all available step definitions.")) // --> format fmt.Println(opt("-f, --format=pretty", "How to format tests output. Available formats:")) for _, f := range formatters { fmt.Println(opt("", s(4)+"- "+cl(f.name, yellow)+": "+f.description)) } + // --> stop on failure + fmt.Println(opt("--stop-on-failure", "Stop processing on first failed scenario.")) + // --> version + fmt.Println(opt("--version", "Show current "+cl("godog", yellow)+" version.")) fmt.Println("") } } @@ -78,6 +89,10 @@ func init() { type config struct { paths []string format string + + definitions bool + stopOnFailure bool + version bool } func (c *config) validate() error { diff --git a/features/hooks.feature b/features/hooks.feature index 4539f27..cdcbb00 100644 --- a/features/hooks.feature +++ b/features/hooks.feature @@ -6,4 +6,12 @@ Feature: suite hooks Background: Given I have a before scenario hook And a feature path "features/load_features.feature:6" - # When I parse and run features + And I parse features + + Scenario: hi there + When I run features + Then I should have a scenario "" recorded in the hook + + Scenario: and there + When I run features + Then I should have a scenario "" recorded in the hook diff --git a/formatter.go b/formatter.go index 8228951..132d705 100644 --- a/formatter.go +++ b/formatter.go @@ -89,10 +89,16 @@ func (f *pretty) line(tok *gherkin.Token) string { // checks whether it should not print a background step once again func (f *pretty) canPrintStep(step *gherkin.Step) bool { - if f.background != nil { - return step.Background != nil + if f.background == nil { + return true } - return true + + if step.Background == nil { + f.doneBackground = true + return true + } + + return !f.doneBackground } // Node takes a gherkin node for formatting @@ -110,9 +116,20 @@ func (f *pretty) Node(node interface{}) { fmt.Println(bcl("Feature: ", white) + t.Title) fmt.Println(t.Description) case *gherkin.Background: - f.background = t - fmt.Println("\n" + bcl("Background:", white)) + // determine comment position based on step length + f.commentPos = len(t.Token.Text) + for _, step := range t.Steps { + if len(step.Token.Text) > f.commentPos { + f.commentPos = len(step.Token.Text) + } + } + // do not repeat background + if !f.doneBackground { + f.background = t + fmt.Println("\n" + strings.Repeat(" ", t.Token.Indent) + bcl("Background:", white)) + } case *gherkin.Scenario: + // determine comment position based on step length f.commentPos = len(t.Token.Text) for _, step := range t.Steps { if len(step.Token.Text) > f.commentPos { @@ -187,9 +204,6 @@ func (f *pretty) Summary() { // prints a single matched step 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 @@ -228,7 +242,9 @@ func (f *pretty) printMatchedStep(step *gherkin.Step, match *stepMatchHandler, c // Passed is called to represent a passed step func (f *pretty) Passed(step *gherkin.Step, match *stepMatchHandler) { - f.printMatchedStep(step, match, green) + if f.canPrintStep(step) { + f.printMatchedStep(step, match, green) + } f.passed = append(f.passed, &passed{step}) } @@ -250,7 +266,9 @@ func (f *pretty) Undefined(step *gherkin.Step) { // Failed is called to represent a failed step func (f *pretty) Failed(step *gherkin.Step, match *stepMatchHandler, err error) { - f.printMatchedStep(step, match, red) - fmt.Println(strings.Repeat(" ", step.Token.Indent) + bcl(err, red)) + if f.canPrintStep(step) { + f.printMatchedStep(step, match, red) + fmt.Println(strings.Repeat(" ", step.Token.Indent) + bcl(err, red)) + } f.failed = append(f.failed, &failed{step, err}) } diff --git a/godog.go b/godog.go index 6b6303b..91809df 100644 --- a/godog.go +++ b/godog.go @@ -33,3 +33,5 @@ As you’ll see, Godog is easy to learn, quick to use, and will put the fun back Godog was inspired by Behat and the above description is taken from it's documentation. */ package godog + +const Version = "v0.1.0-alpha" diff --git a/suite.go b/suite.go index cbbf230..48f5167 100644 --- a/suite.go +++ b/suite.go @@ -3,7 +3,9 @@ package godog import ( "flag" "fmt" + "reflect" "regexp" + "runtime" "github.com/DATA-DOG/godog/gherkin" ) @@ -63,6 +65,8 @@ type suite struct { stepHandlers []*stepMatchHandler features []*gherkin.Feature fmt Formatter + + stop bool } // New initializes a suite which supports the Suite @@ -99,14 +103,29 @@ func (s *suite) Run() { if !flag.Parsed() { flag.Parse() } - fatal(cfg.validate()) + // check if we need to just show something first + switch { + case cfg.version: + fmt.Println(cl("Godog", green) + " version is " + cl(Version, yellow)) + return + case cfg.definitions: + s.printStepDefinitions() + return + } + + // run a feature suite + fatal(cfg.validate()) s.fmt = cfg.formatter() s.features, err = cfg.features() fatal(err) for _, f := range s.features { s.runFeature(f) + if s.stop { + // stop on first failure + break + } } s.fmt.Summary() } @@ -171,14 +190,16 @@ func (s *suite) skipSteps(steps []*gherkin.Step) { func (s *suite) runFeature(f *gherkin.Feature) { s.fmt.Node(f) - var failed bool for _, scenario := range f.Scenarios { + var failed bool + // run before scenario handlers for _, h := range s.beforeScenarioHandlers { h.BeforeScenario(scenario) } + // background - if f.Background != nil && !failed { + if f.Background != nil { s.fmt.Node(f.Background) failed = s.runSteps(f.Background.Steps) } @@ -188,7 +209,28 @@ func (s *suite) runFeature(f *gherkin.Feature) { if failed { s.skipSteps(scenario.Steps) } else { - s.runSteps(scenario.Steps) + failed = s.runSteps(scenario.Steps) + } + + if failed && cfg.stopOnFailure { + s.stop = true + return } } } + +func (st *suite) printStepDefinitions() { + var longest int + for _, def := range st.stepHandlers { + if longest < len(def.expr.String()) { + longest = len(def.expr.String()) + } + } + for _, def := range st.stepHandlers { + location := runtime.FuncForPC(reflect.ValueOf(def.handler).Pointer()).Name() + fmt.Println(cl(def.expr.String(), yellow)+s(longest-len(def.expr.String())), cl("# "+location, black)) + } + if len(st.stepHandlers) == 0 { + fmt.Println("there were no contexts registered, could not find any step definition..") + } +} diff --git a/suite_test.go b/suite_test.go index f1743e7..113c797 100644 --- a/suite_test.go +++ b/suite_test.go @@ -9,11 +9,22 @@ import ( type suiteFeature struct { suite + // for hook tests + befScenarioHook *gherkin.Scenario } func (s *suiteFeature) BeforeScenario(scenario *gherkin.Scenario) { // reset feature paths cfg.paths = []string{} + // reset hook test references + s.befScenarioHook = nil +} + +func (s *suiteFeature) iHaveBeforeScenarioHook(args ...*Arg) error { + s.suite.BeforeScenario(BeforeScenarioHandlerFunc(func(scenario *gherkin.Scenario) { + s.befScenarioHook = scenario + })) + return nil } func (s *suiteFeature) featurePath(args ...*Arg) error { @@ -63,4 +74,7 @@ func SuiteContext(g Suite) { g.Step( regexp.MustCompile(`^I should have ([\d]+) scenarios? registered$`), StepHandlerFunc(s.numScenariosRegistered)) + g.Step( + regexp.MustCompile(`^I have a before scenario hook$`), + StepHandlerFunc(s.iHaveBeforeScenarioHook)) }