continue with output formatter implementation
Этот коммит содержится в:
родитель
3ee0746a3c
коммит
b69fa26b8b
3 изменённых файлов: 173 добавлений и 51 удалений
45
config.go
45
config.go
|
@ -1,6 +1,7 @@
|
|||
package godog
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
@ -9,14 +10,37 @@ import (
|
|||
"github.com/DATA-DOG/godog/gherkin"
|
||||
)
|
||||
|
||||
type registeredFormatter struct {
|
||||
name string
|
||||
fmt Formatter
|
||||
}
|
||||
|
||||
var formatters []*registeredFormatter
|
||||
|
||||
// RegisterFormatter registers a feature suite output
|
||||
// Formatter for its given name
|
||||
func RegisterFormatter(name string, f Formatter) {
|
||||
formatters = append(formatters, ®isteredFormatter{
|
||||
name: name,
|
||||
fmt: f,
|
||||
})
|
||||
}
|
||||
|
||||
var cfg config
|
||||
|
||||
func init() {
|
||||
// @TODO: colorize flag help output
|
||||
flag.StringVar(&cfg.featuresPath, "features", "features", "Path to feature files")
|
||||
flag.StringVar(&cfg.formatterName, "formatter", "pretty", "Formatter name")
|
||||
}
|
||||
|
||||
type config struct {
|
||||
featuresPath string
|
||||
formatterName string
|
||||
}
|
||||
|
||||
func (c config) validate() error {
|
||||
// feature path
|
||||
inf, err := os.Stat(c.featuresPath)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -24,11 +48,20 @@ func (c config) validate() error {
|
|||
if !inf.IsDir() {
|
||||
return fmt.Errorf("feature path \"%s\" is not a directory.", c.featuresPath)
|
||||
}
|
||||
switch c.formatterName {
|
||||
case "pretty":
|
||||
// ok
|
||||
default:
|
||||
return fmt.Errorf("Unsupported formatter name: %s", c.formatterName)
|
||||
|
||||
// formatter
|
||||
var found bool
|
||||
var names []string
|
||||
for _, f := range formatters {
|
||||
if f.name == c.formatterName {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
names = append(names, f.name)
|
||||
}
|
||||
|
||||
if !found {
|
||||
return fmt.Errorf(`unregistered formatter name: "%s", use one of: %s`, c.formatterName, strings.Join(names, ", "))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -46,7 +79,7 @@ func (c config) features() (lst []*gherkin.Feature, err error) {
|
|||
})
|
||||
}
|
||||
|
||||
func (c config) formatter() formatter {
|
||||
func (c config) formatter() Formatter {
|
||||
return &pretty{}
|
||||
}
|
||||
|
||||
|
|
87
formatter.go
87
formatter.go
|
@ -2,26 +2,93 @@ package godog
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/DATA-DOG/godog/gherkin"
|
||||
)
|
||||
|
||||
type formatter interface {
|
||||
node(interface{})
|
||||
func init() {
|
||||
RegisterFormatter("pretty", &pretty{})
|
||||
}
|
||||
|
||||
type pretty struct{}
|
||||
// Formatter is an interface for feature runner output
|
||||
type Formatter interface {
|
||||
Node(interface{})
|
||||
Failed(*gherkin.Step, error)
|
||||
Passed(*gherkin.Step)
|
||||
Skipped(*gherkin.Step)
|
||||
Pending(*gherkin.Step)
|
||||
}
|
||||
|
||||
func (f *pretty) node(node interface{}) {
|
||||
type pretty struct {
|
||||
feature *gherkin.Feature
|
||||
scenario *gherkin.Scenario
|
||||
doneBackground bool
|
||||
background *gherkin.Background
|
||||
}
|
||||
|
||||
func (f *pretty) line(tok *gherkin.Token) string {
|
||||
return cl(fmt.Sprintf("#%s:%d", f.feature.Path, tok.Line), magenta)
|
||||
}
|
||||
|
||||
func (f *pretty) canPrintStep(step *gherkin.Step) bool {
|
||||
if f.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
|
||||
}
|
||||
|
||||
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")
|
||||
f.feature = t
|
||||
f.doneBackground = false
|
||||
f.scenario = nil
|
||||
f.background = nil
|
||||
fmt.Println("\n"+bcl("Feature: ", white)+t.Title, f.line(t.Token))
|
||||
fmt.Println(t.Description)
|
||||
case *gherkin.Background:
|
||||
fmt.Println(bcl("Background:", white))
|
||||
f.background = t
|
||||
fmt.Println("\n" + bcl("Background:", white))
|
||||
case *gherkin.Scenario:
|
||||
fmt.Println(bcl("Scenario: ", white) + t.Title)
|
||||
case *gherkin.Step:
|
||||
fmt.Println(bcl(t.Token.Text, green))
|
||||
fmt.Println("\n"+strings.Repeat(" ", t.Token.Indent)+bcl("Scenario: ", white)+t.Title, f.line(t.Token))
|
||||
}
|
||||
}
|
||||
|
||||
func (f *pretty) Passed(step *gherkin.Step) {
|
||||
if f.canPrintStep(step) {
|
||||
fmt.Println(cl(step.Token.Text, green))
|
||||
}
|
||||
}
|
||||
|
||||
func (f *pretty) Skipped(step *gherkin.Step) {
|
||||
if f.canPrintStep(step) {
|
||||
fmt.Println(cl(step.Token.Text, cyan))
|
||||
}
|
||||
}
|
||||
|
||||
func (f *pretty) Pending(step *gherkin.Step) {
|
||||
if f.canPrintStep(step) {
|
||||
fmt.Println(cl(step.Token.Text, yellow))
|
||||
}
|
||||
}
|
||||
|
||||
func (f *pretty) Failed(step *gherkin.Step, err error) {
|
||||
if f.canPrintStep(step) {
|
||||
fmt.Println(cl(step.Token.Text, red))
|
||||
}
|
||||
}
|
||||
|
|
92
suite.go
92
suite.go
|
@ -64,6 +64,8 @@ func (f StepHandlerFunc) HandleStep(args ...Arg) error {
|
|||
return f(args...)
|
||||
}
|
||||
|
||||
var errPending = fmt.Errorf("pending step")
|
||||
|
||||
// Suite is an interface which allows various contexts
|
||||
// to register step definitions and event handlers
|
||||
type Suite interface {
|
||||
|
@ -73,19 +75,13 @@ type Suite interface {
|
|||
type suite struct {
|
||||
steps map[*regexp.Regexp]StepHandler
|
||||
features []*gherkin.Feature
|
||||
fmt formatter
|
||||
fmt Formatter
|
||||
}
|
||||
|
||||
// New initializes a suite which supports the Suite
|
||||
// interface. The instance is passed around to all
|
||||
// context initialization functions from *_test.go files
|
||||
func New() *suite {
|
||||
// @TODO: colorize flag help output
|
||||
flag.StringVar(&cfg.featuresPath, "features", "features", "Path to feature files")
|
||||
flag.StringVar(&cfg.formatterName, "formatter", "pretty", "Formatter name")
|
||||
if !flag.Parsed() {
|
||||
flag.Parse()
|
||||
}
|
||||
return &suite{
|
||||
steps: make(map[*regexp.Regexp]StepHandler),
|
||||
}
|
||||
|
@ -108,6 +104,11 @@ func (s *suite) Step(exp *regexp.Regexp, h StepHandler) {
|
|||
// Run - runs a godog feature suite
|
||||
func (s *suite) Run() {
|
||||
var err error
|
||||
if !flag.Parsed() {
|
||||
flag.Parse()
|
||||
}
|
||||
fatal(cfg.validate())
|
||||
|
||||
s.fmt = cfg.formatter()
|
||||
s.features, err = cfg.features()
|
||||
fatal(err)
|
||||
|
@ -120,7 +121,14 @@ func (s *suite) Run() {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *suite) runStep(step *gherkin.Step) {
|
||||
func (s *suite) runStep(step *gherkin.Step) (err error) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
err = e.(error)
|
||||
s.fmt.Failed(step, err)
|
||||
}
|
||||
}()
|
||||
|
||||
var handler StepHandler
|
||||
var args []Arg
|
||||
for r, h := range s.steps {
|
||||
|
@ -133,41 +141,55 @@ func (s *suite) runStep(step *gherkin.Step) {
|
|||
}
|
||||
}
|
||||
if handler == nil {
|
||||
fmt.Println("PENDING")
|
||||
return
|
||||
s.fmt.Pending(step)
|
||||
return errPending
|
||||
}
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
fmt.Println("PANIC")
|
||||
}
|
||||
}()
|
||||
if err := handler.HandleStep(args...); err != nil {
|
||||
fmt.Println("ERR")
|
||||
|
||||
if err = handler.HandleStep(args...); err != nil {
|
||||
s.fmt.Failed(step, err)
|
||||
} else {
|
||||
fmt.Println("OK")
|
||||
s.fmt.Passed(step)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *suite) runSteps(steps []*gherkin.Step) bool {
|
||||
var failed bool
|
||||
for _, step := range steps {
|
||||
if failed {
|
||||
s.fmt.Skipped(step)
|
||||
continue
|
||||
}
|
||||
if err := s.runStep(step); err != nil {
|
||||
failed = true
|
||||
}
|
||||
}
|
||||
return failed
|
||||
}
|
||||
|
||||
func (s *suite) skipSteps(steps []*gherkin.Step) {
|
||||
for _, step := range steps {
|
||||
s.fmt.Skipped(step)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *suite) runFeature(f *gherkin.Feature) {
|
||||
s.fmt.node(f)
|
||||
var background bool
|
||||
s.fmt.Node(f)
|
||||
var failed bool
|
||||
for _, scenario := range f.Scenarios {
|
||||
if f.Background != nil {
|
||||
if !background {
|
||||
s.fmt.node(f.Background)
|
||||
}
|
||||
for _, step := range f.Background.Steps {
|
||||
s.runStep(step)
|
||||
if !background {
|
||||
s.fmt.node(step)
|
||||
}
|
||||
}
|
||||
background = true
|
||||
// background
|
||||
// @TODO: do not print more than once
|
||||
if f.Background != nil && !failed {
|
||||
s.fmt.Node(f.Background)
|
||||
failed = s.runSteps(f.Background.Steps)
|
||||
}
|
||||
s.fmt.node(scenario)
|
||||
for _, step := range scenario.Steps {
|
||||
s.runStep(step)
|
||||
s.fmt.node(step)
|
||||
|
||||
// scenario
|
||||
s.fmt.Node(scenario)
|
||||
if failed {
|
||||
s.skipSteps(scenario.Steps)
|
||||
} else {
|
||||
s.runSteps(scenario.Steps)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче