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