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
cmd := exec.Command("sh", "-c", c)
cmd.Stdout = stdout
// @TODO: do not read stderr on production version
cmd.Stderr = stdout
err = cmd.Run()
switch err.(type) {

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

@ -31,7 +31,6 @@ type Formatter interface {
// general pretty formatter structure
type pretty struct {
feature *gherkin.Feature
scenario *gherkin.Scenario
commentPos int
doneBackground bool
background *gherkin.Background
@ -48,34 +47,39 @@ type pretty struct {
// failed represents a failed step data structure
// with all necessary references
type failed struct {
feature *gherkin.Feature
scenario *gherkin.Scenario
step *gherkin.Step
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
// with all necessary references
type passed struct {
feature *gherkin.Feature
scenario *gherkin.Scenario
step *gherkin.Step
step *gherkin.Step
}
// skipped represents a skipped step data structure
// with all necessary references
type skipped struct {
feature *gherkin.Feature
scenario *gherkin.Scenario
step *gherkin.Step
step *gherkin.Step
}
// undefined represents a pending step data structure
// with all necessary references
type undefined struct {
feature *gherkin.Feature
scenario *gherkin.Scenario
step *gherkin.Step
step *gherkin.Step
}
// 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
func (f *pretty) canPrintStep(step *gherkin.Step) bool {
if f.background == nil {
return true
if f.background != nil {
return step.Background != nil
}
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
return true
}
// Node takes a gherkin node for formatting
@ -111,7 +101,6 @@ func (f *pretty) Node(node interface{}) {
case *gherkin.Feature:
f.feature = t
f.doneBackground = false
f.scenario = nil
f.background = nil
f.features = append(f.features, t)
fmt.Println(bcl("Feature: ", white) + t.Title + "\n")
@ -120,7 +109,6 @@ func (f *pretty) Node(node interface{}) {
f.background = t
fmt.Println(bcl("Background:", white) + "\n")
case *gherkin.Scenario:
f.scenario = t
f.commentPos = len(t.Token.Text)
for _, step := range t.Steps {
if len(step.Token.Text) > f.commentPos {
@ -135,10 +123,17 @@ func (f *pretty) Node(node interface{}) {
// Summary sumarize the feature formatter output
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")
for _, fail := range f.failed {
fmt.Println(" " + cl(fmt.Sprintf("%s:%d", fail.feature.Path, fail.scenario.Token.Line), red))
for _, fail := range failedScenarios {
fmt.Println(" " + cl(fail.line(), red))
}
}
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
func (f *pretty) Passed(step *gherkin.Step, match *stepMatchHandler) {
f.printMatchedStep(step, match, green)
f.passed = append(f.passed, &passed{
feature: f.feature,
scenario: f.scenario,
step: step,
})
f.passed = append(f.passed, &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) {
fmt.Println(cl(step.Token.Text, cyan))
}
f.skipped = append(f.skipped, &skipped{
feature: f.feature,
scenario: f.scenario,
step: step,
})
f.skipped = append(f.skipped, &skipped{step})
}
// Undefined is called to represent a pending step
@ -254,21 +241,12 @@ func (f *pretty) Undefined(step *gherkin.Step) {
if f.canPrintStep(step) {
fmt.Println(cl(step.Token.Text, yellow))
}
f.undefined = append(f.undefined, &undefined{
feature: f.feature,
scenario: f.scenario,
step: step,
})
f.undefined = append(f.undefined, &undefined{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))
f.failed = append(f.failed, &failed{
feature: f.feature,
scenario: f.scenario,
step: step,
err: err,
})
f.failed = append(f.failed, &failed{step, err})
}

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

@ -102,22 +102,26 @@ type Scenario struct {
Title string
Steps []*Step
Tags Tags
Examples *Table
Examples *ExampleTable
Feature *Feature
}
// Background steps are run before every scenario
type Background struct {
*Token
Steps []*Step
Steps []*Step
Feature *Feature
}
// Step describes a Scenario or Background step
type Step struct {
*Token
Text string
Type string
PyString *PyString
Table *Table
Text string
Type string
PyString *PyString
Table *StepTable
Scenario *Scenario
Background *Background
}
// Feature describes the whole feature
@ -136,14 +140,28 @@ type Feature struct {
type PyString struct {
*Token
Body string
Step *Step
}
// Table is a row group object used with step definition
type Table struct {
type table struct {
*Token
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{
GIVEN,
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)
}
ft.Background = &Background{Token: tok}
ft.Background = &Background{Token: tok, Feature: ft}
p.next() // jump to background steps
if ft.Background.Steps, err = p.parseSteps(); err != nil {
return ft, err
}
for _, step := range ft.Background.Steps {
step.Background = ft.Background
}
tok = p.peek() // peek to scenario or tags
}
@ -269,6 +290,7 @@ func (p *parser) parseFeature() (ft *Feature, err error) {
}
scenario.Tags = tags
scenario.Feature = ft
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 {
return s, err
}
for _, step := range s.Steps {
step.Scenario = s
}
if examples := p.peek(); examples.Type == EXAMPLES {
p.next() // jump over the peeked token
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",
}, " "), examples.Line)
}
if s.Examples, err = p.parseTable(); err != nil {
tbl, err := p.parseTable()
if err != nil {
return s, err
}
s.Examples = &ExampleTable{OutlineScenario: s, table: tbl}
}
return s, nil
}
@ -309,10 +336,13 @@ func (p *parser) parseSteps() (steps []*Step, err error) {
if step.PyString, err = p.parsePystring(); err != nil {
return steps, err
}
step.PyString.Step = step
case TABLE_ROW:
if step.Table, err = p.parseTable(); err != nil {
tbl, err := p.parseTable()
if err != nil {
return steps, err
}
step.Table = &StepTable{Step: step, table: tbl}
default:
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
}
func (p *parser) parseTable() (*Table, error) {
tbl := &Table{}
func (p *parser) parseTable() (*table, error) {
tbl := &table{}
for row := p.peek(); row.Type == TABLE_ROW; row = p.peek() {
var cols []string
for _, r := range strings.Split(strings.Trim(row.Value, "|"), "|") {

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

@ -4,40 +4,10 @@ import (
"flag"
"fmt"
"regexp"
"strconv"
"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
// registered as step definitions in godog
//