more specific types and references for gherkin nodes

Этот коммит содержится в:
gedi 2015-06-16 17:22:58 +03:00
родитель 7f214c702b
коммит 2d3d04e0e6
5 изменённых файлов: 114 добавлений и 99 удалений

35
arguments.go Обычный файл
Просмотреть файл

@ -0,0 +1,35 @@
package godog
import (
"fmt"
"strconv"
)
// Arg is an argument for StepHandler parsed from
// the regexp submatch to handle the step
type Arg string
// Float converts an argument to float64
// or panics if unable to convert it
func (a Arg) Float() float64 {
v, err := strconv.ParseFloat(string(a), 64)
if err == nil {
return v
}
panic(fmt.Sprintf(`cannot convert "%s" to float64: %s`, a, err))
}
// Int converts an argument to int64
// or panics if unable to convert it
func (a Arg) Int() int64 {
v, err := strconv.ParseInt(string(a), 10, 0)
if err == nil {
return v
}
panic(fmt.Sprintf(`cannot convert "%s" to int64: %s`, a, err))
}
// String converts an argument to string
func (a Arg) String() string {
return string(a)
}

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

@ -37,6 +37,8 @@ func main() {
// @TODO: support for windows // @TODO: support for windows
cmd := exec.Command("sh", "-c", c) cmd := exec.Command("sh", "-c", c)
cmd.Stdout = stdout cmd.Stdout = stdout
// @TODO: do not read stderr on production version
cmd.Stderr = stdout
err = cmd.Run() err = cmd.Run()
switch err.(type) { switch err.(type) {

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

@ -31,7 +31,6 @@ type Formatter interface {
// general pretty formatter structure // general pretty formatter structure
type pretty struct { type pretty struct {
feature *gherkin.Feature feature *gherkin.Feature
scenario *gherkin.Scenario
commentPos int commentPos int
doneBackground bool doneBackground bool
background *gherkin.Background background *gherkin.Background
@ -48,34 +47,39 @@ type pretty struct {
// failed represents a failed step data structure // failed represents a failed step data structure
// with all necessary references // with all necessary references
type failed struct { type failed struct {
feature *gherkin.Feature step *gherkin.Step
scenario *gherkin.Scenario err error
step *gherkin.Step }
err error
func (f failed) line() string {
var tok *gherkin.Token
var ft *gherkin.Feature
if f.step.Scenario != nil {
tok = f.step.Scenario.Token
ft = f.step.Scenario.Feature
} else {
tok = f.step.Background.Token
ft = f.step.Background.Feature
}
return fmt.Sprintf("%s:%d", ft.Path, tok.Line)
} }
// passed represents a successful step data structure // passed represents a successful step data structure
// with all necessary references // with all necessary references
type passed struct { type passed struct {
feature *gherkin.Feature step *gherkin.Step
scenario *gherkin.Scenario
step *gherkin.Step
} }
// skipped represents a skipped step data structure // skipped represents a skipped step data structure
// with all necessary references // with all necessary references
type skipped struct { type skipped struct {
feature *gherkin.Feature step *gherkin.Step
scenario *gherkin.Scenario
step *gherkin.Step
} }
// undefined represents a pending step data structure // undefined represents a pending step data structure
// with all necessary references // with all necessary references
type undefined struct { type undefined struct {
feature *gherkin.Feature step *gherkin.Step
scenario *gherkin.Scenario
step *gherkin.Step
} }
// a line number representation in feature file // a line number representation in feature file
@ -85,24 +89,10 @@ func (f *pretty) line(tok *gherkin.Token) string {
// checks whether it should not print a background step once again // checks whether it should not print a background step once again
func (f *pretty) canPrintStep(step *gherkin.Step) bool { func (f *pretty) canPrintStep(step *gherkin.Step) bool {
if f.background == nil { if f.background != nil {
return true return step.Background != nil
} }
return true
var backgroundStep bool
for _, s := range f.background.Steps {
if s == step {
backgroundStep = true
break
}
}
if !backgroundStep {
f.doneBackground = true
return true
}
return !f.doneBackground
} }
// Node takes a gherkin node for formatting // Node takes a gherkin node for formatting
@ -111,7 +101,6 @@ func (f *pretty) Node(node interface{}) {
case *gherkin.Feature: case *gherkin.Feature:
f.feature = t f.feature = t
f.doneBackground = false f.doneBackground = false
f.scenario = nil
f.background = nil f.background = nil
f.features = append(f.features, t) f.features = append(f.features, t)
fmt.Println(bcl("Feature: ", white) + t.Title + "\n") fmt.Println(bcl("Feature: ", white) + t.Title + "\n")
@ -120,7 +109,6 @@ func (f *pretty) Node(node interface{}) {
f.background = t f.background = t
fmt.Println(bcl("Background:", white) + "\n") fmt.Println(bcl("Background:", white) + "\n")
case *gherkin.Scenario: case *gherkin.Scenario:
f.scenario = t
f.commentPos = len(t.Token.Text) f.commentPos = len(t.Token.Text)
for _, step := range t.Steps { for _, step := range t.Steps {
if len(step.Token.Text) > f.commentPos { if len(step.Token.Text) > f.commentPos {
@ -135,10 +123,17 @@ func (f *pretty) Node(node interface{}) {
// Summary sumarize the feature formatter output // Summary sumarize the feature formatter output
func (f *pretty) Summary() { func (f *pretty) Summary() {
if len(f.failed) > 0 { // failed steps on background are not scenarios
var failedScenarios []*failed
for _, fail := range f.failed {
if fail.step.Scenario != nil {
failedScenarios = append(failedScenarios, fail)
}
}
if len(failedScenarios) > 0 {
fmt.Println("\n--- " + cl("Failed scenarios:", red) + "\n") fmt.Println("\n--- " + cl("Failed scenarios:", red) + "\n")
for _, fail := range f.failed { for _, fail := range failedScenarios {
fmt.Println(" " + cl(fmt.Sprintf("%s:%d", fail.feature.Path, fail.scenario.Token.Line), red)) fmt.Println(" " + cl(fail.line(), red))
} }
} }
var total, passed int var total, passed int
@ -230,11 +225,7 @@ func (f *pretty) printMatchedStep(step *gherkin.Step, match *stepMatchHandler, c
// Passed is called to represent a passed step // Passed is called to represent a passed step
func (f *pretty) Passed(step *gherkin.Step, match *stepMatchHandler) { func (f *pretty) Passed(step *gherkin.Step, match *stepMatchHandler) {
f.printMatchedStep(step, match, green) f.printMatchedStep(step, match, green)
f.passed = append(f.passed, &passed{ f.passed = append(f.passed, &passed{step})
feature: f.feature,
scenario: f.scenario,
step: step,
})
} }
// Skipped is called to represent a passed step // Skipped is called to represent a passed step
@ -242,11 +233,7 @@ func (f *pretty) Skipped(step *gherkin.Step) {
if f.canPrintStep(step) { if f.canPrintStep(step) {
fmt.Println(cl(step.Token.Text, cyan)) fmt.Println(cl(step.Token.Text, cyan))
} }
f.skipped = append(f.skipped, &skipped{ f.skipped = append(f.skipped, &skipped{step})
feature: f.feature,
scenario: f.scenario,
step: step,
})
} }
// Undefined is called to represent a pending step // Undefined is called to represent a pending step
@ -254,21 +241,12 @@ func (f *pretty) Undefined(step *gherkin.Step) {
if f.canPrintStep(step) { if f.canPrintStep(step) {
fmt.Println(cl(step.Token.Text, yellow)) fmt.Println(cl(step.Token.Text, yellow))
} }
f.undefined = append(f.undefined, &undefined{ f.undefined = append(f.undefined, &undefined{step})
feature: f.feature,
scenario: f.scenario,
step: step,
})
} }
// Failed is called to represent a failed step // Failed is called to represent a failed step
func (f *pretty) Failed(step *gherkin.Step, match *stepMatchHandler, err error) { func (f *pretty) Failed(step *gherkin.Step, match *stepMatchHandler, err error) {
f.printMatchedStep(step, match, red) f.printMatchedStep(step, match, red)
fmt.Println(strings.Repeat(" ", step.Token.Indent) + bcl(err, red)) fmt.Println(strings.Repeat(" ", step.Token.Indent) + bcl(err, red))
f.failed = append(f.failed, &failed{ f.failed = append(f.failed, &failed{step, err})
feature: f.feature,
scenario: f.scenario,
step: step,
err: err,
})
} }

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

@ -102,22 +102,26 @@ type Scenario struct {
Title string Title string
Steps []*Step Steps []*Step
Tags Tags Tags Tags
Examples *Table Examples *ExampleTable
Feature *Feature
} }
// Background steps are run before every scenario // Background steps are run before every scenario
type Background struct { type Background struct {
*Token *Token
Steps []*Step Steps []*Step
Feature *Feature
} }
// Step describes a Scenario or Background step // Step describes a Scenario or Background step
type Step struct { type Step struct {
*Token *Token
Text string Text string
Type string Type string
PyString *PyString PyString *PyString
Table *Table Table *StepTable
Scenario *Scenario
Background *Background
} }
// Feature describes the whole feature // Feature describes the whole feature
@ -136,14 +140,28 @@ type Feature struct {
type PyString struct { type PyString struct {
*Token *Token
Body string Body string
Step *Step
} }
// Table is a row group object used with step definition // Table is a row group object used with step definition
type Table struct { type table struct {
*Token *Token
rows [][]string rows [][]string
} }
// ExampleTable is a row group object for
// scenario outline examples
type ExampleTable struct {
*table
OutlineScenario *Scenario
}
// StepTable is a row group object for steps
type StepTable struct {
*table
Step *Step
}
var allSteps = []TokenType{ var allSteps = []TokenType{
GIVEN, GIVEN,
WHEN, WHEN,
@ -238,11 +256,14 @@ func (p *parser) parseFeature() (ft *Feature, err error) {
return ft, p.err("there can only be a single background section, but found another", tok.Line) return ft, p.err("there can only be a single background section, but found another", tok.Line)
} }
ft.Background = &Background{Token: tok} ft.Background = &Background{Token: tok, Feature: ft}
p.next() // jump to background steps p.next() // jump to background steps
if ft.Background.Steps, err = p.parseSteps(); err != nil { if ft.Background.Steps, err = p.parseSteps(); err != nil {
return ft, err return ft, err
} }
for _, step := range ft.Background.Steps {
step.Background = ft.Background
}
tok = p.peek() // peek to scenario or tags tok = p.peek() // peek to scenario or tags
} }
@ -269,6 +290,7 @@ func (p *parser) parseFeature() (ft *Feature, err error) {
} }
scenario.Tags = tags scenario.Tags = tags
scenario.Feature = ft
ft.Scenarios = append(ft.Scenarios, scenario) ft.Scenarios = append(ft.Scenarios, scenario)
} }
@ -281,6 +303,9 @@ func (p *parser) parseScenario() (s *Scenario, err error) {
if s.Steps, err = p.parseSteps(); err != nil { if s.Steps, err = p.parseSteps(); err != nil {
return s, err return s, err
} }
for _, step := range s.Steps {
step.Scenario = s
}
if examples := p.peek(); examples.Type == EXAMPLES { if examples := p.peek(); examples.Type == EXAMPLES {
p.next() // jump over the peeked token p.next() // jump over the peeked token
peek := p.peek() peek := p.peek()
@ -290,9 +315,11 @@ func (p *parser) parseScenario() (s *Scenario, err error) {
"but got '" + peek.Type.String() + "' instead, for scenario outline examples", "but got '" + peek.Type.String() + "' instead, for scenario outline examples",
}, " "), examples.Line) }, " "), examples.Line)
} }
if s.Examples, err = p.parseTable(); err != nil { tbl, err := p.parseTable()
if err != nil {
return s, err return s, err
} }
s.Examples = &ExampleTable{OutlineScenario: s, table: tbl}
} }
return s, nil return s, nil
} }
@ -309,10 +336,13 @@ func (p *parser) parseSteps() (steps []*Step, err error) {
if step.PyString, err = p.parsePystring(); err != nil { if step.PyString, err = p.parsePystring(); err != nil {
return steps, err return steps, err
} }
step.PyString.Step = step
case TABLE_ROW: case TABLE_ROW:
if step.Table, err = p.parseTable(); err != nil { tbl, err := p.parseTable()
if err != nil {
return steps, err return steps, err
} }
step.Table = &StepTable{Step: step, table: tbl}
default: default:
return steps, p.err("pystring or table row was expected, but got: '"+tok.Type.String()+"' instead", tok.Line) return steps, p.err("pystring or table row was expected, but got: '"+tok.Type.String()+"' instead", tok.Line)
} }
@ -339,8 +369,8 @@ func (p *parser) parsePystring() (*PyString, error) {
}, nil }, nil
} }
func (p *parser) parseTable() (*Table, error) { func (p *parser) parseTable() (*table, error) {
tbl := &Table{} tbl := &table{}
for row := p.peek(); row.Type == TABLE_ROW; row = p.peek() { for row := p.peek(); row.Type == TABLE_ROW; row = p.peek() {
var cols []string var cols []string
for _, r := range strings.Split(strings.Trim(row.Value, "|"), "|") { for _, r := range strings.Split(strings.Trim(row.Value, "|"), "|") {

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

@ -4,40 +4,10 @@ import (
"flag" "flag"
"fmt" "fmt"
"regexp" "regexp"
"strconv"
"github.com/DATA-DOG/godog/gherkin" "github.com/DATA-DOG/godog/gherkin"
) )
// Arg is an argument for StepHandler parsed from
// the regexp submatch to handle the step
type Arg string
// Float converts an argument to float64
// or panics if unable to convert it
func (a Arg) Float() float64 {
v, err := strconv.ParseFloat(string(a), 64)
if err == nil {
return v
}
panic(fmt.Sprintf(`cannot convert "%s" to float64: %s`, a, err))
}
// Int converts an argument to int64
// or panics if unable to convert it
func (a Arg) Int() int64 {
v, err := strconv.ParseInt(string(a), 10, 0)
if err == nil {
return v
}
panic(fmt.Sprintf(`cannot convert "%s" to int64: %s`, a, err))
}
// String converts an argument to string
func (a Arg) String() string {
return string(a)
}
// Objects implementing the StepHandler interface can be // Objects implementing the StepHandler interface can be
// registered as step definitions in godog // registered as step definitions in godog
// //