more specific types and references for gherkin nodes
Этот коммит содержится в:
родитель
7f214c702b
коммит
2d3d04e0e6
5 изменённых файлов: 114 добавлений и 99 удалений
35
arguments.go
Обычный файл
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) {
|
||||||
|
|
92
formatter.go
92
formatter.go
|
@ -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, "|"), "|") {
|
||||||
|
|
30
suite.go
30
suite.go
|
@ -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
|
||||||
//
|
//
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче