an initial formatter impl
Этот коммит содержится в:
родитель
fa9419c2d3
коммит
3a2a357bd0
7 изменённых файлов: 96 добавлений и 57 удалений
|
@ -4,6 +4,8 @@ import "fmt"
|
||||||
|
|
||||||
type color int
|
type color int
|
||||||
|
|
||||||
|
const ansiEscape = "\x1b"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
black color = iota + 30
|
black color = iota + 30
|
||||||
red
|
red
|
||||||
|
@ -16,5 +18,9 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
func cl(s interface{}, c color) string {
|
func cl(s interface{}, c color) string {
|
||||||
return fmt.Sprintf("\033[%dm%v\033[0m", c, s)
|
return fmt.Sprintf("%s[%dm%v%s[0m", ansiEscape, c, s, ansiEscape)
|
||||||
|
}
|
||||||
|
|
||||||
|
func bcl(s interface{}, c color) string {
|
||||||
|
return fmt.Sprintf("%s[1;%dm%v%s[0m", ansiEscape, c, s, ansiEscape)
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,10 @@ func (c config) features() (lst []*gherkin.Feature, err error) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c config) formatter() formatter {
|
||||||
|
return &pretty{}
|
||||||
|
}
|
||||||
|
|
||||||
func fatal(err error) {
|
func fatal(err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
|
|
|
@ -3,7 +3,7 @@ Feature: godog bdd suite
|
||||||
As a suite
|
As a suite
|
||||||
I need to be able to register and run features
|
I need to be able to register and run features
|
||||||
|
|
||||||
Scenario:
|
Scenario: parses all features in path
|
||||||
Given a feature path "features"
|
Given a feature path "features"
|
||||||
When I parse features
|
When I parse features
|
||||||
Then I should have 1 feature file
|
Then I should have 1 feature file
|
||||||
|
|
27
formatter.go
Обычный файл
27
formatter.go
Обычный файл
|
@ -0,0 +1,27 @@
|
||||||
|
package godog
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/DATA-DOG/godog/gherkin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type formatter interface {
|
||||||
|
node(interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
type pretty struct{}
|
||||||
|
|
||||||
|
func (f *pretty) node(node interface{}) {
|
||||||
|
switch t := node.(type) {
|
||||||
|
case *gherkin.Feature:
|
||||||
|
fmt.Println(bcl("Feature: ", white) + t.Title)
|
||||||
|
fmt.Println(t.Description + "\n")
|
||||||
|
case *gherkin.Background:
|
||||||
|
fmt.Println(bcl("Background:", white))
|
||||||
|
case *gherkin.Scenario:
|
||||||
|
fmt.Println(bcl("Scenario: ", white) + t.Title)
|
||||||
|
case *gherkin.Step:
|
||||||
|
fmt.Println(bcl(t.Token.Text, green))
|
||||||
|
}
|
||||||
|
}
|
|
@ -77,6 +77,7 @@ type Tag string
|
||||||
// Tags is an array of tags
|
// Tags is an array of tags
|
||||||
type Tags []Tag
|
type Tags []Tag
|
||||||
|
|
||||||
|
// Has checks whether the tag list has a tag
|
||||||
func (t Tags) Has(tag Tag) bool {
|
func (t Tags) Has(tag Tag) bool {
|
||||||
for _, tg := range t {
|
for _, tg := range t {
|
||||||
if tg == tag {
|
if tg == tag {
|
||||||
|
@ -97,17 +98,17 @@ func (t Tags) Has(tag Tag) bool {
|
||||||
// be used to filter out or run specific
|
// be used to filter out or run specific
|
||||||
// initialization tasks
|
// initialization tasks
|
||||||
type Scenario struct {
|
type Scenario struct {
|
||||||
|
*Token
|
||||||
Title string
|
Title string
|
||||||
Steps []*Step
|
Steps []*Step
|
||||||
Tags Tags
|
Tags Tags
|
||||||
Examples *Table
|
Examples *Table
|
||||||
Comment string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Background steps are run before every scenario
|
// Background steps are run before every scenario
|
||||||
type Background struct {
|
type Background struct {
|
||||||
Steps []*Step
|
*Token
|
||||||
Comment string
|
Steps []*Step
|
||||||
}
|
}
|
||||||
|
|
||||||
// StepType is a general type of step
|
// StepType is a general type of step
|
||||||
|
@ -121,8 +122,8 @@ const (
|
||||||
|
|
||||||
// Step describes a Scenario or Background step
|
// Step describes a Scenario or Background step
|
||||||
type Step struct {
|
type Step struct {
|
||||||
|
*Token
|
||||||
Text string
|
Text string
|
||||||
Comment string
|
|
||||||
Type StepType
|
Type StepType
|
||||||
PyString *PyString
|
PyString *PyString
|
||||||
Table *Table
|
Table *Table
|
||||||
|
@ -130,6 +131,7 @@ type Step struct {
|
||||||
|
|
||||||
// Feature describes the whole feature
|
// Feature describes the whole feature
|
||||||
type Feature struct {
|
type Feature struct {
|
||||||
|
*Token
|
||||||
Path string
|
Path string
|
||||||
Tags Tags
|
Tags Tags
|
||||||
Description string
|
Description string
|
||||||
|
@ -137,16 +139,17 @@ type Feature struct {
|
||||||
Background *Background
|
Background *Background
|
||||||
Scenarios []*Scenario
|
Scenarios []*Scenario
|
||||||
AST *AST
|
AST *AST
|
||||||
Comment string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PyString is a multiline text object used with step definition
|
// PyString is a multiline text object used with step definition
|
||||||
type PyString struct {
|
type PyString struct {
|
||||||
|
*Token
|
||||||
Body string
|
Body string
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
rows [][]string
|
rows [][]string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,11 +232,11 @@ func (p *parser) parseFeature() (ft *Feature, err error) {
|
||||||
return ft, p.err("expected a file to begin with a feature definition, but got '"+tok.Type.String()+"' instead", tok.Line)
|
return ft, p.err("expected a file to begin with a feature definition, but got '"+tok.Type.String()+"' instead", tok.Line)
|
||||||
}
|
}
|
||||||
ft.Title = tok.Value
|
ft.Title = tok.Value
|
||||||
ft.Comment = tok.Comment
|
ft.Token = tok
|
||||||
|
|
||||||
var desc []string
|
var desc []string
|
||||||
for ; p.peek().Type == TEXT; tok = p.next() {
|
for ; p.peek().Type == TEXT; tok = p.next() {
|
||||||
desc = append(desc, tok.Value)
|
desc = append(desc, p.peek().Text)
|
||||||
}
|
}
|
||||||
ft.Description = strings.Join(desc, "\n")
|
ft.Description = strings.Join(desc, "\n")
|
||||||
|
|
||||||
|
@ -244,7 +247,7 @@ 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{Comment: tok.Comment}
|
ft.Background = &Background{Token: tok}
|
||||||
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
|
||||||
|
@ -283,7 +286,7 @@ func (p *parser) parseFeature() (ft *Feature, err error) {
|
||||||
|
|
||||||
func (p *parser) parseScenario() (s *Scenario, err error) {
|
func (p *parser) parseScenario() (s *Scenario, err error) {
|
||||||
tok := p.next()
|
tok := p.next()
|
||||||
s = &Scenario{Title: tok.Value, Comment: tok.Comment}
|
s = &Scenario{Title: tok.Value, Token: tok}
|
||||||
if s.Steps, err = p.parseSteps(); err != nil {
|
if s.Steps, err = p.parseSteps(); err != nil {
|
||||||
return s, err
|
return s, err
|
||||||
}
|
}
|
||||||
|
@ -305,7 +308,7 @@ func (p *parser) parseScenario() (s *Scenario, err error) {
|
||||||
|
|
||||||
func (p *parser) parseSteps() (steps []*Step, err error) {
|
func (p *parser) parseSteps() (steps []*Step, err error) {
|
||||||
for tok := p.peek(); tok.OfType(allSteps...); tok = p.peek() {
|
for tok := p.peek(); tok.OfType(allSteps...); tok = p.peek() {
|
||||||
step := &Step{Text: tok.Value, Comment: tok.Comment}
|
step := &Step{Text: tok.Value, Token: tok}
|
||||||
switch tok.Type {
|
switch tok.Type {
|
||||||
case GIVEN:
|
case GIVEN:
|
||||||
step.Type = Given
|
step.Type = Given
|
||||||
|
|
|
@ -66,8 +66,8 @@ func (s *Step) assertPyString(text string, t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Step) assertComment(comment string, t *testing.T) {
|
func (s *Step) assertComment(comment string, t *testing.T) {
|
||||||
if s.Comment != comment {
|
if s.Token.Comment != comment {
|
||||||
t.Fatalf("expected step '%s' comment to be '%s', but got '%s'", s.Text, comment, s.Comment)
|
t.Fatalf("expected step '%s' comment to be '%s', but got '%s'", s.Text, comment, s.Token.Comment)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
85
suite.go
85
suite.go
|
@ -73,6 +73,7 @@ type Suite interface {
|
||||||
type suite struct {
|
type suite struct {
|
||||||
steps map[*regexp.Regexp]StepHandler
|
steps map[*regexp.Regexp]StepHandler
|
||||||
features []*gherkin.Feature
|
features []*gherkin.Feature
|
||||||
|
fmt formatter
|
||||||
}
|
}
|
||||||
|
|
||||||
// New initializes a suite which supports the Suite
|
// New initializes a suite which supports the Suite
|
||||||
|
@ -107,6 +108,7 @@ func (s *suite) Step(exp *regexp.Regexp, h StepHandler) {
|
||||||
// Run - runs a godog feature suite
|
// Run - runs a godog feature suite
|
||||||
func (s *suite) Run() {
|
func (s *suite) Run() {
|
||||||
var err error
|
var err error
|
||||||
|
s.fmt = cfg.formatter()
|
||||||
s.features, err = cfg.features()
|
s.features, err = cfg.features()
|
||||||
fatal(err)
|
fatal(err)
|
||||||
|
|
||||||
|
@ -118,57 +120,54 @@ func (s *suite) Run() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *suite) runStep(step *gherkin.Step) {
|
||||||
|
var handler StepHandler
|
||||||
|
var args []Arg
|
||||||
|
for r, h := range s.steps {
|
||||||
|
if m := r.FindStringSubmatch(step.Text); len(m) > 0 {
|
||||||
|
handler = h
|
||||||
|
for _, a := range m[1:] {
|
||||||
|
args = append(args, Arg(a))
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if handler == nil {
|
||||||
|
fmt.Println("PENDING")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if e := recover(); e != nil {
|
||||||
|
fmt.Println("PANIC")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if err := handler.HandleStep(args...); err != nil {
|
||||||
|
fmt.Println("ERR")
|
||||||
|
} else {
|
||||||
|
fmt.Println("OK")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *suite) runFeature(f *gherkin.Feature) {
|
func (s *suite) runFeature(f *gherkin.Feature) {
|
||||||
|
s.fmt.node(f)
|
||||||
|
var background bool
|
||||||
for _, scenario := range f.Scenarios {
|
for _, scenario := range f.Scenarios {
|
||||||
if f.Background != nil {
|
if f.Background != nil {
|
||||||
|
if !background {
|
||||||
|
s.fmt.node(f.Background)
|
||||||
|
}
|
||||||
for _, step := range f.Background.Steps {
|
for _, step := range f.Background.Steps {
|
||||||
var handler StepHandler
|
s.runStep(step)
|
||||||
var args []Arg
|
if !background {
|
||||||
for r, h := range s.steps {
|
s.fmt.node(step)
|
||||||
if m := r.FindStringSubmatch(step.Text); len(m) > 0 {
|
|
||||||
handler = h
|
|
||||||
for _, a := range m[1:] {
|
|
||||||
args = append(args, Arg(a))
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if handler != nil {
|
|
||||||
if err := handler.HandleStep(args...); err != nil {
|
|
||||||
// @TODO: scenario fails, step failed
|
|
||||||
fmt.Println("ERR")
|
|
||||||
} else {
|
|
||||||
fmt.Println("OK")
|
|
||||||
}
|
|
||||||
// @TODO: handle panic and recover
|
|
||||||
} else {
|
|
||||||
fmt.Println("PENDING")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
background = true
|
||||||
}
|
}
|
||||||
|
s.fmt.node(scenario)
|
||||||
for _, step := range scenario.Steps {
|
for _, step := range scenario.Steps {
|
||||||
var handler StepHandler
|
s.runStep(step)
|
||||||
var args []Arg
|
s.fmt.node(step)
|
||||||
for r, h := range s.steps {
|
|
||||||
if m := r.FindStringSubmatch(step.Text); len(m) > 0 {
|
|
||||||
handler = h
|
|
||||||
for _, a := range m[1:] {
|
|
||||||
args = append(args, Arg(a))
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if handler != nil {
|
|
||||||
if err := handler.HandleStep(args...); err != nil {
|
|
||||||
// @TODO: scenario fails, step failed
|
|
||||||
fmt.Println("ERR")
|
|
||||||
} else {
|
|
||||||
fmt.Println("OK")
|
|
||||||
}
|
|
||||||
// @TODO: handle panic and recover
|
|
||||||
} else {
|
|
||||||
fmt.Println("PENDING")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче