handles undefined step templates for nested steps

Этот коммит содержится в:
gedi 2017-04-29 22:44:40 +03:00
родитель 5bfd57218a
коммит e4ed3b9a9c
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 56604CDCCC201556
10 изменённых файлов: 207 добавлений и 78 удалений

74
fmt.go
Просмотреть файл

@ -105,8 +105,8 @@ type Formatter interface {
Defined(*gherkin.Step, *StepDef) Defined(*gherkin.Step, *StepDef)
Failed(*gherkin.Step, *StepDef, error) Failed(*gherkin.Step, *StepDef, error)
Passed(*gherkin.Step, *StepDef) Passed(*gherkin.Step, *StepDef)
Skipped(*gherkin.Step) Skipped(*gherkin.Step, *StepDef)
Undefined(*gherkin.Step) Undefined(*gherkin.Step, *StepDef)
Pending(*gherkin.Step, *StepDef) Pending(*gherkin.Step, *StepDef)
Summary() Summary()
} }
@ -210,21 +210,23 @@ func (f *basefmt) Passed(step *gherkin.Step, match *StepDef) {
f.passed = append(f.passed, s) f.passed = append(f.passed, s)
} }
func (f *basefmt) Skipped(step *gherkin.Step) { func (f *basefmt) Skipped(step *gherkin.Step, match *StepDef) {
s := &stepResult{ s := &stepResult{
owner: f.owner, owner: f.owner,
feature: f.features[len(f.features)-1], feature: f.features[len(f.features)-1],
step: step, step: step,
def: match,
typ: skipped, typ: skipped,
} }
f.skipped = append(f.skipped, s) f.skipped = append(f.skipped, s)
} }
func (f *basefmt) Undefined(step *gherkin.Step) { func (f *basefmt) Undefined(step *gherkin.Step, match *StepDef) {
s := &stepResult{ s := &stepResult{
owner: f.owner, owner: f.owner,
feature: f.features[len(f.features)-1], feature: f.features[len(f.features)-1],
step: step, step: step,
def: match,
typ: undefined, typ: undefined,
} }
f.undefined = append(f.undefined, s) f.undefined = append(f.undefined, s)
@ -394,38 +396,46 @@ func (f *basefmt) snippets() string {
var snips []*undefinedSnippet var snips []*undefinedSnippet
// build snippets // build snippets
for _, u := range f.undefined { for _, u := range f.undefined {
expr := snippetExprCleanup.ReplaceAllString(u.step.Text, "\\$1") steps := []string{u.step.Text}
expr = snippetNumbers.ReplaceAllString(expr, "(\\d+)") arg := u.step.Argument
expr = snippetExprQuoted.ReplaceAllString(expr, "$1\"([^\"]*)\"$2") if u.def != nil {
expr = "^" + strings.TrimSpace(expr) + "$" steps = u.def.undefined
arg = nil
}
for _, step := range steps {
expr := snippetExprCleanup.ReplaceAllString(step, "\\$1")
expr = snippetNumbers.ReplaceAllString(expr, "(\\d+)")
expr = snippetExprQuoted.ReplaceAllString(expr, "$1\"([^\"]*)\"$2")
expr = "^" + strings.TrimSpace(expr) + "$"
name := snippetNumbers.ReplaceAllString(u.step.Text, " ") name := snippetNumbers.ReplaceAllString(step, " ")
name = snippetExprQuoted.ReplaceAllString(name, " ") name = snippetExprQuoted.ReplaceAllString(name, " ")
name = snippetMethodName.ReplaceAllString(name, "") name = snippetMethodName.ReplaceAllString(name, "")
var words []string var words []string
for i, w := range strings.Split(name, " ") { for i, w := range strings.Split(name, " ") {
if i != 0 { if i != 0 {
w = strings.Title(w) w = strings.Title(w)
} else { } else {
w = string(unicode.ToLower(rune(w[0]))) + w[1:] w = string(unicode.ToLower(rune(w[0]))) + w[1:]
}
words = append(words, w)
}
name = strings.Join(words, "")
if len(name) == 0 {
index++
name = fmt.Sprintf("stepDefinition%d", index)
} }
words = append(words, w)
}
name = strings.Join(words, "")
if len(name) == 0 {
index++
name = fmt.Sprintf("stepDefinition%d", index)
}
var found bool var found bool
for _, snip := range snips { for _, snip := range snips {
if snip.Expr == expr { if snip.Expr == expr {
found = true found = true
break break
}
}
if !found {
snips = append(snips, &undefinedSnippet{Method: name, Expr: expr, argument: arg})
} }
}
if !found {
snips = append(snips, &undefinedSnippet{Method: name, Expr: expr, argument: u.step.Argument})
} }
} }

Просмотреть файл

@ -297,16 +297,16 @@ func (f *cukefmt) Passed(step *gherkin.Step, match *StepDef) {
f.step(f.passed[len(f.passed)-1]) f.step(f.passed[len(f.passed)-1])
} }
func (f *cukefmt) Skipped(step *gherkin.Step) { func (f *cukefmt) Skipped(step *gherkin.Step, match *StepDef) {
f.basefmt.Skipped(step) f.basefmt.Skipped(step, match)
f.step(f.skipped[len(f.skipped)-1]) f.step(f.skipped[len(f.skipped)-1])
// no duration reported for skipped. // no duration reported for skipped.
f.curStep.Result.Duration = nil f.curStep.Result.Duration = nil
} }
func (f *cukefmt) Undefined(step *gherkin.Step) { func (f *cukefmt) Undefined(step *gherkin.Step, match *StepDef) {
f.basefmt.Undefined(step) f.basefmt.Undefined(step, match)
f.stat = undefined f.stat = undefined
f.step(f.undefined[len(f.undefined)-1]) f.step(f.undefined[len(f.undefined)-1])

Просмотреть файл

@ -245,13 +245,13 @@ func (f *events) Passed(step *gherkin.Step, match *StepDef) {
f.step(f.passed[len(f.passed)-1]) f.step(f.passed[len(f.passed)-1])
} }
func (f *events) Skipped(step *gherkin.Step) { func (f *events) Skipped(step *gherkin.Step, match *StepDef) {
f.basefmt.Skipped(step) f.basefmt.Skipped(step, match)
f.step(f.skipped[len(f.skipped)-1]) f.step(f.skipped[len(f.skipped)-1])
} }
func (f *events) Undefined(step *gherkin.Step) { func (f *events) Undefined(step *gherkin.Step, match *StepDef) {
f.basefmt.Undefined(step) f.basefmt.Undefined(step, match)
f.stat = undefined f.stat = undefined
f.step(f.undefined[len(f.undefined)-1]) f.step(f.undefined[len(f.undefined)-1])
} }

Просмотреть файл

@ -101,7 +101,7 @@ func (j *junitFormatter) Passed(step *gherkin.Step, match *StepDef) {
tcase.Status = "passed" tcase.Status = "passed"
} }
func (j *junitFormatter) Skipped(step *gherkin.Step) { func (j *junitFormatter) Skipped(step *gherkin.Step, match *StepDef) {
suite := j.current() suite := j.current()
tcase := suite.current() tcase := suite.current()
@ -112,7 +112,7 @@ func (j *junitFormatter) Skipped(step *gherkin.Step) {
}) })
} }
func (j *junitFormatter) Undefined(step *gherkin.Step) { func (j *junitFormatter) Undefined(step *gherkin.Step, match *StepDef) {
suite := j.current() suite := j.current()
tcase := suite.current() tcase := suite.current()
if tcase.Status != "undefined" { if tcase.Status != "undefined" {

Просмотреть файл

@ -334,13 +334,13 @@ func (f *pretty) Passed(step *gherkin.Step, match *StepDef) {
f.printStepKind(f.passed[len(f.passed)-1]) f.printStepKind(f.passed[len(f.passed)-1])
} }
func (f *pretty) Skipped(step *gherkin.Step) { func (f *pretty) Skipped(step *gherkin.Step, match *StepDef) {
f.basefmt.Skipped(step) f.basefmt.Skipped(step, match)
f.printStepKind(f.skipped[len(f.skipped)-1]) f.printStepKind(f.skipped[len(f.skipped)-1])
} }
func (f *pretty) Undefined(step *gherkin.Step) { func (f *pretty) Undefined(step *gherkin.Step, match *StepDef) {
f.basefmt.Undefined(step) f.basefmt.Undefined(step, match)
f.printStepKind(f.undefined[len(f.undefined)-1]) f.printStepKind(f.undefined[len(f.undefined)-1])
} }

Просмотреть файл

@ -91,17 +91,17 @@ func (f *progress) Passed(step *gherkin.Step, match *StepDef) {
f.step(f.passed[len(f.passed)-1]) f.step(f.passed[len(f.passed)-1])
} }
func (f *progress) Skipped(step *gherkin.Step) { func (f *progress) Skipped(step *gherkin.Step, match *StepDef) {
f.Lock() f.Lock()
defer f.Unlock() defer f.Unlock()
f.basefmt.Skipped(step) f.basefmt.Skipped(step, match)
f.step(f.skipped[len(f.skipped)-1]) f.step(f.skipped[len(f.skipped)-1])
} }
func (f *progress) Undefined(step *gherkin.Step) { func (f *progress) Undefined(step *gherkin.Step, match *StepDef) {
f.Lock() f.Lock()
defer f.Unlock() defer f.Unlock()
f.basefmt.Undefined(step) f.basefmt.Undefined(step, match)
f.step(f.undefined[len(f.undefined)-1]) f.step(f.undefined[len(f.undefined)-1])
} }

Просмотреть файл

@ -92,8 +92,8 @@ var basicGherkinFeature = `
Feature: basic Feature: basic
Scenario: passing scenario Scenario: passing scenario
When step1 When one
Then step2 Then two
` `
func TestProgressFormatterWhenStepPanics(t *testing.T) { func TestProgressFormatterWhenStepPanics(t *testing.T) {
@ -108,8 +108,8 @@ func TestProgressFormatterWhenStepPanics(t *testing.T) {
fmt: progressFunc("progress", w), fmt: progressFunc("progress", w),
features: []*feature{&feature{Feature: feat}}, features: []*feature{&feature{Feature: feat}},
initializer: func(s *Suite) { initializer: func(s *Suite) {
s.Step(`^step1$`, func() error { return nil }) s.Step(`^one$`, func() error { return nil })
s.Step(`^step2$`, func() error { panic("omg") }) s.Step(`^two$`, func() error { panic("omg") })
}, },
} }
@ -137,9 +137,9 @@ func TestProgressFormatterWithPassingMultisteps(t *testing.T) {
initializer: func(s *Suite) { initializer: func(s *Suite) {
s.Step(`^sub1$`, func() error { return nil }) s.Step(`^sub1$`, func() error { return nil })
s.Step(`^sub-sub$`, func() error { return nil }) s.Step(`^sub-sub$`, func() error { return nil })
s.Step(`^sub2$`, func() Steps { return Steps{"sub-sub", "sub1", "step1"} }) s.Step(`^sub2$`, func() Steps { return Steps{"sub-sub", "sub1", "one"} })
s.Step(`^step1$`, func() error { return nil }) s.Step(`^one$`, func() error { return nil })
s.Step(`^step2$`, func() Steps { return Steps{"sub1", "sub2"} }) s.Step(`^two$`, func() Steps { return Steps{"sub1", "sub2"} })
}, },
} }
@ -162,9 +162,9 @@ func TestProgressFormatterWithFailingMultisteps(t *testing.T) {
initializer: func(s *Suite) { initializer: func(s *Suite) {
s.Step(`^sub1$`, func() error { return nil }) s.Step(`^sub1$`, func() error { return nil })
s.Step(`^sub-sub$`, func() error { return fmt.Errorf("errored") }) s.Step(`^sub-sub$`, func() error { return fmt.Errorf("errored") })
s.Step(`^sub2$`, func() Steps { return Steps{"sub-sub", "sub1", "step1"} }) s.Step(`^sub2$`, func() Steps { return Steps{"sub-sub", "sub1", "one"} })
s.Step(`^step1$`, func() error { return nil }) s.Step(`^one$`, func() error { return nil })
s.Step(`^step2$`, func() Steps { return Steps{"sub1", "sub2"} }) s.Step(`^two$`, func() Steps { return Steps{"sub1", "sub2"} })
}, },
} }
@ -187,9 +187,9 @@ func TestProgressFormatterWithPanicInMultistep(t *testing.T) {
initializer: func(s *Suite) { initializer: func(s *Suite) {
s.Step(`^sub1$`, func() error { return nil }) s.Step(`^sub1$`, func() error { return nil })
s.Step(`^sub-sub$`, func() error { return nil }) s.Step(`^sub-sub$`, func() error { return nil })
s.Step(`^sub2$`, func() []string { return []string{"sub-sub", "sub1", "step1"} }) s.Step(`^sub2$`, func() []string { return []string{"sub-sub", "sub1", "one"} })
s.Step(`^step1$`, func() error { return nil }) s.Step(`^one$`, func() error { return nil })
s.Step(`^step2$`, func() []string { return []string{"sub1", "sub2"} }) s.Step(`^two$`, func() []string { return []string{"sub1", "sub2"} })
}, },
} }
@ -197,3 +197,67 @@ func TestProgressFormatterWithPanicInMultistep(t *testing.T) {
t.Fatal("the suite should have failed") t.Fatal("the suite should have failed")
} }
} }
func TestProgressFormatterMultistepTemplates(t *testing.T) {
feat, err := gherkin.ParseFeature(strings.NewReader(basicGherkinFeature))
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
var buf bytes.Buffer
w := colors.Uncolored(&buf)
r := runner{
fmt: progressFunc("progress", w),
features: []*feature{&feature{Feature: feat}},
initializer: func(s *Suite) {
s.Step(`^sub-sub$`, func() error { return nil })
s.Step(`^substep$`, func() Steps { return Steps{"sub-sub", `unavailable "John" cost 5`, "one", "three"} })
s.Step(`^one$`, func() error { return nil })
s.Step(`^(t)wo$`, func(s string) Steps { return Steps{"undef", "substep"} })
},
}
if r.run() {
t.Fatal("the suite should have passed")
}
expected := `
.U 2
1 scenarios (1 undefined)
2 steps (1 passed, 1 undefined)
%s
Randomized with seed: %s
You can implement step definitions for undefined steps with these snippets:
func undef() error {
return godog.ErrPending
}
func unavailableCost(arg1 string, arg2 int) error {
return godog.ErrPending
}
func three() error {
return godog.ErrPending
}
func FeatureContext(s *godog.Suite) {
s.Step(` + "`^undef$`" + `, undef)
s.Step(` + "`^unavailable \"([^\"]*)\" cost (\\d+)$`" + `, unavailableCost)
s.Step(` + "`^three$`" + `, three)
}
`
var zeroDuration time.Duration
expected = fmt.Sprintf(expected, zeroDuration.String(), os.Getenv("GODOG_SEED"))
expected = trimAllLines(expected)
actual := trimAllLines(buf.String())
if actual != expected {
t.Fatalf("expected output does not match: %s", actual)
}
}

Просмотреть файл

@ -2,10 +2,13 @@ package godog
import ( import (
"bytes" "bytes"
"fmt"
"io/ioutil"
"strings" "strings"
"testing" "testing"
"github.com/DATA-DOG/godog/colors" "github.com/DATA-DOG/godog/colors"
"github.com/DATA-DOG/godog/gherkin"
) )
func okStep() error { func okStep() error {
@ -50,3 +53,43 @@ func TestPrintsNoStepDefinitionsIfNoneFound(t *testing.T) {
t.Fatalf("expected output does not match to: %s", out) t.Fatalf("expected output does not match to: %s", out)
} }
} }
func TestShouldNotFailWhenHasPendingSteps(t *testing.T) {
feat, err := gherkin.ParseFeature(strings.NewReader(basicGherkinFeature))
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
r := runner{
fmt: progressFunc("progress", ioutil.Discard),
features: []*feature{&feature{Feature: feat}},
initializer: func(s *Suite) {
s.Step(`^one$`, func() error { return nil })
s.Step(`^two$`, func() error { return ErrPending })
},
}
if r.run() {
t.Fatal("the suite should have passed")
}
}
func TestShouldFailOnError(t *testing.T) {
feat, err := gherkin.ParseFeature(strings.NewReader(basicGherkinFeature))
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
r := runner{
fmt: progressFunc("progress", ioutil.Discard),
features: []*feature{&feature{Feature: feat}},
initializer: func(s *Suite) {
s.Step(`^one$`, func() error { return nil })
s.Step(`^two$`, func() error { return fmt.Errorf("error") })
},
}
if !r.run() {
t.Fatal("the suite should have failed")
}
}

Просмотреть файл

@ -43,7 +43,10 @@ type StepDef struct {
hv reflect.Value hv reflect.Value
Expr *regexp.Regexp Expr *regexp.Regexp
Handler interface{} Handler interface{}
nested bool
// multistep related
nested bool
undefined []string
} }
func (sd *StepDef) definitionID() string { func (sd *StepDef) definitionID() string {

Просмотреть файл

@ -113,10 +113,10 @@ func (s *Suite) Step(expr interface{}, stepFunc interface{}) {
panic(fmt.Sprintf("expected handler to return an error, but got: %s", typ.Kind())) panic(fmt.Sprintf("expected handler to return an error, but got: %s", typ.Kind()))
} }
case reflect.Slice: case reflect.Slice:
def.nested = true
if typ.Elem().Kind() != reflect.String { if typ.Elem().Kind() != reflect.String {
panic(fmt.Sprintf("expected handler to return []string for multistep, but got: []%s", typ.Kind())) panic(fmt.Sprintf("expected handler to return []string for multistep, but got: []%s", typ.Kind()))
} }
def.nested = true
default: default:
panic(fmt.Sprintf("expected handler to return an error or []string, but got: %s", typ.Kind())) panic(fmt.Sprintf("expected handler to return an error or []string, but got: %s", typ.Kind()))
} }
@ -243,14 +243,23 @@ func (s *Suite) runStep(step *gherkin.Step, prevStepErr error) (err error) {
} }
}() }()
// @TODO custom undefined err here to pass step text for snippet if undef := s.maybeUndefined(step.Text); len(undef) > 0 {
if s.maybeUndefined(match) { if match != nil {
s.fmt.Undefined(step) match = &StepDef{
args: match.args,
hv: match.hv,
Expr: match.Expr,
Handler: match.Handler,
nested: match.nested,
undefined: undef,
}
}
s.fmt.Undefined(step, match)
return ErrUndefined return ErrUndefined
} }
if prevStepErr != nil { if prevStepErr != nil {
s.fmt.Skipped(step) s.fmt.Skipped(step, match)
return nil return nil
} }
@ -263,21 +272,21 @@ func (s *Suite) runStep(step *gherkin.Step, prevStepErr error) (err error) {
return return
} }
func (s *Suite) maybeUndefined(step *StepDef) bool { func (s *Suite) maybeUndefined(text string) (undefined []string) {
step := s.matchStepText(text)
if nil == step { if nil == step {
return true undefined = append(undefined, text)
return
} }
if !step.nested { if !step.nested {
return false return
} }
for _, text := range step.run().(Steps) { for _, next := range step.run().(Steps) {
if s.maybeUndefined(s.matchStepText(text)) { undefined = append(undefined, s.maybeUndefined(next)...)
return true
}
} }
return false return
} }
func (s *Suite) maybeSubSteps(result interface{}) error { func (s *Suite) maybeSubSteps(result interface{}) error {
@ -345,7 +354,7 @@ func (s *Suite) runSteps(steps []*gherkin.Step, prevErr error) (err error) {
func (s *Suite) skipSteps(steps []*gherkin.Step) { func (s *Suite) skipSteps(steps []*gherkin.Step) {
for _, step := range steps { for _, step := range steps {
s.fmt.Skipped(step) s.fmt.Skipped(step, s.matchStep(step))
} }
} }