support scenario outline with example table
* 042235c expose StepDef which is needed to formatters * e9474dc fix outline scenario formatting * f02c6ce fix comment position calculation for outlines * 5d7f128 remove step status type, use standard error
Этот коммит содержится в:
родитель
2e696381c9
коммит
df26aa1c1c
12 изменённых файлов: 481 добавлений и 291 удалений
16
events.go
16
events.go
|
@ -56,34 +56,34 @@ func (f BeforeStepHandlerFunc) HandleBeforeStep(step *gherkin.Step) {
|
|||
// in Suite to be executed after every step
|
||||
// which will be run
|
||||
type AfterStepHandler interface {
|
||||
HandleAfterStep(step *gherkin.Step, status Status)
|
||||
HandleAfterStep(step *gherkin.Step, err error)
|
||||
}
|
||||
|
||||
// AfterStepHandlerFunc is a function implementing
|
||||
// AfterStepHandler interface
|
||||
type AfterStepHandlerFunc func(step *gherkin.Step, status Status)
|
||||
type AfterStepHandlerFunc func(step *gherkin.Step, err error)
|
||||
|
||||
// HandleAfterStep is called with a *gherkin.Step argument
|
||||
// for after every step which is run by suite
|
||||
func (f AfterStepHandlerFunc) HandleAfterStep(step *gherkin.Step, status Status) {
|
||||
f(step, status)
|
||||
func (f AfterStepHandlerFunc) HandleAfterStep(step *gherkin.Step, err error) {
|
||||
f(step, err)
|
||||
}
|
||||
|
||||
// AfterScenarioHandler can be registered
|
||||
// in Suite to be executed after every scenario
|
||||
// which will be run
|
||||
type AfterScenarioHandler interface {
|
||||
HandleAfterScenario(scenario *gherkin.Scenario, status Status)
|
||||
HandleAfterScenario(scenario *gherkin.Scenario, err error)
|
||||
}
|
||||
|
||||
// AfterScenarioHandlerFunc is a function implementing
|
||||
// AfterScenarioHandler interface
|
||||
type AfterScenarioHandlerFunc func(scenario *gherkin.Scenario, status Status)
|
||||
type AfterScenarioHandlerFunc func(scenario *gherkin.Scenario, err error)
|
||||
|
||||
// HandleAfterScenario is called with a *gherkin.Scenario argument
|
||||
// for after every scenario which is run by suite
|
||||
func (f AfterScenarioHandlerFunc) HandleAfterScenario(scenario *gherkin.Scenario, status Status) {
|
||||
f(scenario, status)
|
||||
func (f AfterScenarioHandlerFunc) HandleAfterScenario(scenario *gherkin.Scenario, err error) {
|
||||
f(scenario, err)
|
||||
}
|
||||
|
||||
// AfterSuiteHandler can be registered
|
||||
|
|
|
@ -27,8 +27,8 @@ Feature: suite events
|
|||
When I run feature suite
|
||||
Then these events had to be fired for a number of times:
|
||||
| BeforeSuite | 1 |
|
||||
| BeforeScenario | 4 |
|
||||
| BeforeStep | 13 |
|
||||
| AfterStep | 13 |
|
||||
| AfterScenario | 4 |
|
||||
| BeforeScenario | 6 |
|
||||
| BeforeStep | 19 |
|
||||
| AfterStep | 19 |
|
||||
| AfterScenario | 6 |
|
||||
| AfterSuite | 1 |
|
||||
|
|
|
@ -21,10 +21,16 @@ Feature: load features
|
|||
features/load.feature
|
||||
"""
|
||||
|
||||
Scenario: load a feature file with a specified scenario
|
||||
Given a feature path "features/load.feature:6"
|
||||
Scenario Outline: loaded feature should have a number of scenarios
|
||||
Given a feature path "<feature>"
|
||||
When I parse features
|
||||
Then I should have 1 scenario registered
|
||||
Then I should have <number> scenario registered
|
||||
|
||||
Examples:
|
||||
| feature | number |
|
||||
| features/load.feature:3 | 0 |
|
||||
| features/load.feature:6 | 1 |
|
||||
| features/load.feature | 4 |
|
||||
|
||||
Scenario: load a number of feature files
|
||||
Given a feature path "features/load.feature"
|
||||
|
|
14
fmt.go
14
fmt.go
|
@ -10,8 +10,8 @@ import (
|
|||
// output summary presentation
|
||||
type Formatter interface {
|
||||
Node(interface{})
|
||||
Failed(*gherkin.Step, *stepMatchHandler, error)
|
||||
Passed(*gherkin.Step, *stepMatchHandler)
|
||||
Failed(*gherkin.Step, *StepDef, error)
|
||||
Passed(*gherkin.Step, *StepDef)
|
||||
Skipped(*gherkin.Step)
|
||||
Undefined(*gherkin.Step)
|
||||
Summary()
|
||||
|
@ -20,9 +20,9 @@ type Formatter interface {
|
|||
// failed represents a failed step data structure
|
||||
// with all necessary references
|
||||
type failed struct {
|
||||
step *gherkin.Step
|
||||
handler *stepMatchHandler
|
||||
err error
|
||||
step *gherkin.Step
|
||||
def *StepDef
|
||||
err error
|
||||
}
|
||||
|
||||
func (f failed) line() string {
|
||||
|
@ -41,8 +41,8 @@ func (f failed) line() string {
|
|||
// passed represents a successful step data structure
|
||||
// with all necessary references
|
||||
type passed struct {
|
||||
step *gherkin.Step
|
||||
handler *stepMatchHandler
|
||||
step *gherkin.Step
|
||||
def *StepDef
|
||||
}
|
||||
|
||||
// skipped represents a skipped step data structure
|
||||
|
|
320
fmt_pretty.go
320
fmt_pretty.go
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -17,6 +18,8 @@ func init() {
|
|||
})
|
||||
}
|
||||
|
||||
var outlinePlaceholderRegexp *regexp.Regexp = regexp.MustCompile("<[^>]+>")
|
||||
|
||||
// a built in default pretty formatter
|
||||
type pretty struct {
|
||||
feature *gherkin.Feature
|
||||
|
@ -25,6 +28,11 @@ type pretty struct {
|
|||
background *gherkin.Background
|
||||
scenario *gherkin.Scenario
|
||||
|
||||
// outline
|
||||
outlineExamples int
|
||||
outlineNumSteps int
|
||||
outlineSteps []interface{}
|
||||
|
||||
// summary
|
||||
started time.Time
|
||||
features []*gherkin.Feature
|
||||
|
@ -51,33 +59,31 @@ func (f *pretty) Node(node interface{}) {
|
|||
f.scenario = nil
|
||||
f.background = nil
|
||||
f.features = append(f.features, t)
|
||||
fmt.Println(bcl("Feature: ", white) + t.Title)
|
||||
fmt.Println(bcl(t.Token.Keyword+": ", white) + t.Title)
|
||||
fmt.Println(t.Description)
|
||||
case *gherkin.Background:
|
||||
// do not repeat background for the same feature
|
||||
if f.background == nil && f.scenario == nil {
|
||||
f.background = t
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
f.commentPos = longestStep(t.Steps, t.Token.Length())
|
||||
// print background node
|
||||
fmt.Println("\n" + s(t.Token.Indent) + bcl("Background:", white))
|
||||
fmt.Println("\n" + s(t.Token.Indent) + bcl(t.Token.Keyword+":", white))
|
||||
}
|
||||
case *gherkin.Scenario:
|
||||
f.scenario = t
|
||||
// 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)
|
||||
f.commentPos = longestStep(t.Steps, t.Token.Length())
|
||||
if t.Outline != nil {
|
||||
f.outlineSteps = []interface{}{} // reset steps list
|
||||
f.commentPos = longestStep(t.Outline.Steps, t.Token.Length())
|
||||
if f.outlineExamples == 0 {
|
||||
f.outlineNumSteps = len(t.Outline.Steps)
|
||||
f.outlineExamples = len(t.Outline.Examples.Rows) - 1
|
||||
} else {
|
||||
return // already printed an outline
|
||||
}
|
||||
}
|
||||
text := s(t.Token.Indent) + bcl("Scenario: ", white) + t.Title
|
||||
text += s(f.commentPos-len(t.Token.Text)+1) + f.line(t.Token)
|
||||
text := s(t.Token.Indent) + bcl(t.Token.Keyword+": ", white) + t.Title
|
||||
text += s(f.commentPos-t.Token.Length()+1) + f.line(t.Token)
|
||||
fmt.Println("\n" + text)
|
||||
}
|
||||
}
|
||||
|
@ -93,8 +99,22 @@ func (f *pretty) Summary() {
|
|||
}
|
||||
if len(failedScenarios) > 0 {
|
||||
fmt.Println("\n--- " + cl("Failed scenarios:", red) + "\n")
|
||||
var unique []string
|
||||
for _, fail := range failedScenarios {
|
||||
fmt.Println(" " + cl(fail.line(), red))
|
||||
var found bool
|
||||
for _, in := range unique {
|
||||
if in == fail.line() {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
unique = append(unique, fail.line())
|
||||
}
|
||||
}
|
||||
|
||||
for _, fail := range unique {
|
||||
fmt.Println(" " + cl(fail, red))
|
||||
}
|
||||
}
|
||||
var total, passed int
|
||||
|
@ -142,21 +162,129 @@ func (f *pretty) Summary() {
|
|||
fmt.Println(elapsed)
|
||||
}
|
||||
|
||||
func (f *pretty) printStep(stepAction interface{}) {
|
||||
var c color
|
||||
var step *gherkin.Step
|
||||
var h *stepMatchHandler
|
||||
var err error
|
||||
var suffix, prefix string
|
||||
func (f *pretty) printOutlineExample() {
|
||||
var failed error
|
||||
clr := green
|
||||
tbl := f.scenario.Outline.Examples
|
||||
firstExample := f.outlineExamples == len(tbl.Rows)-1
|
||||
|
||||
for i, act := range f.outlineSteps {
|
||||
var c color
|
||||
var def *StepDef
|
||||
var err error
|
||||
|
||||
_, def, c, err = f.stepDetails(act)
|
||||
// determine example row status
|
||||
switch {
|
||||
case err != nil:
|
||||
failed = err
|
||||
clr = red
|
||||
case c == yellow:
|
||||
clr = yellow
|
||||
case c == cyan && clr == green:
|
||||
clr = cyan
|
||||
}
|
||||
if firstExample {
|
||||
// in first example, we need to print steps
|
||||
var text string
|
||||
ostep := f.scenario.Outline.Steps[i]
|
||||
if def != nil {
|
||||
if m := outlinePlaceholderRegexp.FindAllStringIndex(ostep.Text, -1); len(m) > 0 {
|
||||
var pos int
|
||||
for i := 0; i < len(m); i++ {
|
||||
pair := m[i]
|
||||
text += cl(ostep.Text[pos:pair[0]], cyan)
|
||||
text += bcl(ostep.Text[pair[0]:pair[1]], cyan)
|
||||
pos = pair[1]
|
||||
}
|
||||
text += cl(ostep.Text[pos:len(ostep.Text)], cyan)
|
||||
} else {
|
||||
text = cl(ostep.Text, cyan)
|
||||
}
|
||||
// use reflect to get step handler function name
|
||||
name := runtime.FuncForPC(reflect.ValueOf(def.Handler).Pointer()).Name()
|
||||
text += s(f.commentPos-ostep.Token.Length()+1) + cl(fmt.Sprintf("# %s", name), black)
|
||||
} else {
|
||||
text = cl(ostep.Text, cyan)
|
||||
}
|
||||
// print the step outline
|
||||
fmt.Println(s(ostep.Token.Indent) + cl(ostep.Token.Keyword, cyan) + " " + text)
|
||||
}
|
||||
}
|
||||
|
||||
cols := make([]string, len(tbl.Rows[0]))
|
||||
max := longest(tbl)
|
||||
// an example table header
|
||||
if firstExample {
|
||||
out := f.scenario.Outline
|
||||
fmt.Println("")
|
||||
fmt.Println(s(out.Token.Indent) + bcl(out.Token.Keyword+":", white))
|
||||
row := tbl.Rows[0]
|
||||
|
||||
for i, col := range row {
|
||||
cols[i] = cl(col, cyan) + s(max[i]-len(col))
|
||||
}
|
||||
fmt.Println(s(tbl.Token.Indent) + "| " + strings.Join(cols, " | ") + " |")
|
||||
}
|
||||
|
||||
// an example table row
|
||||
row := tbl.Rows[len(tbl.Rows)-f.outlineExamples]
|
||||
for i, col := range row {
|
||||
cols[i] = cl(col, clr) + s(max[i]-len(col))
|
||||
}
|
||||
fmt.Println(s(tbl.Token.Indent) + "| " + strings.Join(cols, " | ") + " |")
|
||||
|
||||
// if there is an error
|
||||
if failed != nil {
|
||||
fmt.Println(s(tbl.Token.Indent) + bcl(failed, red))
|
||||
}
|
||||
}
|
||||
|
||||
func (f *pretty) printStep(step *gherkin.Step, def *StepDef, c color) {
|
||||
text := s(step.Token.Indent) + cl(step.Token.Keyword, c) + " "
|
||||
switch {
|
||||
case def != nil:
|
||||
if m := (def.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)
|
||||
}
|
||||
// use reflect to get step handler function name
|
||||
name := runtime.FuncForPC(reflect.ValueOf(def.Handler).Pointer()).Name()
|
||||
text += s(f.commentPos-step.Token.Length()+1) + cl(fmt.Sprintf("# %s", name), black)
|
||||
default:
|
||||
text += cl(step.Text, c)
|
||||
}
|
||||
|
||||
fmt.Println(text)
|
||||
if step.PyString != nil {
|
||||
fmt.Println(s(step.Token.Indent+2) + cl(`"""`, c))
|
||||
fmt.Println(cl(step.PyString.Raw, c))
|
||||
fmt.Println(s(step.Token.Indent+2) + cl(`"""`, c))
|
||||
}
|
||||
if step.Table != nil {
|
||||
f.printTable(step.Table, c)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *pretty) stepDetails(stepAction interface{}) (step *gherkin.Step, def *StepDef, c color, err error) {
|
||||
switch typ := stepAction.(type) {
|
||||
case *passed:
|
||||
step = typ.step
|
||||
h = typ.handler
|
||||
def = typ.def
|
||||
c = green
|
||||
case *failed:
|
||||
step = typ.step
|
||||
h = typ.handler
|
||||
def = typ.def
|
||||
err = typ.err
|
||||
c = red
|
||||
case *skipped:
|
||||
|
@ -168,56 +296,33 @@ func (f *pretty) printStep(stepAction interface{}) {
|
|||
default:
|
||||
fatal(fmt.Errorf("unexpected step type received: %T", typ))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (f *pretty) printStepKind(stepAction interface{}) {
|
||||
var c color
|
||||
var step *gherkin.Step
|
||||
var def *StepDef
|
||||
var err error
|
||||
|
||||
step, def, c, err = f.stepDetails(stepAction)
|
||||
|
||||
// do not print background more than once
|
||||
if f.scenario == nil && step.Background != f.background {
|
||||
return
|
||||
}
|
||||
|
||||
if h != nil {
|
||||
if m := (h.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 {
|
||||
suffix += cl(step.Text[pos:m[i]], c)
|
||||
} else {
|
||||
suffix += bcl(step.Text[pos:m[i]], c)
|
||||
}
|
||||
pos = m[i]
|
||||
}
|
||||
suffix += cl(step.Text[pos:len(step.Text)], c)
|
||||
} else {
|
||||
suffix = cl(step.Text, c)
|
||||
if f.outlineExamples != 0 {
|
||||
f.outlineSteps = append(f.outlineSteps, stepAction)
|
||||
if len(f.outlineSteps) == f.outlineNumSteps {
|
||||
// an outline example steps has went through
|
||||
f.printOutlineExample()
|
||||
f.outlineExamples -= 1
|
||||
}
|
||||
// use reflect to get step handler function name
|
||||
name := runtime.FuncForPC(reflect.ValueOf(h.handler).Pointer()).Name()
|
||||
suffix += s(f.commentPos-len(step.Token.Text)+1) + cl(fmt.Sprintf("# %s", name), black)
|
||||
} else {
|
||||
suffix = cl(step.Text, c)
|
||||
return // wait till example steps
|
||||
}
|
||||
|
||||
prefix = s(step.Token.Indent)
|
||||
switch step.Token.Type {
|
||||
case gherkin.GIVEN:
|
||||
prefix += cl("Given", c)
|
||||
case gherkin.WHEN:
|
||||
prefix += cl("When", c)
|
||||
case gherkin.THEN:
|
||||
prefix += cl("Then", c)
|
||||
case gherkin.AND:
|
||||
prefix += cl("And", c)
|
||||
case gherkin.BUT:
|
||||
prefix += cl("But", c)
|
||||
}
|
||||
fmt.Println(prefix, suffix)
|
||||
if step.PyString != nil {
|
||||
fmt.Println(s(step.Token.Indent+2) + cl(`"""`, c))
|
||||
fmt.Println(cl(step.PyString.Raw, c))
|
||||
fmt.Println(s(step.Token.Indent+2) + cl(`"""`, c))
|
||||
}
|
||||
if step.Table != nil {
|
||||
f.printTable(step.Table, c)
|
||||
}
|
||||
f.printStep(step, def, c)
|
||||
if err != nil {
|
||||
fmt.Println(s(step.Token.Indent) + bcl(err, red))
|
||||
}
|
||||
|
@ -225,8 +330,47 @@ func (f *pretty) printStep(stepAction interface{}) {
|
|||
|
||||
// print table with aligned table cells
|
||||
func (f *pretty) printTable(t *gherkin.Table, c color) {
|
||||
var longest = make([]int, len(t.Rows[0]))
|
||||
var l = longest(t)
|
||||
var cols = make([]string, len(t.Rows[0]))
|
||||
for _, row := range t.Rows {
|
||||
for i, col := range row {
|
||||
cols[i] = col + s(l[i]-len(col))
|
||||
}
|
||||
fmt.Println(s(t.Token.Indent) + cl("| "+strings.Join(cols, " | ")+" |", c))
|
||||
}
|
||||
}
|
||||
|
||||
// Passed is called to represent a passed step
|
||||
func (f *pretty) Passed(step *gherkin.Step, match *StepDef) {
|
||||
s := &passed{step: step, def: match}
|
||||
f.printStepKind(s)
|
||||
f.passed = append(f.passed, s)
|
||||
}
|
||||
|
||||
// Skipped is called to represent a passed step
|
||||
func (f *pretty) Skipped(step *gherkin.Step) {
|
||||
s := &skipped{step: step}
|
||||
f.printStepKind(s)
|
||||
f.skipped = append(f.skipped, s)
|
||||
}
|
||||
|
||||
// Undefined is called to represent a pending step
|
||||
func (f *pretty) Undefined(step *gherkin.Step) {
|
||||
s := &undefined{step: step}
|
||||
f.printStepKind(s)
|
||||
f.undefined = append(f.undefined, s)
|
||||
}
|
||||
|
||||
// Failed is called to represent a failed step
|
||||
func (f *pretty) Failed(step *gherkin.Step, match *StepDef, err error) {
|
||||
s := &failed{step: step, def: match, err: err}
|
||||
f.printStepKind(s)
|
||||
f.failed = append(f.failed, s)
|
||||
}
|
||||
|
||||
// longest gives a list of longest columns of all rows in Table
|
||||
func longest(t *gherkin.Table) []int {
|
||||
var longest = make([]int, len(t.Rows[0]))
|
||||
for _, row := range t.Rows {
|
||||
for i, col := range row {
|
||||
if longest[i] < len(col) {
|
||||
|
@ -234,38 +378,16 @@ func (f *pretty) printTable(t *gherkin.Table, c color) {
|
|||
}
|
||||
}
|
||||
}
|
||||
for _, row := range t.Rows {
|
||||
for i, col := range row {
|
||||
cols[i] = col + s(longest[i]-len(col))
|
||||
return longest
|
||||
}
|
||||
|
||||
func longestStep(steps []*gherkin.Step, base int) int {
|
||||
ret := base
|
||||
for _, step := range steps {
|
||||
length := step.Token.Length()
|
||||
if length > base {
|
||||
ret = length
|
||||
}
|
||||
fmt.Println(s(t.Token.Indent) + cl("| "+strings.Join(cols, " | ")+" |", c))
|
||||
}
|
||||
}
|
||||
|
||||
// Passed is called to represent a passed step
|
||||
func (f *pretty) Passed(step *gherkin.Step, match *stepMatchHandler) {
|
||||
s := &passed{step: step, handler: match}
|
||||
f.printStep(s)
|
||||
f.passed = append(f.passed, s)
|
||||
}
|
||||
|
||||
// Skipped is called to represent a passed step
|
||||
func (f *pretty) Skipped(step *gherkin.Step) {
|
||||
s := &skipped{step: step}
|
||||
f.printStep(s)
|
||||
f.skipped = append(f.skipped, s)
|
||||
}
|
||||
|
||||
// Undefined is called to represent a pending step
|
||||
func (f *pretty) Undefined(step *gherkin.Step) {
|
||||
s := &undefined{step: step}
|
||||
f.printStep(s)
|
||||
f.undefined = append(f.undefined, s)
|
||||
}
|
||||
|
||||
// Failed is called to represent a failed step
|
||||
func (f *pretty) Failed(step *gherkin.Step, match *stepMatchHandler, err error) {
|
||||
s := &failed{step: step, handler: match, err: err}
|
||||
f.printStep(s)
|
||||
f.failed = append(f.failed, s)
|
||||
return ret
|
||||
}
|
||||
|
|
|
@ -23,8 +23,8 @@ func (f *testFormatter) Node(node interface{}) {
|
|||
|
||||
func (f *testFormatter) Summary() {}
|
||||
|
||||
func (f *testFormatter) Passed(step *gherkin.Step, match *stepMatchHandler) {
|
||||
f.passed = append(f.passed, &passed{step: step, handler: match})
|
||||
func (f *testFormatter) Passed(step *gherkin.Step, match *StepDef) {
|
||||
f.passed = append(f.passed, &passed{step: step, def: match})
|
||||
}
|
||||
|
||||
func (f *testFormatter) Skipped(step *gherkin.Step) {
|
||||
|
@ -35,6 +35,6 @@ func (f *testFormatter) Undefined(step *gherkin.Step) {
|
|||
f.undefined = append(f.undefined, &undefined{step: step})
|
||||
}
|
||||
|
||||
func (f *testFormatter) Failed(step *gherkin.Step, match *stepMatchHandler, err error) {
|
||||
f.failed = append(f.failed, &failed{step: step, handler: match, err: err})
|
||||
func (f *testFormatter) Failed(step *gherkin.Step, match *StepDef, err error) {
|
||||
f.failed = append(f.failed, &failed{step: step, def: match, err: err})
|
||||
}
|
||||
|
|
|
@ -88,6 +88,17 @@ func (t Tags) Has(tag Tag) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// Outline is a scenario outline with an
|
||||
// example table. Steps are listed with
|
||||
// placeholders which are replaced with
|
||||
// each example table row
|
||||
type Outline struct {
|
||||
*Token
|
||||
Scenario *Scenario
|
||||
Steps []*Step
|
||||
Examples *Table
|
||||
}
|
||||
|
||||
// Scenario describes the scenario details
|
||||
//
|
||||
// if Examples table is not nil, then it
|
||||
|
@ -100,11 +111,11 @@ func (t Tags) Has(tag Tag) bool {
|
|||
// initialization tasks
|
||||
type Scenario struct {
|
||||
*Token
|
||||
Title string
|
||||
Steps []*Step
|
||||
Tags Tags
|
||||
Examples *Table
|
||||
Feature *Feature
|
||||
Title string
|
||||
Steps []*Step
|
||||
Tags Tags
|
||||
Outline *Outline
|
||||
Feature *Feature
|
||||
}
|
||||
|
||||
// Background steps are run before every scenario
|
||||
|
@ -154,9 +165,8 @@ func (p *PyString) String() string {
|
|||
// step definition or outline scenario
|
||||
type Table struct {
|
||||
*Token
|
||||
OutlineScenario *Scenario
|
||||
Step *Step
|
||||
Rows [][]string
|
||||
Step *Step
|
||||
Rows [][]string
|
||||
}
|
||||
|
||||
var allSteps = []TokenType{
|
||||
|
@ -320,10 +330,18 @@ func (p *parser) parseScenario() (s *Scenario, err error) {
|
|||
"but got '" + peek.Type.String() + "' instead, for scenario outline examples",
|
||||
}, " "), examples.Line)
|
||||
}
|
||||
if s.Examples, err = p.parseTable(); err != nil {
|
||||
s.Outline = &Outline{
|
||||
Token: examples,
|
||||
Scenario: s,
|
||||
Steps: s.Steps,
|
||||
}
|
||||
s.Steps = []*Step{} // move steps to outline
|
||||
if s.Outline.Examples, err = p.parseTable(); err != nil {
|
||||
return s, err
|
||||
}
|
||||
s.Examples.OutlineScenario = s
|
||||
if len(s.Outline.Examples.Rows) < 2 {
|
||||
return s, p.err("expected an example table to have at least two rows: header and at least one example", examples.Line)
|
||||
}
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
|
|
@ -21,6 +21,30 @@ var matchers = map[string]*regexp.Regexp{
|
|||
"table_row": regexp.MustCompile("^(\\s*)\\|([^#]*)(#.*)?"),
|
||||
}
|
||||
|
||||
// for now only english language is supported
|
||||
var keywords = map[TokenType]string{
|
||||
// special
|
||||
ILLEGAL: "Illegal",
|
||||
EOF: "End of file",
|
||||
NEW_LINE: "New line",
|
||||
TAGS: "Tags",
|
||||
COMMENT: "Comment",
|
||||
PYSTRING: "PyString",
|
||||
TABLE_ROW: "Table row",
|
||||
TEXT: "Text",
|
||||
// general
|
||||
GIVEN: "Given",
|
||||
WHEN: "When",
|
||||
THEN: "Then",
|
||||
AND: "And",
|
||||
BUT: "But",
|
||||
FEATURE: "Feature",
|
||||
BACKGROUND: "Background",
|
||||
SCENARIO: "Scenario",
|
||||
SCENARIO_OUTLINE: "Scenario Outline",
|
||||
EXAMPLES: "Examples",
|
||||
}
|
||||
|
||||
type lexer struct {
|
||||
reader *bufio.Reader
|
||||
lines int
|
||||
|
@ -36,8 +60,9 @@ func (l *lexer) read() *Token {
|
|||
line, err := l.reader.ReadString(byte('\n'))
|
||||
if err != nil && len(line) == 0 {
|
||||
return &Token{
|
||||
Type: EOF,
|
||||
Line: l.lines,
|
||||
Type: EOF,
|
||||
Line: l.lines + 1,
|
||||
Keyword: keywords[EOF],
|
||||
}
|
||||
}
|
||||
l.lines++
|
||||
|
@ -45,8 +70,9 @@ func (l *lexer) read() *Token {
|
|||
// newline
|
||||
if len(line) == 0 {
|
||||
return &Token{
|
||||
Type: NEW_LINE,
|
||||
Line: l.lines,
|
||||
Type: NEW_LINE,
|
||||
Line: l.lines,
|
||||
Keyword: keywords[NEW_LINE],
|
||||
}
|
||||
}
|
||||
// comment
|
||||
|
@ -59,15 +85,17 @@ func (l *lexer) read() *Token {
|
|||
Value: comment,
|
||||
Text: line,
|
||||
Comment: comment,
|
||||
Keyword: keywords[COMMENT],
|
||||
}
|
||||
}
|
||||
// pystring
|
||||
if m := matchers["pystring"].FindStringSubmatch(line); len(m) > 0 {
|
||||
return &Token{
|
||||
Type: PYSTRING,
|
||||
Indent: len(m[1]),
|
||||
Line: l.lines,
|
||||
Text: line,
|
||||
Type: PYSTRING,
|
||||
Indent: len(m[1]),
|
||||
Line: l.lines,
|
||||
Text: line,
|
||||
Keyword: keywords[PYSTRING],
|
||||
}
|
||||
}
|
||||
// step
|
||||
|
@ -91,6 +119,7 @@ func (l *lexer) read() *Token {
|
|||
case "But":
|
||||
tok.Type = BUT
|
||||
}
|
||||
tok.Keyword = keywords[tok.Type]
|
||||
return tok
|
||||
}
|
||||
// scenario
|
||||
|
@ -102,6 +131,7 @@ func (l *lexer) read() *Token {
|
|||
Value: strings.TrimSpace(m[2]),
|
||||
Text: line,
|
||||
Comment: strings.Trim(m[3], " #"),
|
||||
Keyword: keywords[SCENARIO],
|
||||
}
|
||||
}
|
||||
// background
|
||||
|
@ -112,6 +142,7 @@ func (l *lexer) read() *Token {
|
|||
Line: l.lines,
|
||||
Text: line,
|
||||
Comment: strings.Trim(m[2], " #"),
|
||||
Keyword: keywords[BACKGROUND],
|
||||
}
|
||||
}
|
||||
// feature
|
||||
|
@ -123,6 +154,7 @@ func (l *lexer) read() *Token {
|
|||
Value: strings.TrimSpace(m[2]),
|
||||
Text: line,
|
||||
Comment: strings.Trim(m[3], " #"),
|
||||
Keyword: keywords[FEATURE],
|
||||
}
|
||||
}
|
||||
// tags
|
||||
|
@ -134,6 +166,7 @@ func (l *lexer) read() *Token {
|
|||
Value: strings.TrimSpace(m[2]),
|
||||
Text: line,
|
||||
Comment: strings.Trim(m[3], " #"),
|
||||
Keyword: keywords[TAGS],
|
||||
}
|
||||
}
|
||||
// table row
|
||||
|
@ -145,6 +178,7 @@ func (l *lexer) read() *Token {
|
|||
Value: strings.TrimSpace(m[2]),
|
||||
Text: line,
|
||||
Comment: strings.Trim(m[3], " #"),
|
||||
Keyword: keywords[TABLE_ROW],
|
||||
}
|
||||
}
|
||||
// scenario outline
|
||||
|
@ -156,6 +190,7 @@ func (l *lexer) read() *Token {
|
|||
Value: strings.TrimSpace(m[2]),
|
||||
Text: line,
|
||||
Comment: strings.Trim(m[3], " #"),
|
||||
Keyword: keywords[SCENARIO_OUTLINE],
|
||||
}
|
||||
}
|
||||
// examples
|
||||
|
@ -166,15 +201,17 @@ func (l *lexer) read() *Token {
|
|||
Line: l.lines,
|
||||
Text: line,
|
||||
Comment: strings.Trim(m[2], " #"),
|
||||
Keyword: keywords[EXAMPLES],
|
||||
}
|
||||
}
|
||||
// text
|
||||
text := strings.TrimLeftFunc(line, unicode.IsSpace)
|
||||
return &Token{
|
||||
Type: TEXT,
|
||||
Line: l.lines,
|
||||
Value: text,
|
||||
Indent: len(line) - len(text),
|
||||
Text: line,
|
||||
Type: TEXT,
|
||||
Line: l.lines,
|
||||
Value: text,
|
||||
Indent: len(line) - len(text),
|
||||
Text: line,
|
||||
Keyword: keywords[TEXT],
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,16 @@ func (s *Scenario) assertTitle(title string, t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *Scenario) assertOutlineStep(text string, t *testing.T) *Step {
|
||||
for _, stp := range s.Outline.Steps {
|
||||
if stp.Text == text {
|
||||
return stp
|
||||
}
|
||||
}
|
||||
t.Fatal("expected scenario '%s' to have step: '%s', but it did not", s.Title, text)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Scenario) assertStep(text string, t *testing.T) *Step {
|
||||
for _, stp := range s.Steps {
|
||||
if stp.Text == text {
|
||||
|
@ -22,16 +32,16 @@ func (s *Scenario) assertStep(text string, t *testing.T) *Step {
|
|||
}
|
||||
|
||||
func (s *Scenario) assertExampleRow(t *testing.T, num int, cols ...string) {
|
||||
if s.Examples == nil {
|
||||
if s.Outline.Examples == nil {
|
||||
t.Fatalf("outline scenario '%s' has no examples", s.Title)
|
||||
}
|
||||
if len(s.Examples.Rows) <= num {
|
||||
if len(s.Outline.Examples.Rows) <= num {
|
||||
t.Fatalf("outline scenario '%s' table has no row: %d", s.Title, num)
|
||||
}
|
||||
if len(s.Examples.Rows[num]) != len(cols) {
|
||||
if len(s.Outline.Examples.Rows[num]) != len(cols) {
|
||||
t.Fatalf("outline scenario '%s' table row length, does not match expected: %d", s.Title, len(cols))
|
||||
}
|
||||
for i, col := range s.Examples.Rows[num] {
|
||||
for i, col := range s.Outline.Examples.Rows[num] {
|
||||
if col != cols[i] {
|
||||
t.Fatalf("outline scenario '%s' table row %d, column %d - value '%s', does not match expected: %s", s.Title, num, i, col, cols[i])
|
||||
}
|
||||
|
@ -64,11 +74,11 @@ func Test_parse_scenario_outline(t *testing.T) {
|
|||
TABLE_ROW,
|
||||
}, t)
|
||||
|
||||
s.assertStep(`I am in a directory "test"`, t)
|
||||
s.assertStep(`I have a file named "foo"`, t)
|
||||
s.assertStep(`I have a file named "bar"`, t)
|
||||
s.assertStep(`I run "ls" with options "<options>"`, t)
|
||||
s.assertStep(`I should see "<result>"`, t)
|
||||
s.assertOutlineStep(`I am in a directory "test"`, t)
|
||||
s.assertOutlineStep(`I have a file named "foo"`, t)
|
||||
s.assertOutlineStep(`I have a file named "bar"`, t)
|
||||
s.assertOutlineStep(`I run "ls" with options "<options>"`, t)
|
||||
s.assertOutlineStep(`I should see "<result>"`, t)
|
||||
|
||||
s.assertExampleRow(t, 0, "options", "result")
|
||||
s.assertExampleRow(t, 1, "-t", "bar foo")
|
||||
|
|
|
@ -1,29 +1,26 @@
|
|||
package gherkin
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
type TokenType int
|
||||
|
||||
const (
|
||||
ILLEGAL TokenType = iota
|
||||
|
||||
specials
|
||||
COMMENT
|
||||
NEW_LINE
|
||||
EOF
|
||||
|
||||
elements
|
||||
TEXT
|
||||
TAGS
|
||||
TABLE_ROW
|
||||
PYSTRING
|
||||
|
||||
keywords
|
||||
FEATURE
|
||||
BACKGROUND
|
||||
SCENARIO
|
||||
SCENARIO_OUTLINE
|
||||
EXAMPLES
|
||||
|
||||
steps
|
||||
GIVEN
|
||||
WHEN
|
||||
THEN
|
||||
|
@ -31,54 +28,22 @@ const (
|
|||
BUT
|
||||
)
|
||||
|
||||
// String gives a string representation of token type
|
||||
func (t TokenType) String() string {
|
||||
switch t {
|
||||
case COMMENT:
|
||||
return "comment"
|
||||
case NEW_LINE:
|
||||
return "new line"
|
||||
case EOF:
|
||||
return "end of file"
|
||||
case TEXT:
|
||||
return "text"
|
||||
case TAGS:
|
||||
return "tags"
|
||||
case TABLE_ROW:
|
||||
return "table row"
|
||||
case PYSTRING:
|
||||
return "pystring"
|
||||
case FEATURE:
|
||||
return "feature"
|
||||
case BACKGROUND:
|
||||
return "background"
|
||||
case SCENARIO:
|
||||
return "scenario"
|
||||
case SCENARIO_OUTLINE:
|
||||
return "scenario outline"
|
||||
case EXAMPLES:
|
||||
return "examples"
|
||||
case GIVEN:
|
||||
return "given step"
|
||||
case WHEN:
|
||||
return "when step"
|
||||
case THEN:
|
||||
return "then step"
|
||||
case AND:
|
||||
return "and step"
|
||||
case BUT:
|
||||
return "but step"
|
||||
}
|
||||
return "illegal"
|
||||
return keywords[t]
|
||||
}
|
||||
|
||||
// Token represents a line in gherkin feature file
|
||||
type Token struct {
|
||||
Type TokenType // type of token
|
||||
Line, Indent int // line and indentation number
|
||||
Value string // interpreted value
|
||||
Text string // same text as read
|
||||
Keyword string // @TODO: the translated keyword
|
||||
Comment string // a comment
|
||||
}
|
||||
|
||||
// OfType checks whether token is one of types
|
||||
func (t *Token) OfType(all ...TokenType) bool {
|
||||
for _, typ := range all {
|
||||
if typ == t.Type {
|
||||
|
@ -87,3 +52,12 @@ func (t *Token) OfType(all ...TokenType) bool {
|
|||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Length gives a token text length with indentation
|
||||
// and keyword, but without comment
|
||||
func (t *Token) Length() int {
|
||||
if pos := strings.Index(t.Text, "#"); pos != -1 {
|
||||
return len(strings.TrimRightFunc(t.Text[:pos], unicode.IsSpace))
|
||||
}
|
||||
return len(t.Text)
|
||||
}
|
||||
|
|
193
suite.go
193
suite.go
|
@ -7,6 +7,7 @@ import (
|
|||
"reflect"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/DATA-DOG/godog/gherkin"
|
||||
)
|
||||
|
@ -20,30 +21,6 @@ type Regexp interface{}
|
|||
// 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
|
||||
Passed
|
||||
Failed
|
||||
Undefined
|
||||
)
|
||||
|
||||
// String represents status as string
|
||||
func (s Status) String() string {
|
||||
switch s {
|
||||
case Passed:
|
||||
return "passed"
|
||||
case Failed:
|
||||
return "failed"
|
||||
case Undefined:
|
||||
return "undefined"
|
||||
}
|
||||
return "invalid"
|
||||
}
|
||||
|
||||
// Objects implementing the StepHandler interface can be
|
||||
// registered as step definitions in godog
|
||||
//
|
||||
|
@ -70,11 +47,17 @@ func (f StepHandlerFunc) HandleStep(args ...*Arg) error {
|
|||
return f(args...)
|
||||
}
|
||||
|
||||
var errPending = fmt.Errorf("pending step")
|
||||
// ErrUndefined is returned in case if step definition was not found
|
||||
var ErrUndefined = fmt.Errorf("step is undefined")
|
||||
|
||||
type stepMatchHandler struct {
|
||||
handler StepHandler
|
||||
expr *regexp.Regexp
|
||||
// StepDef is a registered step definition
|
||||
// contains a StepHandler, a regexp which
|
||||
// is used to match a step and Args which
|
||||
// were matched by last step
|
||||
type StepDef struct {
|
||||
Args []*Arg
|
||||
Handler StepHandler
|
||||
Expr *regexp.Regexp
|
||||
}
|
||||
|
||||
// Suite is an interface which allows various contexts
|
||||
|
@ -91,7 +74,7 @@ type Suite interface {
|
|||
}
|
||||
|
||||
type suite struct {
|
||||
stepHandlers []*stepMatchHandler
|
||||
stepHandlers []*StepDef
|
||||
features []*gherkin.Feature
|
||||
fmt Formatter
|
||||
|
||||
|
@ -151,9 +134,9 @@ func (s *suite) Step(expr Regexp, h Handler) {
|
|||
default:
|
||||
panic(fmt.Sprintf("expecting handler to satisfy StepHandler interface, got type: %T", h))
|
||||
}
|
||||
s.stepHandlers = append(s.stepHandlers, &stepMatchHandler{
|
||||
handler: handler,
|
||||
expr: regex,
|
||||
s.stepHandlers = append(s.stepHandlers, &StepDef{
|
||||
Handler: handler,
|
||||
Expr: regex,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -243,12 +226,10 @@ func (s *suite) run() {
|
|||
s.fmt.Summary()
|
||||
}
|
||||
|
||||
func (s *suite) runStep(step *gherkin.Step) (err error) {
|
||||
var match *stepMatchHandler
|
||||
var args []*Arg
|
||||
func (s *suite) matchStep(step *gherkin.Step) *StepDef {
|
||||
for _, h := range s.stepHandlers {
|
||||
if m := h.expr.FindStringSubmatch(step.Text); len(m) > 0 {
|
||||
match = h
|
||||
if m := h.Expr.FindStringSubmatch(step.Text); len(m) > 0 {
|
||||
var args []*Arg
|
||||
for _, a := range m[1:] {
|
||||
args = append(args, &Arg{value: a})
|
||||
}
|
||||
|
@ -258,12 +239,18 @@ func (s *suite) runStep(step *gherkin.Step) (err error) {
|
|||
if step.PyString != nil {
|
||||
args = append(args, &Arg{value: step.PyString})
|
||||
}
|
||||
break
|
||||
h.Args = args
|
||||
return h
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *suite) runStep(step *gherkin.Step) (err error) {
|
||||
match := s.matchStep(step)
|
||||
if match == nil {
|
||||
s.fmt.Undefined(step)
|
||||
return errPending
|
||||
return ErrUndefined
|
||||
}
|
||||
|
||||
defer func() {
|
||||
|
@ -273,7 +260,7 @@ func (s *suite) runStep(step *gherkin.Step) (err error) {
|
|||
}
|
||||
}()
|
||||
|
||||
if err = match.handler.HandleStep(args...); err != nil {
|
||||
if err = match.Handler.HandleStep(match.Args...); err != nil {
|
||||
s.fmt.Failed(step, match, err)
|
||||
} else {
|
||||
s.fmt.Passed(step, match)
|
||||
|
@ -281,9 +268,9 @@ func (s *suite) runStep(step *gherkin.Step) (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func (s *suite) runSteps(steps []*gherkin.Step) (st Status) {
|
||||
func (s *suite) runSteps(steps []*gherkin.Step) (err error) {
|
||||
for _, step := range steps {
|
||||
if st == Failed || st == Undefined {
|
||||
if err != nil {
|
||||
s.fmt.Skipped(step)
|
||||
continue
|
||||
}
|
||||
|
@ -293,19 +280,11 @@ func (s *suite) runSteps(steps []*gherkin.Step) (st Status) {
|
|||
h.HandleBeforeStep(step)
|
||||
}
|
||||
|
||||
err := s.runStep(step)
|
||||
switch err {
|
||||
case errPending:
|
||||
st = Undefined
|
||||
case nil:
|
||||
st = Passed
|
||||
default:
|
||||
st = Failed
|
||||
}
|
||||
err = s.runStep(step)
|
||||
|
||||
// run after step handlers
|
||||
for _, h := range s.afterStepHandlers {
|
||||
h.HandleAfterStep(step, st)
|
||||
h.HandleAfterStep(step, err)
|
||||
}
|
||||
}
|
||||
return
|
||||
|
@ -317,39 +296,52 @@ func (s *suite) skipSteps(steps []*gherkin.Step) {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *suite) runOutline(scenario *gherkin.Scenario) (err error) {
|
||||
placeholders := scenario.Outline.Examples.Rows[0]
|
||||
examples := scenario.Outline.Examples.Rows[1:]
|
||||
for _, example := range examples {
|
||||
var steps []*gherkin.Step
|
||||
for _, step := range scenario.Outline.Steps {
|
||||
text := step.Text
|
||||
for i, placeholder := range placeholders {
|
||||
text = strings.Replace(text, "<"+placeholder+">", example[i], -1)
|
||||
}
|
||||
// clone a step
|
||||
cloned := &gherkin.Step{
|
||||
Token: step.Token,
|
||||
Text: text,
|
||||
Type: step.Type,
|
||||
PyString: step.PyString,
|
||||
Table: step.Table,
|
||||
Background: step.Background,
|
||||
Scenario: scenario,
|
||||
}
|
||||
steps = append(steps, cloned)
|
||||
}
|
||||
|
||||
// set steps to scenario
|
||||
scenario.Steps = steps
|
||||
if err = s.runScenario(scenario); err != nil && err != ErrUndefined {
|
||||
s.failed = true
|
||||
if cfg.stopOnFailure {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *suite) runFeature(f *gherkin.Feature) {
|
||||
s.fmt.Node(f)
|
||||
for _, scenario := range f.Scenarios {
|
||||
var status Status
|
||||
|
||||
// run before scenario handlers
|
||||
for _, h := range s.beforeScenarioHandlers {
|
||||
h.HandleBeforeScenario(scenario)
|
||||
var err error
|
||||
// handle scenario outline differently
|
||||
if scenario.Outline != nil {
|
||||
err = s.runOutline(scenario)
|
||||
} else {
|
||||
err = s.runScenario(scenario)
|
||||
}
|
||||
|
||||
// background
|
||||
if f.Background != nil {
|
||||
s.fmt.Node(f.Background)
|
||||
status = s.runSteps(f.Background.Steps)
|
||||
}
|
||||
|
||||
// scenario
|
||||
s.fmt.Node(scenario)
|
||||
switch {
|
||||
case status == Failed:
|
||||
s.skipSteps(scenario.Steps)
|
||||
case status == Undefined:
|
||||
s.skipSteps(scenario.Steps)
|
||||
case status == Passed || status == Invalid:
|
||||
status = s.runSteps(scenario.Steps)
|
||||
}
|
||||
|
||||
// run after scenario handlers
|
||||
for _, h := range s.afterScenarioHandlers {
|
||||
h.HandleAfterScenario(scenario, status)
|
||||
}
|
||||
|
||||
if status == Failed {
|
||||
if err != nil && err != ErrUndefined {
|
||||
s.failed = true
|
||||
if cfg.stopOnFailure {
|
||||
return
|
||||
|
@ -358,16 +350,47 @@ func (s *suite) runFeature(f *gherkin.Feature) {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *suite) runScenario(scenario *gherkin.Scenario) (err error) {
|
||||
// run before scenario handlers
|
||||
for _, h := range s.beforeScenarioHandlers {
|
||||
h.HandleBeforeScenario(scenario)
|
||||
}
|
||||
|
||||
// background
|
||||
if scenario.Feature.Background != nil {
|
||||
s.fmt.Node(scenario.Feature.Background)
|
||||
err = s.runSteps(scenario.Feature.Background.Steps)
|
||||
}
|
||||
|
||||
// scenario
|
||||
s.fmt.Node(scenario)
|
||||
switch err {
|
||||
case ErrUndefined:
|
||||
s.skipSteps(scenario.Steps)
|
||||
case nil:
|
||||
err = s.runSteps(scenario.Steps)
|
||||
default:
|
||||
s.skipSteps(scenario.Steps)
|
||||
}
|
||||
|
||||
// run after scenario handlers
|
||||
for _, h := range s.afterScenarioHandlers {
|
||||
h.HandleAfterScenario(scenario, err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (st *suite) printStepDefinitions() {
|
||||
var longest int
|
||||
for _, def := range st.stepHandlers {
|
||||
if longest < len(def.expr.String()) {
|
||||
longest = len(def.expr.String())
|
||||
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))
|
||||
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..")
|
||||
|
|
|
@ -121,14 +121,14 @@ func (s *suiteContext) iAmListeningToSuiteEvents(args ...*Arg) error {
|
|||
s.testedSuite.BeforeScenario(BeforeScenarioHandlerFunc(func(scenario *gherkin.Scenario) {
|
||||
s.events = append(s.events, &firedEvent{"BeforeScenario", []interface{}{scenario}})
|
||||
}))
|
||||
s.testedSuite.AfterScenario(AfterScenarioHandlerFunc(func(scenario *gherkin.Scenario, status Status) {
|
||||
s.events = append(s.events, &firedEvent{"AfterScenario", []interface{}{scenario, status}})
|
||||
s.testedSuite.AfterScenario(AfterScenarioHandlerFunc(func(scenario *gherkin.Scenario, err error) {
|
||||
s.events = append(s.events, &firedEvent{"AfterScenario", []interface{}{scenario, err}})
|
||||
}))
|
||||
s.testedSuite.BeforeStep(BeforeStepHandlerFunc(func(step *gherkin.Step) {
|
||||
s.events = append(s.events, &firedEvent{"BeforeStep", []interface{}{step}})
|
||||
}))
|
||||
s.testedSuite.AfterStep(AfterStepHandlerFunc(func(step *gherkin.Step, status Status) {
|
||||
s.events = append(s.events, &firedEvent{"AfterStep", []interface{}{step, status}})
|
||||
s.testedSuite.AfterStep(AfterStepHandlerFunc(func(step *gherkin.Step, err error) {
|
||||
s.events = append(s.events, &firedEvent{"AfterStep", []interface{}{step, err}})
|
||||
}))
|
||||
return nil
|
||||
}
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче