refactor colorization into the separate package

Этот коммит содержится в:
gedi 2016-10-28 17:59:10 +03:00
родитель a4b5349b94
коммит 115923c97f
12 изменённых файлов: 195 добавлений и 142 удалений

Просмотреть файл

@ -12,14 +12,12 @@ import (
"syscall" "syscall"
"github.com/DATA-DOG/godog" "github.com/DATA-DOG/godog"
"github.com/DATA-DOG/godog/colors"
) )
var statusMatch = regexp.MustCompile("^exit status (\\d+)") var statusMatch = regexp.MustCompile("^exit status (\\d+)")
var parsedStatus int var parsedStatus int
var stdout = io.Writer(os.Stdout)
var stderr = statusOutputFilter(os.Stderr)
func buildAndRun() (int, error) { func buildAndRun() (int, error) {
var status int var status int
@ -36,8 +34,8 @@ func buildAndRun() (int, error) {
defer os.Remove(bin) defer os.Remove(bin)
cmd := exec.Command(bin, os.Args[1:]...) cmd := exec.Command(bin, os.Args[1:]...)
cmd.Stdout = stdout cmd.Stdout = os.Stdout
cmd.Stderr = stderr cmd.Stderr = os.Stderr
cmd.Env = os.Environ() cmd.Env = os.Environ()
if err = cmd.Start(); err != nil { if err = cmd.Start(); err != nil {
@ -68,46 +66,37 @@ func main() {
var tags, format, output string var tags, format, output string
var concurrency int var concurrency int
flagSet := godog.FlagSet(&format, &tags, &defs, &sof, &noclr, &concurrency) flagSet := godog.FlagSet(colors.Colored(os.Stdout), &format, &tags, &defs, &sof, &noclr, &concurrency)
flagSet.BoolVar(&vers, "version", false, "Show current version.") flagSet.BoolVar(&vers, "version", false, "Show current version.")
flagSet.StringVar(&output, "o", "", "Build and output test runner executable to given target path.") flagSet.StringVar(&output, "o", "", "Build and output test runner executable to given target path.")
flagSet.StringVar(&output, "output", "", "Build and output test runner executable to given target path.") flagSet.StringVar(&output, "output", "", "Build and output test runner executable to given target path.")
err := flagSet.Parse(os.Args[1:]) if err := flagSet.Parse(os.Args[1:]); err != nil {
if err != nil { fmt.Fprintln(os.Stderr, err)
fmt.Fprintln(stderr, err)
os.Exit(1) os.Exit(1)
} }
if len(output) > 0 { if len(output) > 0 {
bin, err := filepath.Abs(output) bin, err := filepath.Abs(output)
if err != nil { if err != nil {
fmt.Fprintln(stderr, "could not locate absolute path for:", output, err) fmt.Fprintln(os.Stderr, "could not locate absolute path for:", output, err)
os.Exit(1) os.Exit(1)
} }
if err = godog.Build(bin); err != nil { if err = godog.Build(bin); err != nil {
fmt.Fprintln(stderr, "could not build binary at:", output, err) fmt.Fprintln(os.Stderr, "could not build binary at:", output, err)
os.Exit(1) os.Exit(1)
} }
os.Exit(0) os.Exit(0)
} }
if noclr {
stdout = noColorsWriter(stdout)
stderr = noColorsWriter(stderr)
} else {
stdout = createAnsiColorWriter(stdout)
stderr = createAnsiColorWriter(stderr)
}
if vers { if vers {
fmt.Fprintln(stdout, "Godog version is:", godog.Version) fmt.Fprintln(os.Stdout, "Godog version is:", godog.Version)
os.Exit(0) // should it be 0? os.Exit(0) // should it be 0?
} }
status, err := buildAndRun() status, err := buildAndRun()
if err != nil { if err != nil {
fmt.Fprintln(stderr, err) fmt.Fprintln(os.Stderr, err)
os.Exit(1) os.Exit(1)
} }
// it might be a case, that status might not be resolved // it might be a case, that status might not be resolved

Просмотреть файл

@ -4,7 +4,7 @@
// +build !windows // +build !windows
package main package colors
import "io" import "io"

Просмотреть файл

@ -4,7 +4,7 @@
// +build windows // +build windows
package main package colors
import ( import (
"bytes" "bytes"
@ -351,7 +351,7 @@ func isParameterChar(b byte) bool {
func (cw *ansiColorWriter) Write(p []byte) (int, error) { func (cw *ansiColorWriter) Write(p []byte) (int, error) {
r, nw, first, last := 0, 0, 0, 0 r, nw, first, last := 0, 0, 0, 0
if cw.mode != DiscardNonColorEscSeq { if cw.mode != discardNonColorEscSeq {
cw.state = outsideCsiCode cw.state = outsideCsiCode
cw.resetBuffer() cw.resetBuffer()
} }
@ -388,7 +388,7 @@ func (cw *ansiColorWriter) Write(p []byte) (int, error) {
} }
first = i + 1 first = i + 1
result := parseEscapeSequence(ch, cw.paramBuf.Bytes()) result := parseEscapeSequence(ch, cw.paramBuf.Bytes())
if result == noConsole || (cw.mode == OutputNonColorEscSeq && result == unknown) { if result == noConsole || (cw.mode == outputNonColorEscSeq && result == unknown) {
cw.paramBuf.WriteByte(ch) cw.paramBuf.WriteByte(ch)
nw, err := cw.flushBuffer() nw, err := cw.flushBuffer()
if err != nil { if err != nil {
@ -408,7 +408,7 @@ func (cw *ansiColorWriter) Write(p []byte) (int, error) {
} }
} }
if cw.mode != DiscardNonColorEscSeq || cw.state == outsideCsiCode { if cw.mode != discardNonColorEscSeq || cw.state == outsideCsiCode {
nw, err = cw.w.Write(p[first:len(p)]) nw, err = cw.w.Write(p[first:len(p)])
r += nw r += nw
} }

59
colors/colors.go Обычный файл
Просмотреть файл

@ -0,0 +1,59 @@
package colors
import (
"fmt"
"strings"
)
const ansiEscape = "\x1b"
// a color code type
type color int
// some ansi colors
const (
black color = iota + 30
red
green
yellow
blue
magenta
cyan
white
)
func colorize(s interface{}, c color) string {
return fmt.Sprintf("%s[%dm%v%s[0m", ansiEscape, c, s, ansiEscape)
}
type ColorFunc func(interface{}) string
func Bold(fn ColorFunc) ColorFunc {
return ColorFunc(func(input interface{}) string {
return strings.Replace(fn(input), ansiEscape+"[", ansiEscape+"[1;", 1)
})
}
func Green(s interface{}) string {
return colorize(s, green)
}
func Red(s interface{}) string {
return colorize(s, red)
}
func Cyan(s interface{}) string {
return colorize(s, cyan)
}
func Black(s interface{}) string {
return colorize(s, black)
}
func Yellow(s interface{}) string {
return colorize(s, yellow)
}
func White(s interface{}) string {
return colorize(s, white)
}

Просмотреть файл

@ -1,4 +1,4 @@
package main package colors
import ( import (
"bytes" "bytes"
@ -11,7 +11,7 @@ type noColors struct {
lastbuf bytes.Buffer lastbuf bytes.Buffer
} }
func noColorsWriter(w io.Writer) io.Writer { func Uncolored(w io.Writer) io.Writer {
return &noColors{out: w} return &noColors{out: w}
} }

Просмотреть файл

@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package main package colors
import "io" import "io"
@ -15,17 +15,17 @@ type outputMode int
// color escape sequence. // color escape sequence.
const ( const (
_ outputMode = iota _ outputMode = iota
DiscardNonColorEscSeq discardNonColorEscSeq
OutputNonColorEscSeq outputNonColorEscSeq
) )
// NewAnsiColorWriter creates and initializes a new ansiColorWriter // Colored creates and initializes a new ansiColorWriter
// using io.Writer w as its initial contents. // using io.Writer w as its initial contents.
// In the console of Windows, which change the foreground and background // In the console of Windows, which change the foreground and background
// colors of the text by the escape sequence. // colors of the text by the escape sequence.
// In the console of other systems, which writes to w all text. // In the console of other systems, which writes to w all text.
func createAnsiColorWriter(w io.Writer) io.Writer { func Colored(w io.Writer) io.Writer {
return createModeAnsiColorWriter(w, DiscardNonColorEscSeq) return createModeAnsiColorWriter(w, discardNonColorEscSeq)
} }
// NewModeAnsiColorWriter create and initializes a new ansiColorWriter // NewModeAnsiColorWriter create and initializes a new ansiColorWriter

Просмотреть файл

@ -3,31 +3,35 @@ package godog
import ( import (
"flag" "flag"
"fmt" "fmt"
"io"
"strings" "strings"
"github.com/DATA-DOG/godog/colors"
) )
var descFeaturesArgument = "Optional feature(s) to run. Can be:\n" + var descFeaturesArgument = "Optional feature(s) to run. Can be:\n" +
s(4) + "- dir " + cl("(features/)", yellow) + "\n" + s(4) + "- dir " + colors.Yellow("(features/)") + "\n" +
s(4) + "- feature " + cl("(*.feature)", yellow) + "\n" + s(4) + "- feature " + colors.Yellow("(*.feature)") + "\n" +
s(4) + "- scenario at specific line " + cl("(*.feature:10)", yellow) + "\n" + s(4) + "- scenario at specific line " + colors.Yellow("(*.feature:10)") + "\n" +
"If no feature paths are listed, suite tries " + cl("features", yellow) + " path by default.\n" "If no feature paths are listed, suite tries " + colors.Yellow("features") + " path by default.\n"
var descConcurrencyOption = "Run the test suite with concurrency level:\n" + var descConcurrencyOption = "Run the test suite with concurrency level:\n" +
s(4) + "- " + cl(`= 1`, yellow) + ": supports all types of formats.\n" + s(4) + "- " + colors.Yellow(`= 1`) + ": supports all types of formats.\n" +
s(4) + "- " + cl(`>= 2`, yellow) + ": only supports " + cl("progress", yellow) + ". Note, that\n" + s(4) + "- " + colors.Yellow(`>= 2`) + ": only supports " + colors.Yellow("progress") + ". Note, that\n" +
s(4) + "your context needs to support parallel execution." s(4) + "your context needs to support parallel execution."
var descTagsOption = "Filter scenarios by tags. Expression can be:\n" + var descTagsOption = "Filter scenarios by tags. Expression can be:\n" +
s(4) + "- " + cl(`"@wip"`, yellow) + ": run all scenarios with wip tag\n" + s(4) + "- " + colors.Yellow(`"@wip"`) + ": run all scenarios with wip tag\n" +
s(4) + "- " + cl(`"~@wip"`, yellow) + ": exclude all scenarios with wip tag\n" + s(4) + "- " + colors.Yellow(`"~@wip"`) + ": exclude all scenarios with wip tag\n" +
s(4) + "- " + cl(`"@wip && ~@new"`, yellow) + ": run wip scenarios, but exclude new\n" + s(4) + "- " + colors.Yellow(`"@wip && ~@new"`) + ": run wip scenarios, but exclude new\n" +
s(4) + "- " + cl(`"@wip,@undone"`, yellow) + ": run wip or undone scenarios" s(4) + "- " + colors.Yellow(`"@wip,@undone"`) + ": run wip or undone scenarios"
// FlagSet allows to manage flags by external suite runner // FlagSet allows to manage flags by external suite runner
func FlagSet(format, tags *string, defs, sof, noclr *bool, cr *int) *flag.FlagSet { func FlagSet(w io.Writer, format, tags *string, defs, sof, noclr *bool, cr *int) *flag.FlagSet {
descFormatOption := "How to format tests output. Available formats:\n" descFormatOption := "How to format tests output. Available formats:\n"
for _, f := range formatters { // @TODO: sort by name
descFormatOption += s(4) + "- " + cl(f.name, yellow) + ": " + f.description + "\n" for name, desc := range AvailableFormatters() {
descFormatOption += s(4) + "- " + colors.Yellow(name) + ": " + desc + "\n"
} }
descFormatOption = strings.TrimSpace(descFormatOption) descFormatOption = strings.TrimSpace(descFormatOption)
@ -42,7 +46,7 @@ func FlagSet(format, tags *string, defs, sof, noclr *bool, cr *int) *flag.FlagSe
set.BoolVar(defs, "d", false, "Print all available step definitions.") set.BoolVar(defs, "d", false, "Print all available step definitions.")
set.BoolVar(sof, "stop-on-failure", false, "Stop processing on first failed scenario.") set.BoolVar(sof, "stop-on-failure", false, "Stop processing on first failed scenario.")
set.BoolVar(noclr, "no-colors", false, "Disable ansi colors.") set.BoolVar(noclr, "no-colors", false, "Disable ansi colors.")
set.Usage = usage(set) set.Usage = usage(set, w)
return set return set
} }
@ -66,7 +70,7 @@ func (f *flagged) name() string {
return name return name
} }
func usage(set *flag.FlagSet) func() { func usage(set *flag.FlagSet, w io.Writer) func() {
return func() { return func() {
var list []*flagged var list []*flagged
var longest int var longest int
@ -102,7 +106,7 @@ func usage(set *flag.FlagSet) func() {
opt := func(name, desc string) string { opt := func(name, desc string) string {
var ret []string var ret []string
lines := strings.Split(desc, "\n") lines := strings.Split(desc, "\n")
ret = append(ret, s(2)+cl(name, green)+s(longest+2-len(name))+lines[0]) ret = append(ret, s(2)+colors.Green(name)+s(longest+2-len(name))+lines[0])
if len(lines) > 1 { if len(lines) > 1 {
for _, ln := range lines[1:] { for _, ln := range lines[1:] {
ret = append(ret, s(2)+s(longest+2)+ln) ret = append(ret, s(2)+s(longest+2)+ln)
@ -112,22 +116,22 @@ func usage(set *flag.FlagSet) func() {
} }
// --- GENERAL --- // --- GENERAL ---
fmt.Println(cl("Usage:", yellow)) fmt.Fprintln(w, colors.Yellow("Usage:"))
fmt.Printf(s(2) + "godog [options] [<features>]\n\n") fmt.Printf(s(2) + "godog [options] [<features>]\n\n")
// description // description
fmt.Println("Builds a test package and runs given feature files.") fmt.Fprintln(w, "Builds a test package and runs given feature files.")
fmt.Printf("Command should be run from the directory of tested package and contain buildable go source.\n\n") fmt.Fprintf(w, "Command should be run from the directory of tested package and contain buildable go source.\n\n")
// --- ARGUMENTS --- // --- ARGUMENTS ---
fmt.Println(cl("Arguments:", yellow)) fmt.Fprintln(w, colors.Yellow("Arguments:"))
// --> features // --> features
fmt.Println(opt("features", descFeaturesArgument)) fmt.Fprintln(w, opt("features", descFeaturesArgument))
// --- OPTIONS --- // --- OPTIONS ---
fmt.Println(cl("Options:", yellow)) fmt.Fprintln(w, colors.Yellow("Options:"))
for _, f := range list { for _, f := range list {
fmt.Println(opt(f.name(), f.descr)) fmt.Fprintln(w, opt(f.name(), f.descr))
} }
fmt.Println("") fmt.Fprintln(w, "")
} }
} }

34
fmt.go
Просмотреть файл

@ -11,6 +11,7 @@ import (
"time" "time"
"unicode" "unicode"
"github.com/DATA-DOG/godog/colors"
"github.com/DATA-DOG/godog/gherkin" "github.com/DATA-DOG/godog/gherkin"
) )
@ -78,6 +79,17 @@ func Format(name, description string, f FormatterFunc) {
}) })
} }
// AvailableFormatters gives a map of all
// formatters registered with their name as key
// and description as value
func AvailableFormatters() map[string]string {
fmts := make(map[string]string, len(formatters))
for _, f := range formatters {
fmts[f.name] = f.description
}
return fmts
}
// Formatter is an interface for feature runner // Formatter is an interface for feature runner
// output summary presentation. // output summary presentation.
// //
@ -111,7 +123,7 @@ const (
pending pending
) )
func (st stepType) clr() color { func (st stepType) clr() colors.ColorFunc {
switch st { switch st {
case passed: case passed:
return green return green
@ -267,28 +279,28 @@ func (f *basefmt) Summary() {
var steps, parts, scenarios []string var steps, parts, scenarios []string
nsteps := len(f.passed) + len(f.failed) + len(f.skipped) + len(f.undefined) + len(f.pending) nsteps := len(f.passed) + len(f.failed) + len(f.skipped) + len(f.undefined) + len(f.pending)
if len(f.passed) > 0 { if len(f.passed) > 0 {
steps = append(steps, cl(fmt.Sprintf("%d passed", len(f.passed)), green)) steps = append(steps, green(fmt.Sprintf("%d passed", len(f.passed))))
} }
if len(f.failed) > 0 { if len(f.failed) > 0 {
passed -= len(f.failed) passed -= len(f.failed)
parts = append(parts, cl(fmt.Sprintf("%d failed", len(f.failed)), red)) parts = append(parts, red(fmt.Sprintf("%d failed", len(f.failed))))
steps = append(steps, parts[len(parts)-1]) steps = append(steps, parts[len(parts)-1])
} }
if len(f.pending) > 0 { if len(f.pending) > 0 {
passed -= len(f.pending) passed -= len(f.pending)
parts = append(parts, cl(fmt.Sprintf("%d pending", len(f.pending)), yellow)) parts = append(parts, yellow(fmt.Sprintf("%d pending", len(f.pending))))
steps = append(steps, cl(fmt.Sprintf("%d pending", len(f.pending)), yellow)) steps = append(steps, yellow(fmt.Sprintf("%d pending", len(f.pending))))
} }
if len(f.undefined) > 0 { if len(f.undefined) > 0 {
passed -= undefined passed -= undefined
parts = append(parts, cl(fmt.Sprintf("%d undefined", undefined), yellow)) parts = append(parts, yellow(fmt.Sprintf("%d undefined", undefined)))
steps = append(steps, cl(fmt.Sprintf("%d undefined", len(f.undefined)), yellow)) steps = append(steps, yellow(fmt.Sprintf("%d undefined", len(f.undefined))))
} }
if len(f.skipped) > 0 { if len(f.skipped) > 0 {
steps = append(steps, cl(fmt.Sprintf("%d skipped", len(f.skipped)), cyan)) steps = append(steps, cyan(fmt.Sprintf("%d skipped", len(f.skipped))))
} }
if passed > 0 { if passed > 0 {
scenarios = append(scenarios, cl(fmt.Sprintf("%d passed", passed), green)) scenarios = append(scenarios, green(fmt.Sprintf("%d passed", passed)))
} }
scenarios = append(scenarios, parts...) scenarios = append(scenarios, parts...)
elapsed := time.Since(f.started) elapsed := time.Since(f.started)
@ -308,8 +320,8 @@ func (f *basefmt) Summary() {
fmt.Fprintln(f.out, elapsed) fmt.Fprintln(f.out, elapsed)
if text := f.snippets(); text != "" { if text := f.snippets(); text != "" {
fmt.Fprintln(f.out, cl("\nYou can implement step definitions for undefined steps with these snippets:", yellow)) fmt.Fprintln(f.out, yellow("\nYou can implement step definitions for undefined steps with these snippets:"))
fmt.Fprintln(f.out, cl(text, yellow)) fmt.Fprintln(f.out, yellow(text))
} }
} }

Просмотреть файл

@ -9,6 +9,7 @@ import (
"time" "time"
"unicode/utf8" "unicode/utf8"
"github.com/DATA-DOG/godog/colors"
"github.com/DATA-DOG/godog/gherkin" "github.com/DATA-DOG/godog/gherkin"
) )
@ -57,7 +58,7 @@ func (f *pretty) Feature(ft *gherkin.Feature, p string, c []byte) {
fmt.Fprintln(f.out, "") fmt.Fprintln(f.out, "")
} }
f.features = append(f.features, &feature{Path: p, Feature: ft}) f.features = append(f.features, &feature{Path: p, Feature: ft})
fmt.Fprintln(f.out, bcl(ft.Keyword+": ", white)+ft.Name) fmt.Fprintln(f.out, whiteb(ft.Keyword+": ")+ft.Name)
if strings.TrimSpace(ft.Description) != "" { if strings.TrimSpace(ft.Description) != "" {
for _, line := range strings.Split(ft.Description, "\n") { for _, line := range strings.Split(ft.Description, "\n") {
fmt.Fprintln(f.out, s(f.indent)+strings.TrimSpace(line)) fmt.Fprintln(f.out, s(f.indent)+strings.TrimSpace(line))
@ -110,7 +111,7 @@ func (f *pretty) Summary() {
} }
} }
if len(failedScenarios) > 0 { if len(failedScenarios) > 0 {
fmt.Fprintln(f.out, "\n--- "+cl("Failed scenarios:", red)+"\n") fmt.Fprintln(f.out, "\n--- "+red("Failed scenarios:")+"\n")
var unique []string var unique []string
for _, fail := range failedScenarios { for _, fail := range failedScenarios {
var found bool var found bool
@ -126,7 +127,7 @@ func (f *pretty) Summary() {
} }
for _, fail := range unique { for _, fail := range unique {
fmt.Fprintln(f.out, " "+cl(fail, red)) fmt.Fprintln(f.out, " "+red(fail))
} }
} }
f.basefmt.Summary() f.basefmt.Summary()
@ -134,7 +135,7 @@ func (f *pretty) Summary() {
func (f *pretty) printOutlineExample(outline *gherkin.ScenarioOutline) { func (f *pretty) printOutlineExample(outline *gherkin.ScenarioOutline) {
var msg string var msg string
clr := green var clr colors.ColorFunc
ex := outline.Examples[f.outlineNumExample] ex := outline.Examples[f.outlineNumExample]
example, hasExamples := examples(ex) example, hasExamples := examples(ex)
@ -154,7 +155,7 @@ func (f *pretty) printOutlineExample(outline *gherkin.ScenarioOutline) {
clr = res.typ.clr() clr = res.typ.clr()
case res.typ == undefined || res.typ == pending: case res.typ == undefined || res.typ == pending:
clr = res.typ.clr() clr = res.typ.clr()
case res.typ == skipped && clr == green: case res.typ == skipped && clr == nil:
clr = cyan clr = cyan
} }
if printSteps { if printSteps {
@ -166,20 +167,20 @@ func (f *pretty) printOutlineExample(outline *gherkin.ScenarioOutline) {
var pos int var pos int
for i := 0; i < len(m); i++ { for i := 0; i < len(m); i++ {
pair := m[i] pair := m[i]
text += cl(ostep.Text[pos:pair[0]], cyan) text += cyan(ostep.Text[pos:pair[0]])
text += bcl(ostep.Text[pair[0]:pair[1]], cyan) text += cyanb(ostep.Text[pair[0]:pair[1]])
pos = pair[1] pos = pair[1]
} }
text += cl(ostep.Text[pos:len(ostep.Text)], cyan) text += cyan(ostep.Text[pos:len(ostep.Text)])
} else { } else {
text = cl(ostep.Text, cyan) text = cyan(ostep.Text)
} }
text += s(f.commentPos-f.length(ostep)+1) + cl(fmt.Sprintf("# %s", res.def.definitionID()), black) text += s(f.commentPos-f.length(ostep)+1) + black(fmt.Sprintf("# %s", res.def.definitionID()))
} else { } else {
text = cl(ostep.Text, cyan) text = cyan(ostep.Text)
} }
// print the step outline // print the step outline
fmt.Fprintln(f.out, s(f.indent*2)+cl(strings.TrimSpace(ostep.Keyword), cyan)+" "+text) fmt.Fprintln(f.out, s(f.indent*2)+cyan(strings.TrimSpace(ostep.Keyword))+" "+text)
} }
} }
@ -188,48 +189,51 @@ func (f *pretty) printOutlineExample(outline *gherkin.ScenarioOutline) {
// an example table header // an example table header
if firstExample { if firstExample {
fmt.Fprintln(f.out, "") fmt.Fprintln(f.out, "")
fmt.Fprintln(f.out, s(f.indent*2)+bcl(example.Keyword+": ", white)+example.Name) fmt.Fprintln(f.out, s(f.indent*2)+whiteb(example.Keyword+": ")+example.Name)
for i, cell := range example.TableHeader.Cells { for i, cell := range example.TableHeader.Cells {
cells[i] = cl(cell.Value, cyan) + s(max[i]-len(cell.Value)) cells[i] = cyan(cell.Value) + s(max[i]-len(cell.Value))
} }
fmt.Fprintln(f.out, s(f.indent*3)+"| "+strings.Join(cells, " | ")+" |") fmt.Fprintln(f.out, s(f.indent*3)+"| "+strings.Join(cells, " | ")+" |")
} }
if clr == nil {
clr = green
}
// an example table row // an example table row
row := example.TableBody[len(example.TableBody)-f.outlineNumExamples] row := example.TableBody[len(example.TableBody)-f.outlineNumExamples]
for i, cell := range row.Cells { for i, cell := range row.Cells {
cells[i] = cl(cell.Value, clr) + s(max[i]-len(cell.Value)) cells[i] = clr(cell.Value) + s(max[i]-len(cell.Value))
} }
fmt.Fprintln(f.out, s(f.indent*3)+"| "+strings.Join(cells, " | ")+" |") fmt.Fprintln(f.out, s(f.indent*3)+"| "+strings.Join(cells, " | ")+" |")
// if there is an error // if there is an error
if msg != "" { if msg != "" {
fmt.Fprintln(f.out, s(f.indent*4)+bcl(msg, red)) fmt.Fprintln(f.out, s(f.indent*4)+redb(msg))
} }
} }
func (f *pretty) printStep(step *gherkin.Step, def *StepDef, c color) { func (f *pretty) printStep(step *gherkin.Step, def *StepDef, c colors.ColorFunc) {
text := s(f.indent*2) + cl(strings.TrimSpace(step.Keyword), c) + " " text := s(f.indent*2) + c(strings.TrimSpace(step.Keyword)) + " "
switch { switch {
case def != nil: case def != nil:
if m := def.Expr.FindStringSubmatchIndex(step.Text)[2:]; len(m) > 0 { if m := def.Expr.FindStringSubmatchIndex(step.Text)[2:]; len(m) > 0 {
var pos, i int var pos, i int
for pos, i = 0, 0; i < len(m); i++ { for pos, i = 0, 0; i < len(m); i++ {
if math.Mod(float64(i), 2) == 0 { if math.Mod(float64(i), 2) == 0 {
text += cl(step.Text[pos:m[i]], c) text += c(step.Text[pos:m[i]])
} else { } else {
text += bcl(step.Text[pos:m[i]], c) text += c(step.Text[pos:m[i]])
} }
pos = m[i] pos = m[i]
} }
text += cl(step.Text[pos:len(step.Text)], c) text += c(step.Text[pos:len(step.Text)])
} else { } else {
text += cl(step.Text, c) text += c(step.Text)
} }
text += s(f.commentPos-f.length(step)+1) + cl(fmt.Sprintf("# %s", def.definitionID()), black) text += s(f.commentPos-f.length(step)+1) + black(fmt.Sprintf("# %s", def.definitionID()))
default: default:
text += cl(step.Text, c) text += c(step.Text)
} }
fmt.Fprintln(f.out, text) fmt.Fprintln(f.out, text)
@ -239,13 +243,13 @@ func (f *pretty) printStep(step *gherkin.Step, def *StepDef, c color) {
case *gherkin.DocString: case *gherkin.DocString:
var ct string var ct string
if len(t.ContentType) > 0 { if len(t.ContentType) > 0 {
ct = " " + cl(t.ContentType, c) ct = " " + c(t.ContentType)
} }
fmt.Fprintln(f.out, s(f.indent*3)+cl(t.Delimitter, c)+ct) fmt.Fprintln(f.out, s(f.indent*3)+c(t.Delimitter)+ct)
for _, ln := range strings.Split(t.Content, "\n") { for _, ln := range strings.Split(t.Content, "\n") {
fmt.Fprintln(f.out, s(f.indent*3)+cl(ln, c)) fmt.Fprintln(f.out, s(f.indent*3)+c(ln))
} }
fmt.Fprintln(f.out, s(f.indent*3)+cl(t.Delimitter, c)) fmt.Fprintln(f.out, s(f.indent*3)+c(t.Delimitter))
} }
} }
@ -255,7 +259,7 @@ func (f *pretty) printStepKind(res *stepResult) {
// first background step // first background step
case f.bgSteps > 0 && f.bgSteps == len(f.feature.Background.Steps): case f.bgSteps > 0 && f.bgSteps == len(f.feature.Background.Steps):
f.commentPos = f.longestStep(f.feature.Background.Steps, f.length(f.feature.Background)) f.commentPos = f.longestStep(f.feature.Background.Steps, f.length(f.feature.Background))
fmt.Fprintln(f.out, "\n"+s(f.indent)+bcl(f.feature.Background.Keyword+": "+f.feature.Background.Name, white)) fmt.Fprintln(f.out, "\n"+s(f.indent)+whiteb(f.feature.Background.Keyword+": "+f.feature.Background.Name))
f.bgSteps-- f.bgSteps--
// subsequent background steps // subsequent background steps
case f.bgSteps > 0: case f.bgSteps > 0:
@ -270,7 +274,7 @@ func (f *pretty) printStepKind(res *stepResult) {
f.commentPos = bgLen f.commentPos = bgLen
} }
} }
text := s(f.indent) + bcl(f.scenario.Keyword+": ", white) + f.scenario.Name text := s(f.indent) + whiteb(f.scenario.Keyword+": ") + f.scenario.Name
text += s(f.commentPos-f.length(f.scenario)+1) + f.line(f.scenario.Location) text += s(f.commentPos-f.length(f.scenario)+1) + f.line(f.scenario.Location)
fmt.Fprintln(f.out, "\n"+text) fmt.Fprintln(f.out, "\n"+text)
f.scenarioKeyword = true f.scenarioKeyword = true
@ -289,7 +293,7 @@ func (f *pretty) printStepKind(res *stepResult) {
f.commentPos = bgLen f.commentPos = bgLen
} }
} }
text := s(f.indent) + bcl(f.outline.Keyword+": ", white) + f.outline.Name text := s(f.indent) + whiteb(f.outline.Keyword+": ") + f.outline.Name
text += s(f.commentPos-f.length(f.outline)+1) + f.line(f.outline.Location) text += s(f.commentPos-f.length(f.outline)+1) + f.line(f.outline.Location)
fmt.Fprintln(f.out, "\n"+text) fmt.Fprintln(f.out, "\n"+text)
f.scenarioKeyword = true f.scenarioKeyword = true
@ -304,22 +308,22 @@ func (f *pretty) printStepKind(res *stepResult) {
f.printStep(res.step, res.def, res.typ.clr()) f.printStep(res.step, res.def, res.typ.clr())
if res.err != nil { if res.err != nil {
fmt.Fprintln(f.out, s(f.indent*2)+bcl(res.err, red)) fmt.Fprintln(f.out, s(f.indent*2)+redb(res.err))
} }
if res.typ == pending { if res.typ == pending {
fmt.Fprintln(f.out, s(f.indent*3)+cl("TODO: write pending definition", yellow)) fmt.Fprintln(f.out, s(f.indent*3)+yellow("TODO: write pending definition"))
} }
} }
// print table with aligned table cells // print table with aligned table cells
func (f *pretty) printTable(t *gherkin.DataTable, c color) { func (f *pretty) printTable(t *gherkin.DataTable, c colors.ColorFunc) {
var l = longest(t) var l = longest(t)
var cols = make([]string, len(t.Rows[0].Cells)) var cols = make([]string, len(t.Rows[0].Cells))
for _, row := range t.Rows { for _, row := range t.Rows {
for i, cell := range row.Cells { for i, cell := range row.Cells {
cols[i] = cell.Value + s(l[i]-len(cell.Value)) cols[i] = cell.Value + s(l[i]-len(cell.Value))
} }
fmt.Fprintln(f.out, s(f.indent*3)+cl("| "+strings.Join(cols, " | ")+" |", c)) fmt.Fprintln(f.out, s(f.indent*3)+c("| "+strings.Join(cols, " | ")+" |"))
} }
} }
@ -383,7 +387,7 @@ func (f *pretty) longestStep(steps []*gherkin.Step, base int) int {
// a line number representation in feature file // a line number representation in feature file
func (f *pretty) line(loc *gherkin.Location) string { func (f *pretty) line(loc *gherkin.Location) string {
return cl(fmt.Sprintf("# %s:%d", f.features[len(f.features)-1].Path, loc.Line), black) return black(fmt.Sprintf("# %s:%d", f.features[len(f.features)-1].Path, loc.Line))
} }
func (f *pretty) length(node interface{}) int { func (f *pretty) length(node interface{}) int {

Просмотреть файл

@ -48,18 +48,18 @@ func (f *progress) Summary() {
left := math.Mod(float64(f.steps), float64(f.stepsPerRow)) left := math.Mod(float64(f.steps), float64(f.stepsPerRow))
if left != 0 { if left != 0 {
if int(f.steps) > f.stepsPerRow { if int(f.steps) > f.stepsPerRow {
fmt.Printf(s(f.stepsPerRow-int(left)) + fmt.Sprintf(" %d\n", f.steps)) fmt.Fprintf(f.out, s(f.stepsPerRow-int(left))+fmt.Sprintf(" %d\n", f.steps))
} else { } else {
fmt.Printf(" %d\n", f.steps) fmt.Fprintf(f.out, " %d\n", f.steps)
} }
} }
fmt.Fprintln(f.out, "") fmt.Fprintln(f.out, "")
if len(f.failed) > 0 { if len(f.failed) > 0 {
fmt.Fprintln(f.out, "\n--- "+cl("Failed steps:", red)+"\n") fmt.Fprintln(f.out, "\n--- "+red("Failed steps:")+"\n")
for _, fail := range f.failed { for _, fail := range f.failed {
fmt.Fprintln(f.out, s(4)+cl(fail.step.Keyword+" "+fail.step.Text, red)+cl(" # "+fail.line(), black)) fmt.Fprintln(f.out, s(4)+red(fail.step.Keyword+" "+fail.step.Text)+black(" # "+fail.line()))
fmt.Fprintln(f.out, s(6)+cl("Error: ", red)+bcl(fail.err, red)+"\n") fmt.Fprintln(f.out, s(6)+red("Error: ")+redb(fail.err)+"\n")
} }
} }
f.basefmt.Summary() f.basefmt.Summary()
@ -68,15 +68,15 @@ func (f *progress) Summary() {
func (f *progress) step(res *stepResult) { func (f *progress) step(res *stepResult) {
switch res.typ { switch res.typ {
case passed: case passed:
fmt.Print(cl(".", green)) fmt.Fprint(f.out, green("."))
case skipped: case skipped:
fmt.Print(cl("-", cyan)) fmt.Fprint(f.out, cyan("-"))
case failed: case failed:
fmt.Print(cl("F", red)) fmt.Fprint(f.out, red("F"))
case undefined: case undefined:
fmt.Print(cl("U", yellow)) fmt.Fprint(f.out, yellow("U"))
case pending: case pending:
fmt.Print(cl("P", yellow)) fmt.Fprint(f.out, yellow("P"))
} }
f.steps++ f.steps++
if math.Mod(float64(f.steps), float64(f.stepsPerRow)) == 0 { if math.Mod(float64(f.steps), float64(f.stepsPerRow)) == 0 {

Просмотреть файл

@ -387,7 +387,7 @@ func (s *Suite) printStepDefinitions() {
n := utf8.RuneCountInString(def.Expr.String()) n := utf8.RuneCountInString(def.Expr.String())
location := def.definitionID() location := def.definitionID()
spaces := strings.Repeat(" ", longest-n) spaces := strings.Repeat(" ", longest-n)
fmt.Println(cl(def.Expr.String(), yellow)+spaces, cl("# "+location, black)) fmt.Println(yellow(def.Expr.String())+spaces, black("# "+location))
} }
if len(s.steps) == 0 { if len(s.steps) == 0 {
fmt.Println("there were no contexts registered, could not find any step definition..") fmt.Println("there were no contexts registered, could not find any step definition..")

Просмотреть файл

@ -4,37 +4,22 @@ import (
"fmt" "fmt"
"os" "os"
"strings" "strings"
"github.com/DATA-DOG/godog/colors"
) )
// empty struct value takes no space allocation // empty struct value takes no space allocation
type void struct{} type void struct{}
// a color code type var red = colors.Red
type color int var redb = colors.Bold(colors.Red)
var green = colors.Green
const ansiEscape = "\x1b" var black = colors.Black
var blackb = colors.Bold(colors.Black)
// some ansi colors var yellow = colors.Yellow
const ( var cyan = colors.Cyan
black color = iota + 30 var cyanb = colors.Bold(colors.Cyan)
red var whiteb = colors.Bold(colors.White)
green
yellow
blue
magenta
cyan
white
)
// colorizes foreground s with color c
func cl(s interface{}, c color) string {
return fmt.Sprintf("%s[%dm%v%s[0m", ansiEscape, c, s, ansiEscape)
}
// colorizes foreground s with bold color c
func bcl(s interface{}, c color) string {
return fmt.Sprintf("%s[1;%dm%v%s[0m", ansiEscape, c, s, ansiEscape)
}
// repeats a space n times // repeats a space n times
func s(n int) string { func s(n int) string {