From c604d39ac5d0539d7a0030fb91efd11b17adf1ba Mon Sep 17 00:00:00 2001 From: gedi Date: Fri, 19 Jun 2015 10:30:38 +0300 Subject: [PATCH] less strict step registration --- features/events.feature | 6 +- .../{load_features.feature => load.feature} | 15 +++-- features/run.feature | 18 +++++ suite.go | 50 ++++++++++++-- suite_test.go | 66 ++++++++----------- 5 files changed, 101 insertions(+), 54 deletions(-) rename features/{load_features.feature => load.feature} (68%) create mode 100644 features/run.feature diff --git a/features/events.feature b/features/events.feature index 3e39959..c978519 100644 --- a/features/events.feature +++ b/features/events.feature @@ -7,12 +7,12 @@ Feature: suite events Given I'm listening to suite events Scenario: triggers before scenario event - Given a feature path "features/load_features.feature:6" + Given a feature path "features/load.feature:6" When I run feature suite Then there was event triggered before scenario "load features within path" Scenario: triggers appropriate events for a single scenario - Given a feature path "features/load_features.feature:6" + Given a feature path "features/load.feature:6" When I run feature suite Then these events had to be fired for a number of times: | BeforeSuite | 1 | @@ -23,7 +23,7 @@ Feature: suite events | AfterSuite | 1 | Scenario: triggers appropriate events whole feature - Given a feature path "features/load_features.feature" + Given a feature path "features/load.feature" When I run feature suite Then these events had to be fired for a number of times: | BeforeSuite | 1 | diff --git a/features/load_features.feature b/features/load.feature similarity index 68% rename from features/load_features.feature rename to features/load.feature index cbb5138..f65045e 100644 --- a/features/load_features.feature +++ b/features/load.feature @@ -6,31 +6,32 @@ Feature: load features Scenario: load features within path Given a feature path "features" When I parse features - Then I should have 2 feature files: + Then I should have 3 feature files: """ features/events.feature - features/load_features.feature + features/load.feature + features/run.feature """ Scenario: load a specific feature file - Given a feature path "features/load_features.feature" + Given a feature path "features/load.feature" When I parse features Then I should have 1 feature file: """ - features/load_features.feature + features/load.feature """ Scenario: load a feature file with a specified scenario - Given a feature path "features/load_features.feature:6" + Given a feature path "features/load.feature:6" When I parse features Then I should have 1 scenario registered Scenario: load a number of feature files - Given a feature path "features/load_features.feature" + Given a feature path "features/load.feature" And a feature path "features/events.feature" When I parse features Then I should have 2 feature files: """ - features/load_features.feature + features/load.feature features/events.feature """ diff --git a/features/run.feature b/features/run.feature new file mode 100644 index 0000000..c6ad874 --- /dev/null +++ b/features/run.feature @@ -0,0 +1,18 @@ +Feature: run features + In order to test application behavior + As a test suite + I need to be able to run features + + Scenario: should run a normal feature + Given a feature "normal.feature" file: + """ + Feature: normal feature + + Scenario: parse a scenario + Given a feature path "features/load.feature:6" + When I parse features + Then I should have 1 scenario registered + """ + When I run feature suite + Then the suite should have passed successfully + diff --git a/suite.go b/suite.go index b34f116..304cdba 100644 --- a/suite.go +++ b/suite.go @@ -11,12 +11,21 @@ import ( "github.com/DATA-DOG/godog/gherkin" ) +// Regexp is an unified type for regular expression +// it can be either a string or a *regexp.Regexp +type Regexp interface{} + +// Handler is an unified type for a StepHandler +// interface satisfaction. It may be a function +// or a step handler +type Handler interface{} + // Status represents a step or scenario status type Status int // step or scenario status constants const ( - invalid Status = iota + Invalid Status = iota Passed Failed Undefined @@ -71,7 +80,7 @@ type stepMatchHandler struct { // Suite is an interface which allows various contexts // to register step definitions and event handlers type Suite interface { - Step(expr *regexp.Regexp, h StepHandler) + Step(expr Regexp, h Handler) // suite events BeforeSuite(h BeforeSuiteHandler) BeforeScenario(h BeforeScenarioHandler) @@ -106,7 +115,10 @@ func New() *suite { // Step allows to register a StepHandler in Godog // feature suite, the handler will be applied to all -// steps matching the given regexp +// steps matching the given regexp expr +// +// It will panic if expr is not a valid regular expression +// or handler does not satisfy StepHandler interface // // Note that if there are two handlers which may match // the same step, then the only first matched handler @@ -114,10 +126,34 @@ func New() *suite { // // If none of the StepHandlers are matched, then a pending // step error will be raised. -func (s *suite) Step(expr *regexp.Regexp, h StepHandler) { +func (s *suite) Step(expr Regexp, h Handler) { + var handler StepHandler + var regex *regexp.Regexp + + switch t := expr.(type) { + case *regexp.Regexp: + regex = t + case string: + regex = regexp.MustCompile(t) + case []byte: + regex = regexp.MustCompile(string(t)) + default: + panic(fmt.Sprintf("expecting expr to be a *regexp.Regexp or a string, got type: %T", expr)) + } + + switch t := h.(type) { + case StepHandlerFunc: + handler = t + case StepHandler: + handler = t + case func(...*Arg) error: + handler = StepHandlerFunc(t) + default: + panic(fmt.Sprintf("expecting handler to satisfy StepHandler interface, got type: %T", h)) + } s.stepHandlers = append(s.stepHandlers, &stepMatchHandler{ - handler: h, - expr: expr, + handler: handler, + expr: regex, }) } @@ -304,7 +340,7 @@ func (s *suite) runFeature(f *gherkin.Feature) { s.skipSteps(scenario.Steps) case status == Undefined: s.skipSteps(scenario.Steps) - case status == Passed || status == invalid: + case status == Passed || status == Invalid: status = s.runSteps(scenario.Steps) } diff --git a/suite_test.go b/suite_test.go index 1909b13..cdfc654 100644 --- a/suite_test.go +++ b/suite_test.go @@ -2,8 +2,6 @@ package godog import ( "fmt" - "os" - "regexp" "strings" "github.com/DATA-DOG/godog/gherkin" @@ -15,9 +13,8 @@ type firedEvent struct { } type suiteFeature struct { - testedSuite *suite - events []*firedEvent - tempFeatures []string + testedSuite *suite + events []*firedEvent } func (s *suiteFeature) HandleBeforeScenario(*gherkin.Scenario) { @@ -27,18 +24,10 @@ func (s *suiteFeature) HandleBeforeScenario(*gherkin.Scenario) { SuiteContext(s.testedSuite) // reset feature paths cfg.paths = []string{} - s.tempFeatures = []string{} // reset all fired events s.events = []*firedEvent{} } -func (s *suiteFeature) HandleAfterScenario(*gherkin.Scenario) { - // remove temp files - for _, f := range s.tempFeatures { - os.Remove("/tmp/" + f) - } -} - func (s *suiteFeature) iAmListeningToSuiteEvents(args ...*Arg) error { s.testedSuite.BeforeSuite(BeforeSuiteHandlerFunc(func() { s.events = append(s.events, &firedEvent{"BeforeSuite", []interface{}{}}) @@ -65,8 +54,13 @@ func (s *suiteFeature) aFailingStep(...*Arg) error { return fmt.Errorf("intentional failure") } -func (s *suiteFeature) tempFeatureFile(args ...*Arg) error { - return nil +// parse a given feature file body as a feature +func (s *suiteFeature) aFeatureFile(args ...*Arg) error { + name := args[0].String() + body := args[1].PyString().Raw + feature, err := gherkin.Parse(strings.NewReader(body), name) + s.testedSuite.features = append(s.testedSuite.features, feature) + return err } func (s *suiteFeature) featurePath(args ...*Arg) error { @@ -79,6 +73,13 @@ func (s *suiteFeature) parseFeatures(args ...*Arg) (err error) { return } +func (s *suiteFeature) theSuitePassedSuccessfully(...*Arg) error { + if s.testedSuite.failed { + return fmt.Errorf("the feature suite has failed") + } + return nil +} + func (s *suiteFeature) iShouldHaveNumFeatureFiles(args ...*Arg) error { if len(s.testedSuite.features) != args[0].Int() { return fmt.Errorf("expected %d features to be parsed, but have %d", args[0].Int(), len(s.testedSuite.features)) @@ -177,28 +178,19 @@ func SuiteContext(g Suite) { g.BeforeScenario(s) - g.Step(regexp.MustCompile(`^a feature path "([^"]*)"$`), StepHandlerFunc(s.featurePath)) - g.Step(regexp.MustCompile(`^I parse features$`), StepHandlerFunc(s.parseFeatures)) - g.Step(regexp.MustCompile(`^I'm listening to suite events$`), StepHandlerFunc(s.iAmListeningToSuiteEvents)) - g.Step(regexp.MustCompile(`^I run feature suite$`), StepHandlerFunc(s.iRunFeatureSuite)) - g.Step(regexp.MustCompile(`^feature "([^"]*)" file:$`), StepHandlerFunc(s.tempFeatureFile)) + g.Step(`^a feature path "([^"]*)"$`, s.featurePath) + g.Step(`^I parse features$`, s.parseFeatures) + g.Step(`^I'm listening to suite events$`, s.iAmListeningToSuiteEvents) + g.Step(`^I run feature suite$`, s.iRunFeatureSuite) + g.Step(`^a feature "([^"]*)" file:$`, s.aFeatureFile) + g.Step(`^the suite should have passed successfully$`, s.theSuitePassedSuccessfully) - g.Step( - regexp.MustCompile(`^I should have ([\d]+) features? files?:$`), - StepHandlerFunc(s.iShouldHaveNumFeatureFiles)) - g.Step( - regexp.MustCompile(`^I should have ([\d]+) scenarios? registered$`), - StepHandlerFunc(s.numScenariosRegistered)) - g.Step( - regexp.MustCompile(`^there (was|were) ([\d]+) "([^"]*)" events? fired$`), - StepHandlerFunc(s.thereWereNumEventsFired)) - g.Step( - regexp.MustCompile(`^there was event triggered before scenario "([^"]*)"$`), - StepHandlerFunc(s.thereWasEventTriggeredBeforeScenario)) - g.Step( - regexp.MustCompile(`^these events had to be fired for a number of times:$`), - StepHandlerFunc(s.theseEventsHadToBeFiredForNumberOfTimes)) + g.Step(`^I should have ([\d]+) features? files?:$`, s.iShouldHaveNumFeatureFiles) + g.Step(`^I should have ([\d]+) scenarios? registered$`, s.numScenariosRegistered) + g.Step(`^there (was|were) ([\d]+) "([^"]*)" events? fired$`, s.thereWereNumEventsFired) + g.Step(`^there was event triggered before scenario "([^"]*)"$`, s.thereWasEventTriggeredBeforeScenario) + g.Step(`^these events had to be fired for a number of times:$`, s.theseEventsHadToBeFiredForNumberOfTimes) - g.Step(regexp.MustCompile(`^a failing step`), StepHandlerFunc(s.aFailingStep)) - g.Step(regexp.MustCompile(`^this step should fail`), StepHandlerFunc(s.aFailingStep)) + g.Step(`^a failing step`, s.aFailingStep) + g.Step(`^this step should fail`, s.aFailingStep) }