refactor colorization into the separate package
Этот коммит содержится в:
родитель
a4b5349b94
коммит
115923c97f
12 изменённых файлов: 195 добавлений и 142 удалений
|
@ -12,14 +12,12 @@ import (
|
|||
"syscall"
|
||||
|
||||
"github.com/DATA-DOG/godog"
|
||||
"github.com/DATA-DOG/godog/colors"
|
||||
)
|
||||
|
||||
var statusMatch = regexp.MustCompile("^exit status (\\d+)")
|
||||
var parsedStatus int
|
||||
|
||||
var stdout = io.Writer(os.Stdout)
|
||||
var stderr = statusOutputFilter(os.Stderr)
|
||||
|
||||
func buildAndRun() (int, error) {
|
||||
var status int
|
||||
|
||||
|
@ -36,8 +34,8 @@ func buildAndRun() (int, error) {
|
|||
defer os.Remove(bin)
|
||||
|
||||
cmd := exec.Command(bin, os.Args[1:]...)
|
||||
cmd.Stdout = stdout
|
||||
cmd.Stderr = stderr
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Env = os.Environ()
|
||||
|
||||
if err = cmd.Start(); err != nil {
|
||||
|
@ -68,46 +66,37 @@ func main() {
|
|||
var tags, format, output string
|
||||
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.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.")
|
||||
|
||||
err := flagSet.Parse(os.Args[1:])
|
||||
if err != nil {
|
||||
fmt.Fprintln(stderr, err)
|
||||
if err := flagSet.Parse(os.Args[1:]); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if len(output) > 0 {
|
||||
bin, err := filepath.Abs(output)
|
||||
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)
|
||||
}
|
||||
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(0)
|
||||
}
|
||||
|
||||
if noclr {
|
||||
stdout = noColorsWriter(stdout)
|
||||
stderr = noColorsWriter(stderr)
|
||||
} else {
|
||||
stdout = createAnsiColorWriter(stdout)
|
||||
stderr = createAnsiColorWriter(stderr)
|
||||
}
|
||||
|
||||
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?
|
||||
}
|
||||
|
||||
status, err := buildAndRun()
|
||||
if err != nil {
|
||||
fmt.Fprintln(stderr, err)
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
// it might be a case, that status might not be resolved
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
// +build !windows
|
||||
|
||||
package main
|
||||
package colors
|
||||
|
||||
import "io"
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
// +build windows
|
||||
|
||||
package main
|
||||
package colors
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
@ -351,7 +351,7 @@ func isParameterChar(b byte) bool {
|
|||
|
||||
func (cw *ansiColorWriter) Write(p []byte) (int, error) {
|
||||
r, nw, first, last := 0, 0, 0, 0
|
||||
if cw.mode != DiscardNonColorEscSeq {
|
||||
if cw.mode != discardNonColorEscSeq {
|
||||
cw.state = outsideCsiCode
|
||||
cw.resetBuffer()
|
||||
}
|
||||
|
@ -388,7 +388,7 @@ func (cw *ansiColorWriter) Write(p []byte) (int, error) {
|
|||
}
|
||||
first = i + 1
|
||||
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)
|
||||
nw, err := cw.flushBuffer()
|
||||
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)])
|
||||
r += nw
|
||||
}
|
59
colors/colors.go
Обычный файл
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 (
|
||||
"bytes"
|
||||
|
@ -11,7 +11,7 @@ type noColors struct {
|
|||
lastbuf bytes.Buffer
|
||||
}
|
||||
|
||||
func noColorsWriter(w io.Writer) io.Writer {
|
||||
func Uncolored(w io.Writer) io.Writer {
|
||||
return &noColors{out: w}
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
package colors
|
||||
|
||||
import "io"
|
||||
|
||||
|
@ -15,17 +15,17 @@ type outputMode int
|
|||
// color escape sequence.
|
||||
const (
|
||||
_ outputMode = iota
|
||||
DiscardNonColorEscSeq
|
||||
OutputNonColorEscSeq
|
||||
discardNonColorEscSeq
|
||||
outputNonColorEscSeq
|
||||
)
|
||||
|
||||
// NewAnsiColorWriter creates and initializes a new ansiColorWriter
|
||||
// Colored creates and initializes a new ansiColorWriter
|
||||
// using io.Writer w as its initial contents.
|
||||
// In the console of Windows, which change the foreground and background
|
||||
// colors of the text by the escape sequence.
|
||||
// In the console of other systems, which writes to w all text.
|
||||
func createAnsiColorWriter(w io.Writer) io.Writer {
|
||||
return createModeAnsiColorWriter(w, DiscardNonColorEscSeq)
|
||||
func Colored(w io.Writer) io.Writer {
|
||||
return createModeAnsiColorWriter(w, discardNonColorEscSeq)
|
||||
}
|
||||
|
||||
// NewModeAnsiColorWriter create and initializes a new ansiColorWriter
|
52
flags.go
52
flags.go
|
@ -3,31 +3,35 @@ package godog
|
|||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/DATA-DOG/godog/colors"
|
||||
)
|
||||
|
||||
var descFeaturesArgument = "Optional feature(s) to run. Can be:\n" +
|
||||
s(4) + "- dir " + cl("(features/)", yellow) + "\n" +
|
||||
s(4) + "- feature " + cl("(*.feature)", yellow) + "\n" +
|
||||
s(4) + "- scenario at specific line " + cl("(*.feature:10)", yellow) + "\n" +
|
||||
"If no feature paths are listed, suite tries " + cl("features", yellow) + " path by default.\n"
|
||||
s(4) + "- dir " + colors.Yellow("(features/)") + "\n" +
|
||||
s(4) + "- feature " + colors.Yellow("(*.feature)") + "\n" +
|
||||
s(4) + "- scenario at specific line " + colors.Yellow("(*.feature:10)") + "\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" +
|
||||
s(4) + "- " + cl(`= 1`, yellow) + ": supports all types of formats.\n" +
|
||||
s(4) + "- " + cl(`>= 2`, yellow) + ": only supports " + cl("progress", yellow) + ". Note, that\n" +
|
||||
s(4) + "- " + colors.Yellow(`= 1`) + ": supports all types of formats.\n" +
|
||||
s(4) + "- " + colors.Yellow(`>= 2`) + ": only supports " + colors.Yellow("progress") + ". Note, that\n" +
|
||||
s(4) + "your context needs to support parallel execution."
|
||||
|
||||
var descTagsOption = "Filter scenarios by tags. Expression can be:\n" +
|
||||
s(4) + "- " + cl(`"@wip"`, yellow) + ": run all scenarios with wip tag\n" +
|
||||
s(4) + "- " + cl(`"~@wip"`, yellow) + ": exclude all scenarios with wip tag\n" +
|
||||
s(4) + "- " + cl(`"@wip && ~@new"`, yellow) + ": run wip scenarios, but exclude new\n" +
|
||||
s(4) + "- " + cl(`"@wip,@undone"`, yellow) + ": run wip or undone scenarios"
|
||||
s(4) + "- " + colors.Yellow(`"@wip"`) + ": run all scenarios with wip tag\n" +
|
||||
s(4) + "- " + colors.Yellow(`"~@wip"`) + ": exclude all scenarios with wip tag\n" +
|
||||
s(4) + "- " + colors.Yellow(`"@wip && ~@new"`) + ": run wip scenarios, but exclude new\n" +
|
||||
s(4) + "- " + colors.Yellow(`"@wip,@undone"`) + ": run wip or undone scenarios"
|
||||
|
||||
// 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"
|
||||
for _, f := range formatters {
|
||||
descFormatOption += s(4) + "- " + cl(f.name, yellow) + ": " + f.description + "\n"
|
||||
// @TODO: sort by name
|
||||
for name, desc := range AvailableFormatters() {
|
||||
descFormatOption += s(4) + "- " + colors.Yellow(name) + ": " + desc + "\n"
|
||||
}
|
||||
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(sof, "stop-on-failure", false, "Stop processing on first failed scenario.")
|
||||
set.BoolVar(noclr, "no-colors", false, "Disable ansi colors.")
|
||||
set.Usage = usage(set)
|
||||
set.Usage = usage(set, w)
|
||||
return set
|
||||
}
|
||||
|
||||
|
@ -66,7 +70,7 @@ func (f *flagged) name() string {
|
|||
return name
|
||||
}
|
||||
|
||||
func usage(set *flag.FlagSet) func() {
|
||||
func usage(set *flag.FlagSet, w io.Writer) func() {
|
||||
return func() {
|
||||
var list []*flagged
|
||||
var longest int
|
||||
|
@ -102,7 +106,7 @@ func usage(set *flag.FlagSet) func() {
|
|||
opt := func(name, desc string) string {
|
||||
var ret []string
|
||||
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 {
|
||||
for _, ln := range lines[1:] {
|
||||
ret = append(ret, s(2)+s(longest+2)+ln)
|
||||
|
@ -112,22 +116,22 @@ func usage(set *flag.FlagSet) func() {
|
|||
}
|
||||
|
||||
// --- GENERAL ---
|
||||
fmt.Println(cl("Usage:", yellow))
|
||||
fmt.Fprintln(w, colors.Yellow("Usage:"))
|
||||
fmt.Printf(s(2) + "godog [options] [<features>]\n\n")
|
||||
// description
|
||||
fmt.Println("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.Fprintln(w, "Builds a test package and runs given feature files.")
|
||||
fmt.Fprintf(w, "Command should be run from the directory of tested package and contain buildable go source.\n\n")
|
||||
|
||||
// --- ARGUMENTS ---
|
||||
fmt.Println(cl("Arguments:", yellow))
|
||||
fmt.Fprintln(w, colors.Yellow("Arguments:"))
|
||||
// --> features
|
||||
fmt.Println(opt("features", descFeaturesArgument))
|
||||
fmt.Fprintln(w, opt("features", descFeaturesArgument))
|
||||
|
||||
// --- OPTIONS ---
|
||||
fmt.Println(cl("Options:", yellow))
|
||||
fmt.Fprintln(w, colors.Yellow("Options:"))
|
||||
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
34
fmt.go
|
@ -11,6 +11,7 @@ import (
|
|||
"time"
|
||||
"unicode"
|
||||
|
||||
"github.com/DATA-DOG/godog/colors"
|
||||
"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
|
||||
// output summary presentation.
|
||||
//
|
||||
|
@ -111,7 +123,7 @@ const (
|
|||
pending
|
||||
)
|
||||
|
||||
func (st stepType) clr() color {
|
||||
func (st stepType) clr() colors.ColorFunc {
|
||||
switch st {
|
||||
case passed:
|
||||
return green
|
||||
|
@ -267,28 +279,28 @@ func (f *basefmt) Summary() {
|
|||
var steps, parts, scenarios []string
|
||||
nsteps := len(f.passed) + len(f.failed) + len(f.skipped) + len(f.undefined) + len(f.pending)
|
||||
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 {
|
||||
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])
|
||||
}
|
||||
if len(f.pending) > 0 {
|
||||
passed -= len(f.pending)
|
||||
parts = append(parts, cl(fmt.Sprintf("%d pending", len(f.pending)), yellow))
|
||||
steps = append(steps, cl(fmt.Sprintf("%d pending", len(f.pending)), yellow))
|
||||
parts = append(parts, yellow(fmt.Sprintf("%d pending", len(f.pending))))
|
||||
steps = append(steps, yellow(fmt.Sprintf("%d pending", len(f.pending))))
|
||||
}
|
||||
if len(f.undefined) > 0 {
|
||||
passed -= undefined
|
||||
parts = append(parts, cl(fmt.Sprintf("%d undefined", undefined), yellow))
|
||||
steps = append(steps, cl(fmt.Sprintf("%d undefined", len(f.undefined)), yellow))
|
||||
parts = append(parts, yellow(fmt.Sprintf("%d undefined", undefined)))
|
||||
steps = append(steps, yellow(fmt.Sprintf("%d undefined", len(f.undefined))))
|
||||
}
|
||||
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 {
|
||||
scenarios = append(scenarios, cl(fmt.Sprintf("%d passed", passed), green))
|
||||
scenarios = append(scenarios, green(fmt.Sprintf("%d passed", passed)))
|
||||
}
|
||||
scenarios = append(scenarios, parts...)
|
||||
elapsed := time.Since(f.started)
|
||||
|
@ -308,8 +320,8 @@ func (f *basefmt) Summary() {
|
|||
fmt.Fprintln(f.out, elapsed)
|
||||
|
||||
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, cl(text, yellow))
|
||||
fmt.Fprintln(f.out, yellow("\nYou can implement step definitions for undefined steps with these snippets:"))
|
||||
fmt.Fprintln(f.out, yellow(text))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/DATA-DOG/godog/colors"
|
||||
"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, "")
|
||||
}
|
||||
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) != "" {
|
||||
for _, line := range strings.Split(ft.Description, "\n") {
|
||||
fmt.Fprintln(f.out, s(f.indent)+strings.TrimSpace(line))
|
||||
|
@ -110,7 +111,7 @@ func (f *pretty) Summary() {
|
|||
}
|
||||
}
|
||||
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
|
||||
for _, fail := range failedScenarios {
|
||||
var found bool
|
||||
|
@ -126,7 +127,7 @@ func (f *pretty) Summary() {
|
|||
}
|
||||
|
||||
for _, fail := range unique {
|
||||
fmt.Fprintln(f.out, " "+cl(fail, red))
|
||||
fmt.Fprintln(f.out, " "+red(fail))
|
||||
}
|
||||
}
|
||||
f.basefmt.Summary()
|
||||
|
@ -134,7 +135,7 @@ func (f *pretty) Summary() {
|
|||
|
||||
func (f *pretty) printOutlineExample(outline *gherkin.ScenarioOutline) {
|
||||
var msg string
|
||||
clr := green
|
||||
var clr colors.ColorFunc
|
||||
|
||||
ex := outline.Examples[f.outlineNumExample]
|
||||
example, hasExamples := examples(ex)
|
||||
|
@ -154,7 +155,7 @@ func (f *pretty) printOutlineExample(outline *gherkin.ScenarioOutline) {
|
|||
clr = res.typ.clr()
|
||||
case res.typ == undefined || res.typ == pending:
|
||||
clr = res.typ.clr()
|
||||
case res.typ == skipped && clr == green:
|
||||
case res.typ == skipped && clr == nil:
|
||||
clr = cyan
|
||||
}
|
||||
if printSteps {
|
||||
|
@ -166,20 +167,20 @@ func (f *pretty) printOutlineExample(outline *gherkin.ScenarioOutline) {
|
|||
var pos int
|
||||
for i := 0; i < len(m); i++ {
|
||||
pair := m[i]
|
||||
text += cl(ostep.Text[pos:pair[0]], cyan)
|
||||
text += bcl(ostep.Text[pair[0]:pair[1]], cyan)
|
||||
text += cyan(ostep.Text[pos:pair[0]])
|
||||
text += cyanb(ostep.Text[pair[0]:pair[1]])
|
||||
pos = pair[1]
|
||||
}
|
||||
text += cl(ostep.Text[pos:len(ostep.Text)], cyan)
|
||||
text += cyan(ostep.Text[pos:len(ostep.Text)])
|
||||
} 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 {
|
||||
text = cl(ostep.Text, cyan)
|
||||
text = cyan(ostep.Text)
|
||||
}
|
||||
// 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
|
||||
if firstExample {
|
||||
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 {
|
||||
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, " | ")+" |")
|
||||
}
|
||||
|
||||
if clr == nil {
|
||||
clr = green
|
||||
}
|
||||
// an example table row
|
||||
row := example.TableBody[len(example.TableBody)-f.outlineNumExamples]
|
||||
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, " | ")+" |")
|
||||
|
||||
// if there is an error
|
||||
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) {
|
||||
text := s(f.indent*2) + cl(strings.TrimSpace(step.Keyword), c) + " "
|
||||
func (f *pretty) printStep(step *gherkin.Step, def *StepDef, c colors.ColorFunc) {
|
||||
text := s(f.indent*2) + c(strings.TrimSpace(step.Keyword)) + " "
|
||||
switch {
|
||||
case def != nil:
|
||||
if m := def.Expr.FindStringSubmatchIndex(step.Text)[2:]; len(m) > 0 {
|
||||
var pos, i int
|
||||
for pos, i = 0, 0; i < len(m); i++ {
|
||||
if math.Mod(float64(i), 2) == 0 {
|
||||
text += cl(step.Text[pos:m[i]], c)
|
||||
text += c(step.Text[pos:m[i]])
|
||||
} else {
|
||||
text += bcl(step.Text[pos:m[i]], c)
|
||||
text += c(step.Text[pos:m[i]])
|
||||
}
|
||||
pos = m[i]
|
||||
}
|
||||
text += cl(step.Text[pos:len(step.Text)], c)
|
||||
text += c(step.Text[pos:len(step.Text)])
|
||||
} 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:
|
||||
text += cl(step.Text, c)
|
||||
text += c(step.Text)
|
||||
}
|
||||
|
||||
fmt.Fprintln(f.out, text)
|
||||
|
@ -239,13 +243,13 @@ func (f *pretty) printStep(step *gherkin.Step, def *StepDef, c color) {
|
|||
case *gherkin.DocString:
|
||||
var ct string
|
||||
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") {
|
||||
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
|
||||
case f.bgSteps > 0 && f.bgSteps == len(f.feature.Background.Steps):
|
||||
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--
|
||||
// subsequent background steps
|
||||
case f.bgSteps > 0:
|
||||
|
@ -270,7 +274,7 @@ func (f *pretty) printStepKind(res *stepResult) {
|
|||
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)
|
||||
fmt.Fprintln(f.out, "\n"+text)
|
||||
f.scenarioKeyword = true
|
||||
|
@ -289,7 +293,7 @@ func (f *pretty) printStepKind(res *stepResult) {
|
|||
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)
|
||||
fmt.Fprintln(f.out, "\n"+text)
|
||||
f.scenarioKeyword = true
|
||||
|
@ -304,22 +308,22 @@ func (f *pretty) printStepKind(res *stepResult) {
|
|||
|
||||
f.printStep(res.step, res.def, res.typ.clr())
|
||||
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 {
|
||||
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
|
||||
func (f *pretty) printTable(t *gherkin.DataTable, c color) {
|
||||
func (f *pretty) printTable(t *gherkin.DataTable, c colors.ColorFunc) {
|
||||
var l = longest(t)
|
||||
var cols = make([]string, len(t.Rows[0].Cells))
|
||||
for _, row := range t.Rows {
|
||||
for i, cell := range row.Cells {
|
||||
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
|
||||
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 {
|
||||
|
|
|
@ -48,18 +48,18 @@ 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))
|
||||
fmt.Fprintf(f.out, s(f.stepsPerRow-int(left))+fmt.Sprintf(" %d\n", f.steps))
|
||||
} else {
|
||||
fmt.Printf(" %d\n", f.steps)
|
||||
fmt.Fprintf(f.out, " %d\n", f.steps)
|
||||
}
|
||||
}
|
||||
fmt.Fprintln(f.out, "")
|
||||
|
||||
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 {
|
||||
fmt.Fprintln(f.out, s(4)+cl(fail.step.Keyword+" "+fail.step.Text, red)+cl(" # "+fail.line(), black))
|
||||
fmt.Fprintln(f.out, s(6)+cl("Error: ", red)+bcl(fail.err, red)+"\n")
|
||||
fmt.Fprintln(f.out, s(4)+red(fail.step.Keyword+" "+fail.step.Text)+black(" # "+fail.line()))
|
||||
fmt.Fprintln(f.out, s(6)+red("Error: ")+redb(fail.err)+"\n")
|
||||
}
|
||||
}
|
||||
f.basefmt.Summary()
|
||||
|
@ -68,15 +68,15 @@ func (f *progress) Summary() {
|
|||
func (f *progress) step(res *stepResult) {
|
||||
switch res.typ {
|
||||
case passed:
|
||||
fmt.Print(cl(".", green))
|
||||
fmt.Fprint(f.out, green("."))
|
||||
case skipped:
|
||||
fmt.Print(cl("-", cyan))
|
||||
fmt.Fprint(f.out, cyan("-"))
|
||||
case failed:
|
||||
fmt.Print(cl("F", red))
|
||||
fmt.Fprint(f.out, red("F"))
|
||||
case undefined:
|
||||
fmt.Print(cl("U", yellow))
|
||||
fmt.Fprint(f.out, yellow("U"))
|
||||
case pending:
|
||||
fmt.Print(cl("P", yellow))
|
||||
fmt.Fprint(f.out, yellow("P"))
|
||||
}
|
||||
f.steps++
|
||||
if math.Mod(float64(f.steps), float64(f.stepsPerRow)) == 0 {
|
||||
|
|
2
suite.go
2
suite.go
|
@ -387,7 +387,7 @@ func (s *Suite) printStepDefinitions() {
|
|||
n := utf8.RuneCountInString(def.Expr.String())
|
||||
location := def.definitionID()
|
||||
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 {
|
||||
fmt.Println("there were no contexts registered, could not find any step definition..")
|
||||
|
|
37
utils.go
37
utils.go
|
@ -4,37 +4,22 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/DATA-DOG/godog/colors"
|
||||
)
|
||||
|
||||
// empty struct value takes no space allocation
|
||||
type void struct{}
|
||||
|
||||
// a color code type
|
||||
type color int
|
||||
|
||||
const ansiEscape = "\x1b"
|
||||
|
||||
// some ansi colors
|
||||
const (
|
||||
black color = iota + 30
|
||||
red
|
||||
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)
|
||||
}
|
||||
var red = colors.Red
|
||||
var redb = colors.Bold(colors.Red)
|
||||
var green = colors.Green
|
||||
var black = colors.Black
|
||||
var blackb = colors.Bold(colors.Black)
|
||||
var yellow = colors.Yellow
|
||||
var cyan = colors.Cyan
|
||||
var cyanb = colors.Bold(colors.Cyan)
|
||||
var whiteb = colors.Bold(colors.White)
|
||||
|
||||
// repeats a space n times
|
||||
func s(n int) string {
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче