diff --git a/fmt_progress.go b/fmt_progress.go index 01cee24..8d503af 100644 --- a/fmt_progress.go +++ b/fmt_progress.go @@ -1,3 +1,140 @@ package godog -// @TODO: implement progress formatter +import ( + "fmt" + "math" + "strings" + "time" + + "github.com/DATA-DOG/godog/gherkin" +) + +func init() { + RegisterFormatter("progress", "Prints a character per step.", &progress{ + started: time.Now(), + stepsPerRow: 70, + }) +} + +type progress struct { + stepsPerRow int + started time.Time + steps int + features []*gherkin.Feature + + failed []*failed + passed []*passed + skipped []*skipped + undefined []*undefined +} + +func (f *progress) Node(node interface{}) { + switch t := node.(type) { + case *gherkin.Feature: + f.features = append(f.features, t) + } +} + +func (f *progress) Summary() { + left := math.Mod(float64(f.steps), float64(f.stepsPerRow)) + if left != 0 { + if int(f.steps) > f.stepsPerRow { + fmt.Printf(s(f.stepsPerRow-int(left)) + fmt.Sprintf(" %d\n", f.steps)) + } else { + fmt.Printf(" %d\n", f.steps) + } + } + fmt.Println("") + + if len(f.failed) > 0 { + fmt.Println("\n--- " + cl("Failed steps:", red) + "\n") + for _, fail := range f.failed { + fmt.Println(s(4) + cl(fail.step.Token.Keyword+" "+fail.step.Text, red) + cl(" # "+fail.line(), black)) + fmt.Println(s(6) + cl("Error: ", red) + bcl(fail.err, red) + "\n") + } + } + var total, passed int + for _, ft := range f.features { + total += len(ft.Scenarios) + } + passed = total + + var steps, parts, scenarios []string + nsteps := len(f.passed) + len(f.failed) + len(f.skipped) + len(f.undefined) + if len(f.passed) > 0 { + steps = append(steps, cl(fmt.Sprintf("%d passed", len(f.passed)), green)) + } + if len(f.failed) > 0 { + passed -= len(f.failed) + parts = append(parts, cl(fmt.Sprintf("%d failed", len(f.failed)), red)) + steps = append(steps, parts[len(parts)-1]) + } + if len(f.skipped) > 0 { + steps = append(steps, cl(fmt.Sprintf("%d skipped", len(f.skipped)), cyan)) + } + if len(f.undefined) > 0 { + passed -= len(f.undefined) + parts = append(parts, cl(fmt.Sprintf("%d undefined", len(f.undefined)), yellow)) + steps = append(steps, parts[len(parts)-1]) + } + if passed > 0 { + scenarios = append(scenarios, cl(fmt.Sprintf("%d passed", passed), green)) + } + scenarios = append(scenarios, parts...) + elapsed := time.Since(f.started) + + fmt.Println("") + if total == 0 { + fmt.Println("No scenarios") + } else { + fmt.Println(fmt.Sprintf("%d scenarios (%s)", total, strings.Join(scenarios, ", "))) + } + + if nsteps == 0 { + fmt.Println("No steps") + } else { + fmt.Println(fmt.Sprintf("%d steps (%s)", nsteps, strings.Join(steps, ", "))) + } + fmt.Println(elapsed) +} + +func (f *progress) step(step interface{}) { + switch step.(type) { + case *passed: + fmt.Print(cl(".", green)) + case *skipped: + fmt.Print(cl("-", cyan)) + case *failed: + fmt.Print(cl("F", red)) + case *undefined: + fmt.Print(cl("U", yellow)) + } + f.steps += 1 + if math.Mod(float64(f.steps), float64(f.stepsPerRow)) == 0 { + fmt.Printf(" %d\n", f.steps) + } +} + +func (f *progress) Passed(step *gherkin.Step, match *StepDef) { + s := &passed{step: step, def: match} + f.passed = append(f.passed, s) + f.step(s) +} + +func (f *progress) Skipped(step *gherkin.Step) { + s := &skipped{step: step} + f.skipped = append(f.skipped, s) + f.step(s) +} + +func (f *progress) Undefined(step *gherkin.Step) { + s := &undefined{step: step} + f.undefined = append(f.undefined, s) + f.step(s) +} + +func (f *progress) Failed(step *gherkin.Step, match *StepDef, err error) { + s := &failed{step: step, def: match, err: err} + f.failed = append(f.failed, s) + f.step(s) +}