Created internal packages for formatters, storage and models

Этот коммит содержится в:
Fredrik Lönnblad 2020-06-27 23:18:30 +02:00
родитель 28ad994ad1
коммит 150c400163
105 изменённых файлов: 1493 добавлений и 872 удалений

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

@ -36,7 +36,7 @@ commands:
go_test:
description: "Run go test"
steps:
- run: sed -i 's#github.com/cucumber/godog_test#_test#g' formatter-tests/*/*
- run: sed -i 's#github.com/cucumber/godog/internal/formatters_test#/internal/formatters_test#g' internal/formatters/formatter-tests/*/*
- run: go test -v -race -coverprofile=coverage.txt -covermode=atomic ./...
godog:
description: "Run godog"

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

@ -10,8 +10,12 @@ import (
"time"
"github.com/cucumber/godog/colors"
"github.com/cucumber/godog/internal/utils"
)
// repeats a space n times
var s = utils.S
var descFeaturesArgument = "Optional feature(s) to run. Can be:\n" +
s(4) + "- dir " + colors.Yellow("(features/)") + "\n" +
s(4) + "- feature " + colors.Yellow("(*.feature)") + "\n" +

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

@ -8,6 +8,7 @@ import (
"testing"
"github.com/cucumber/godog/colors"
"github.com/cucumber/godog/internal/formatters"
)
func TestFlagsShouldRandomizeAndGenerateSeed(t *testing.T) {
@ -61,7 +62,7 @@ func TestFlagsUsageShouldIncludeFormatDescriptons(t *testing.T) {
output := colors.Uncolored(&buf)
// register some custom formatter
Format("custom", "custom format description", junitFunc)
Format("custom", "custom format description", formatters.JUnitFormatterFunc)
var opt Options
flags := FlagSet(&opt)

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

@ -6,28 +6,18 @@ import (
"strings"
"unicode/utf8"
"github.com/cucumber/messages-go/v10"
"github.com/cucumber/godog/colors"
"github.com/cucumber/godog/formatters"
internal_fmt "github.com/cucumber/godog/internal/formatters"
"github.com/cucumber/godog/internal/models"
"github.com/cucumber/godog/internal/storage"
)
type registeredFormatter struct {
name string
description string
fmt FormatterFunc
}
var formatters []*registeredFormatter
// FindFmt searches available formatters registered
// and returns FormaterFunc matched by given
// format name or nil otherwise
func FindFmt(name string) FormatterFunc {
for _, el := range formatters {
if el.name == name {
return el.fmt
}
}
return nil
return formatters.FindFmt(name)
}
// Format registers a feature suite output
@ -35,24 +25,14 @@ func FindFmt(name string) FormatterFunc {
// FormatterFunc constructor function, to initialize
// formatter with the output recorder.
func Format(name, description string, f FormatterFunc) {
formatters = append(formatters, &registeredFormatter{
name: name,
fmt: f,
description: description,
})
formatters.Format(name, description, f)
}
// 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
return formatters.AvailableFormatters()
}
// Formatter is an interface for feature runner
@ -62,32 +42,17 @@ func AvailableFormatters() map[string]string {
// suite results in different ways. These new
// formatters needs to be registered with a
// godog.Format function call
type Formatter interface {
TestRunStarted()
Feature(*messages.GherkinDocument, string, []byte)
Pickle(*messages.Pickle)
Defined(*messages.Pickle, *messages.Pickle_PickleStep, *StepDefinition)
Failed(*messages.Pickle, *messages.Pickle_PickleStep, *StepDefinition, error)
Passed(*messages.Pickle, *messages.Pickle_PickleStep, *StepDefinition)
Skipped(*messages.Pickle, *messages.Pickle_PickleStep, *StepDefinition)
Undefined(*messages.Pickle, *messages.Pickle_PickleStep, *StepDefinition)
Pending(*messages.Pickle, *messages.Pickle_PickleStep, *StepDefinition)
Summary()
}
type Formatter = formatters.Formatter
type storageFormatter interface {
setStorage(*storage)
SetStorage(*storage.Storage)
}
// FormatterFunc builds a formatter with given
// suite name and io.Writer to record output
type FormatterFunc func(string, io.Writer) Formatter
type FormatterFunc = formatters.FormatterFunc
func isLastStep(pickle *messages.Pickle, step *messages.Pickle_PickleStep) bool {
return pickle.Steps[len(pickle.Steps)-1].Id == step.Id
}
func printStepDefinitions(steps []*StepDefinition, w io.Writer) {
func printStepDefinitions(steps []*models.StepDefinition, w io.Writer) {
var longest int
for _, def := range steps {
n := utf8.RuneCountInString(def.Expr.String())
@ -98,9 +63,11 @@ func printStepDefinitions(steps []*StepDefinition, w io.Writer) {
for _, def := range steps {
n := utf8.RuneCountInString(def.Expr.String())
location := def.definitionID()
location := internal_fmt.DefinitionID(def)
spaces := strings.Repeat(" ", longest-n)
fmt.Fprintln(w, yellow(def.Expr.String())+spaces, blackb("# "+location))
fmt.Fprintln(w,
colors.Yellow(def.Expr.String())+spaces,
colors.Bold(colors.Black)("# "+location))
}
if len(steps) == 0 {

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

@ -0,0 +1,79 @@
package formatters
import (
"io"
"github.com/cucumber/messages-go/v10"
"github.com/cucumber/godog/internal/models"
)
type registeredFormatter struct {
name string
description string
fmt FormatterFunc
}
var registeredFormatters []*registeredFormatter
// FindFmt searches available formatters registered
// and returns FormaterFunc matched by given
// format name or nil otherwise
func FindFmt(name string) FormatterFunc {
for _, el := range registeredFormatters {
if el.name == name {
return el.fmt
}
}
return nil
}
// Format registers a feature suite output
// formatter by given name, description and
// FormatterFunc constructor function, to initialize
// formatter with the output recorder.
func Format(name, description string, f FormatterFunc) {
registeredFormatters = append(registeredFormatters, &registeredFormatter{
name: name,
fmt: f,
description: description,
})
}
// 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(registeredFormatters))
for _, f := range registeredFormatters {
fmts[f.name] = f.description
}
return fmts
}
// Formatter is an interface for feature runner
// output summary presentation.
//
// New formatters may be created to represent
// suite results in different ways. These new
// formatters needs to be registered with a
// godog.Format function call
type Formatter interface {
TestRunStarted()
Feature(*messages.GherkinDocument, string, []byte)
Pickle(*messages.Pickle)
Defined(*messages.Pickle, *messages.Pickle_PickleStep, *models.StepDefinition)
Failed(*messages.Pickle, *messages.Pickle_PickleStep, *models.StepDefinition, error)
Passed(*messages.Pickle, *messages.Pickle_PickleStep, *models.StepDefinition)
Skipped(*messages.Pickle, *messages.Pickle_PickleStep, *models.StepDefinition)
Undefined(*messages.Pickle, *messages.Pickle_PickleStep, *models.StepDefinition)
Pending(*messages.Pickle, *messages.Pickle_PickleStep, *models.StepDefinition)
Summary()
}
// FormatterFunc builds a formatter with given
// suite name and io.Writer to record output
type FormatterFunc func(string, io.Writer) Formatter

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

@ -0,0 +1,65 @@
package formatters_test
import (
"io"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/cucumber/godog"
)
func Test_FindFmt(t *testing.T) {
cases := map[string]bool{
"cucumber": true,
"events": true,
"junit": true,
"pretty": true,
"progress": true,
"unknown": false,
"undef": false,
}
for name, expected := range cases {
t.Run(
name,
func(t *testing.T) {
actual := godog.FindFmt(name)
if expected {
assert.NotNilf(t, actual, "expected %s formatter should be available", name)
} else {
assert.Nilf(t, actual, "expected %s formatter should be available", name)
}
},
)
}
}
func Test_AvailableFormatters(t *testing.T) {
expected := map[string]string{
"cucumber": "Produces cucumber JSON format output.",
"events": "Produces JSON event stream, based on spec: 0.1.0.",
"junit": "Prints junit compatible xml to stdout",
"pretty": "Prints every feature with runtime statuses.",
"progress": "Prints a character per step.",
}
actual := godog.AvailableFormatters()
assert.Equal(t, expected, actual)
}
func Test_Format(t *testing.T) {
actual := godog.FindFmt("Test_Format")
require.Nil(t, actual)
godog.Format("Test_Format", "...", testFormatterFunc)
actual = godog.FindFmt("Test_Format")
assert.NotNil(t, actual)
}
func testFormatterFunc(suiteName string, out io.Writer) godog.Formatter {
return nil
}

104
internal/formatters/fmt.go Обычный файл
Просмотреть файл

@ -0,0 +1,104 @@
package formatters
import (
"fmt"
"os"
"path/filepath"
"regexp"
"runtime"
"strconv"
"strings"
"github.com/cucumber/godog/colors"
"github.com/cucumber/godog/internal/models"
"github.com/cucumber/godog/internal/utils"
"github.com/cucumber/messages-go/v10"
)
var (
red = colors.Red
redb = colors.Bold(colors.Red)
green = colors.Green
blackb = colors.Bold(colors.Black)
yellow = colors.Yellow
cyan = colors.Cyan
cyanb = colors.Bold(colors.Cyan)
whiteb = colors.Bold(colors.White)
)
// repeats a space n times
var s = utils.S
var (
passed = models.Passed
failed = models.Failed
skipped = models.Skipped
undefined = models.Undefined
pending = models.Pending
)
type sortFeaturesByName []*models.Feature
func (s sortFeaturesByName) Len() int { return len(s) }
func (s sortFeaturesByName) Less(i, j int) bool { return s[i].Feature.Name < s[j].Feature.Name }
func (s sortFeaturesByName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
type sortPicklesByID []*messages.Pickle
func (s sortPicklesByID) Len() int { return len(s) }
func (s sortPicklesByID) Less(i, j int) bool {
iID := mustConvertStringToInt(s[i].Id)
jID := mustConvertStringToInt(s[j].Id)
return iID < jID
}
func (s sortPicklesByID) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
type sortPickleStepResultsByPickleStepID []models.PickleStepResult
func (s sortPickleStepResultsByPickleStepID) Len() int { return len(s) }
func (s sortPickleStepResultsByPickleStepID) Less(i, j int) bool {
iID := mustConvertStringToInt(s[i].PickleStepID)
jID := mustConvertStringToInt(s[j].PickleStepID)
return iID < jID
}
func (s sortPickleStepResultsByPickleStepID) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func mustConvertStringToInt(s string) int {
i, err := strconv.Atoi(s)
if err != nil {
panic(err)
}
return i
}
// DefinitionID ...
func DefinitionID(sd *models.StepDefinition) string {
ptr := sd.HandlerValue.Pointer()
f := runtime.FuncForPC(ptr)
file, line := f.FileLine(ptr)
dir := filepath.Dir(file)
fn := strings.Replace(f.Name(), dir, "", -1)
var parts []string
for _, gr := range matchFuncDefRef.FindAllStringSubmatch(fn, -1) {
parts = append(parts, strings.Trim(gr[1], "_."))
}
if len(parts) > 0 {
// case when suite is a structure with methods
fn = strings.Join(parts, ".")
} else {
// case when steps are just plain funcs
fn = strings.Trim(fn, "_.")
}
if pkg := os.Getenv("GODOG_TESTED_PACKAGE"); len(pkg) > 0 {
fn = strings.Replace(fn, pkg, "", 1)
fn = strings.TrimLeft(fn, ".")
fn = strings.Replace(fn, "..", ".", -1)
}
return fmt.Sprintf("%s:%d -> %s", filepath.Base(file), line, fn)
}
var matchFuncDefRef = regexp.MustCompile(`\(([^\)]+)\)`)

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

@ -1,4 +1,4 @@
package godog
package formatters
import (
"bytes"
@ -14,14 +14,20 @@ import (
"github.com/cucumber/messages-go/v10"
"github.com/cucumber/godog/colors"
"github.com/cucumber/godog/formatters"
"github.com/cucumber/godog/internal/models"
"github.com/cucumber/godog/internal/storage"
"github.com/cucumber/godog/internal/utils"
)
func baseFmtFunc(suite string, out io.Writer) Formatter {
return newBaseFmt(suite, out)
// BaseFormatterFunc implements the FormatterFunc for the base formatter
func BaseFormatterFunc(suite string, out io.Writer) formatters.Formatter {
return NewBaseFmt(suite, out)
}
func newBaseFmt(suite string, out io.Writer) *basefmt {
return &basefmt{
// NewBaseFmt creates a new base formatter
func NewBaseFmt(suite string, out io.Writer) *Basefmt {
return &Basefmt{
suiteName: suite,
indent: 2,
out: out,
@ -29,47 +35,63 @@ func newBaseFmt(suite string, out io.Writer) *basefmt {
}
}
type basefmt struct {
// Basefmt ...
type Basefmt struct {
suiteName string
out io.Writer
indent int
storage *storage
storage *storage.Storage
lock *sync.Mutex
}
func (f *basefmt) setStorage(st *storage) {
// SetStorage ...
func (f *Basefmt) SetStorage(st *storage.Storage) {
f.lock.Lock()
defer f.lock.Unlock()
f.storage = st
}
func (f *basefmt) TestRunStarted() {}
func (f *basefmt) Feature(ft *messages.GherkinDocument, p string, c []byte) {}
func (f *basefmt) Pickle(p *messages.Pickle) {}
func (f *basefmt) Defined(*messages.Pickle, *messages.Pickle_PickleStep, *StepDefinition) {}
func (f *basefmt) Passed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
}
func (f *basefmt) Skipped(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
}
func (f *basefmt) Undefined(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
}
func (f *basefmt) Failed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition, err error) {
}
func (f *basefmt) Pending(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
// TestRunStarted ...
func (f *Basefmt) TestRunStarted() {}
// Feature ...
func (f *Basefmt) Feature(*messages.GherkinDocument, string, []byte) {}
// Pickle ...
func (f *Basefmt) Pickle(*messages.Pickle) {}
// Defined ...
func (f *Basefmt) Defined(*messages.Pickle, *messages.Pickle_PickleStep, *models.StepDefinition) {}
// Passed ...
func (f *Basefmt) Passed(*messages.Pickle, *messages.Pickle_PickleStep, *models.StepDefinition) {}
// Skipped ...
func (f *Basefmt) Skipped(*messages.Pickle, *messages.Pickle_PickleStep, *models.StepDefinition) {}
// Undefined ...
func (f *Basefmt) Undefined(*messages.Pickle, *messages.Pickle_PickleStep, *models.StepDefinition) {}
// Failed ...
func (f *Basefmt) Failed(*messages.Pickle, *messages.Pickle_PickleStep, *models.StepDefinition, error) {
}
func (f *basefmt) Summary() {
// Pending ...
func (f *Basefmt) Pending(*messages.Pickle, *messages.Pickle_PickleStep, *models.StepDefinition) {}
// Summary ...
func (f *Basefmt) Summary() {
var totalSc, passedSc, undefinedSc int
var totalSt, passedSt, failedSt, skippedSt, pendingSt, undefinedSt int
pickleResults := f.storage.mustGetPickleResults()
pickleResults := f.storage.MustGetPickleResults()
for _, pr := range pickleResults {
var prStatus stepResultStatus
var prStatus models.StepResultStatus
totalSc++
pickleStepResults := f.storage.mustGetPickleStepResultsByPickleID(pr.PickleID)
pickleStepResults := f.storage.MustGetPickleStepResultsByPickleID(pr.PickleID)
if len(pickleStepResults) == 0 {
prStatus = undefined
@ -130,8 +152,8 @@ func (f *basefmt) Summary() {
}
scenarios = append(scenarios, parts...)
testRunStartedAt := f.storage.mustGetTestRunStarted().StartedAt
elapsed := timeNowFunc().Sub(testRunStartedAt)
testRunStartedAt := f.storage.MustGetTestRunStarted().StartedAt
elapsed := utils.TimeNowFunc().Sub(testRunStartedAt)
fmt.Fprintln(f.out, "")
@ -161,15 +183,16 @@ func (f *basefmt) Summary() {
fmt.Fprintln(f.out, "Randomized with seed:", colors.Yellow(seed))
}
if text := f.snippets(); text != "" {
if text := f.Snippets(); text != "" {
fmt.Fprintln(f.out, "")
fmt.Fprintln(f.out, yellow("You can implement step definitions for undefined steps with these snippets:"))
fmt.Fprintln(f.out, yellow(text))
}
}
func (f *basefmt) snippets() string {
undefinedStepResults := f.storage.mustGetPickleStepResultsByStatus(undefined)
// Snippets ...
func (f *Basefmt) Snippets() string {
undefinedStepResults := f.storage.MustGetPickleStepResultsByStatus(undefined)
if len(undefinedStepResults) == 0 {
return ""
}
@ -178,12 +201,12 @@ func (f *basefmt) snippets() string {
var snips []undefinedSnippet
// build snippets
for _, u := range undefinedStepResults {
pickleStep := f.storage.mustGetPickleStep(u.PickleStepID)
pickleStep := f.storage.MustGetPickleStep(u.PickleStepID)
steps := []string{pickleStep.Text}
arg := pickleStep.Argument
if u.def != nil {
steps = u.def.undefined
if u.Def != nil {
steps = u.Def.Undefined
arg = nil
}
for _, step := range steps {

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

@ -1,4 +1,4 @@
package godog_test
package formatters_test
import (
"bytes"

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

@ -1,4 +1,4 @@
package godog
package formatters
/*
The specification for the formatting originated from https://www.relishapp.com/cucumber/cucumber/docs/formatters/json-output-formatter.
@ -19,22 +19,26 @@ import (
"strings"
"github.com/cucumber/messages-go/v10"
"github.com/cucumber/godog/formatters"
"github.com/cucumber/godog/internal/models"
)
func init() {
Format("cucumber", "Produces cucumber JSON format output.", cucumberFunc)
formatters.Format("cucumber", "Produces cucumber JSON format output.", CucumberFormatterFunc)
}
func cucumberFunc(suite string, out io.Writer) Formatter {
return &cukefmt{basefmt: newBaseFmt(suite, out)}
// CucumberFormatterFunc implements the FormatterFunc for the cucumber formatter
func CucumberFormatterFunc(suite string, out io.Writer) formatters.Formatter {
return &cukefmt{Basefmt: NewBaseFmt(suite, out)}
}
type cukefmt struct {
*basefmt
*Basefmt
}
func (f *cukefmt) Summary() {
features := f.storage.mustGetFeatures()
features := f.storage.MustGetFeatures()
res := f.buildCukeFeatures(features)
@ -46,15 +50,15 @@ func (f *cukefmt) Summary() {
fmt.Fprintf(f.out, "%s\n", string(dat))
}
func (f *cukefmt) buildCukeFeatures(features []*feature) (res []cukeFeatureJSON) {
func (f *cukefmt) buildCukeFeatures(features []*models.Feature) (res []CukeFeatureJSON) {
sort.Sort(sortFeaturesByName(features))
res = make([]cukeFeatureJSON, len(features))
res = make([]CukeFeatureJSON, len(features))
for idx, feat := range features {
cukeFeature := buildCukeFeature(feat)
pickles := f.storage.mustGetPickles(feat.Uri)
pickles := f.storage.MustGetPickles(feat.Uri)
sort.Sort(sortPicklesByID(pickles))
cukeFeature.Elements = f.buildCukeElements(pickles)
@ -75,8 +79,8 @@ func (f *cukefmt) buildCukeElements(pickles []*messages.Pickle) (res []cukeEleme
res = make([]cukeElement, len(pickles))
for idx, pickle := range pickles {
pickleResult := f.storage.mustGetPickleResult(pickle.Id)
pickleStepResults := f.storage.mustGetPickleStepResultsByPickleID(pickle.Id)
pickleResult := f.storage.MustGetPickleResult(pickle.Id)
pickleStepResults := f.storage.MustGetPickleStepResultsByPickleID(pickle.Id)
cukeElement := f.buildCukeElement(pickle)
@ -88,12 +92,14 @@ func (f *cukefmt) buildCukeElements(pickles []*messages.Pickle) (res []cukeEleme
for jdx, stepResult := range pickleStepResults {
cukeStep := f.buildCukeStep(pickle, stepResult)
stepResultFinishedAt := stepResult.finishedAt
stepResultFinishedAt := stepResult.FinishedAt
d := int(stepResultFinishedAt.Sub(stepStartedAt).Nanoseconds())
stepStartedAt = stepResultFinishedAt
cukeStep.Result.Duration = &d
if stepResult.Status == undefined || stepResult.Status == pending || stepResult.Status == skipped {
if stepResult.Status == undefined ||
stepResult.Status == pending ||
stepResult.Status == skipped {
cukeStep.Result.Duration = nil
}
@ -157,7 +163,8 @@ type cukeElement struct {
Steps []cukeStep `json:"steps,omitempty"`
}
type cukeFeatureJSON struct {
// CukeFeatureJSON ...
type CukeFeatureJSON struct {
URI string `json:"uri"`
ID string `json:"id"`
Keyword string `json:"keyword"`
@ -169,8 +176,8 @@ type cukeFeatureJSON struct {
Elements []cukeElement `json:"elements,omitempty"`
}
func buildCukeFeature(feat *feature) cukeFeatureJSON {
cukeFeature := cukeFeatureJSON{
func buildCukeFeature(feat *models.Feature) CukeFeatureJSON {
cukeFeature := CukeFeatureJSON{
URI: feat.Uri,
ID: makeCukeID(feat.Feature.Name),
Keyword: feat.Feature.Keyword,
@ -195,8 +202,8 @@ func buildCukeFeature(feat *feature) cukeFeatureJSON {
}
func (f *cukefmt) buildCukeElement(pickle *messages.Pickle) (cukeElement cukeElement) {
feature := f.storage.mustGetFeature(pickle.Uri)
scenario := feature.findScenario(pickle.AstNodeIds[0])
feature := f.storage.MustGetFeature(pickle.Uri)
scenario := feature.FindScenario(pickle.AstNodeIds[0])
cukeElement.Name = pickle.Name
cukeElement.Line = int(scenario.Location.Line)
@ -214,7 +221,7 @@ func (f *cukefmt) buildCukeElement(pickle *messages.Pickle) (cukeElement cukeEle
return
}
example, _ := feature.findExample(pickle.AstNodeIds[1])
example, _ := feature.FindExample(pickle.AstNodeIds[1])
for _, tag := range example.Tags {
tag := cukeTag{Line: int(tag.Location.Line), Name: tag.Name}
@ -238,14 +245,14 @@ func (f *cukefmt) buildCukeElement(pickle *messages.Pickle) (cukeElement cukeEle
return cukeElement
}
func (f *cukefmt) buildCukeStep(pickle *messages.Pickle, stepResult pickleStepResult) (cukeStep cukeStep) {
feature := f.storage.mustGetFeature(pickle.Uri)
pickleStep := f.storage.mustGetPickleStep(stepResult.PickleStepID)
step := feature.findStep(pickleStep.AstNodeIds[0])
func (f *cukefmt) buildCukeStep(pickle *messages.Pickle, stepResult models.PickleStepResult) (cukeStep cukeStep) {
feature := f.storage.MustGetFeature(pickle.Uri)
pickleStep := f.storage.MustGetPickleStep(stepResult.PickleStepID)
step := feature.FindStep(pickleStep.AstNodeIds[0])
line := step.Location.Line
if len(pickle.AstNodeIds) == 2 {
_, row := feature.findExample(pickle.AstNodeIds[1])
_, row := feature.FindExample(pickle.AstNodeIds[1])
line = row.Location.Line
}
@ -273,13 +280,13 @@ func (f *cukefmt) buildCukeStep(pickle *messages.Pickle, stepResult pickleStepRe
}
}
if stepResult.def != nil {
cukeStep.Match.Location = strings.Split(stepResult.def.definitionID(), " ")[0]
if stepResult.Def != nil {
cukeStep.Match.Location = strings.Split(DefinitionID(stepResult.Def), " ")[0]
}
cukeStep.Result.Status = stepResult.Status.String()
if stepResult.err != nil {
cukeStep.Result.Error = stepResult.err.Error()
if stepResult.Err != nil {
cukeStep.Result.Error = stepResult.Err.Error()
}
if stepResult.Status == undefined || stepResult.Status == pending {

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

@ -1,4 +1,4 @@
package godog
package formatters
import (
"encoding/json"
@ -6,24 +6,29 @@ import (
"io"
"github.com/cucumber/messages-go/v10"
"github.com/cucumber/godog/formatters"
"github.com/cucumber/godog/internal/models"
"github.com/cucumber/godog/internal/utils"
)
const nanoSec = 1000000
const spec = "0.1.0"
func init() {
Format("events", fmt.Sprintf("Produces JSON event stream, based on spec: %s.", spec), eventsFunc)
formatters.Format("events", fmt.Sprintf("Produces JSON event stream, based on spec: %s.", spec), EventsFormatterFunc)
}
func eventsFunc(suite string, out io.Writer) Formatter {
return &events{basefmt: newBaseFmt(suite, out)}
// EventsFormatterFunc implements the FormatterFunc for the events formatter
func EventsFormatterFunc(suite string, out io.Writer) formatters.Formatter {
return &eventsFormatter{Basefmt: NewBaseFmt(suite, out)}
}
type events struct {
*basefmt
type eventsFormatter struct {
*Basefmt
}
func (f *events) event(ev interface{}) {
func (f *eventsFormatter) event(ev interface{}) {
data, err := json.Marshal(ev)
if err != nil {
panic(fmt.Sprintf("failed to marshal stream event: %+v - %v", ev, err))
@ -31,8 +36,8 @@ func (f *events) event(ev interface{}) {
fmt.Fprintln(f.out, string(data))
}
func (f *events) Pickle(pickle *messages.Pickle) {
f.basefmt.Pickle(pickle)
func (f *eventsFormatter) Pickle(pickle *messages.Pickle) {
f.Basefmt.Pickle(pickle)
f.lock.Lock()
defer f.lock.Unlock()
@ -44,7 +49,7 @@ func (f *events) Pickle(pickle *messages.Pickle) {
}{
"TestCaseStarted",
f.scenarioLocation(pickle),
timeNowFunc().UnixNano() / nanoSec,
utils.TimeNowFunc().UnixNano() / nanoSec,
})
if len(pickle.Steps) == 0 {
@ -58,14 +63,14 @@ func (f *events) Pickle(pickle *messages.Pickle) {
}{
"TestCaseFinished",
f.scenarioLocation(pickle),
timeNowFunc().UnixNano() / nanoSec,
utils.TimeNowFunc().UnixNano() / nanoSec,
"undefined",
})
}
}
func (f *events) TestRunStarted() {
f.basefmt.TestRunStarted()
func (f *eventsFormatter) TestRunStarted() {
f.Basefmt.TestRunStarted()
f.lock.Lock()
defer f.lock.Unlock()
@ -78,13 +83,13 @@ func (f *events) TestRunStarted() {
}{
"TestRunStarted",
spec,
timeNowFunc().UnixNano() / nanoSec,
utils.TimeNowFunc().UnixNano() / nanoSec,
f.suiteName,
})
}
func (f *events) Feature(ft *messages.GherkinDocument, p string, c []byte) {
f.basefmt.Feature(ft, p, c)
func (f *eventsFormatter) Feature(ft *messages.GherkinDocument, p string, c []byte) {
f.Basefmt.Feature(ft, p, c)
f.lock.Lock()
defer f.lock.Unlock()
@ -100,23 +105,23 @@ func (f *events) Feature(ft *messages.GherkinDocument, p string, c []byte) {
})
}
func (f *events) Summary() {
func (f *eventsFormatter) Summary() {
// @TODO: determine status
status := passed
f.storage.mustGetPickleStepResultsByStatus(failed)
f.storage.MustGetPickleStepResultsByStatus(failed)
if len(f.storage.mustGetPickleStepResultsByStatus(failed)) > 0 {
if len(f.storage.MustGetPickleStepResultsByStatus(failed)) > 0 {
status = failed
} else if len(f.storage.mustGetPickleStepResultsByStatus(passed)) == 0 {
if len(f.storage.mustGetPickleStepResultsByStatus(undefined)) > len(f.storage.mustGetPickleStepResultsByStatus(pending)) {
} else if len(f.storage.MustGetPickleStepResultsByStatus(passed)) == 0 {
if len(f.storage.MustGetPickleStepResultsByStatus(undefined)) > len(f.storage.MustGetPickleStepResultsByStatus(pending)) {
status = undefined
} else {
status = pending
}
}
snips := f.snippets()
snips := f.Snippets()
if len(snips) > 0 {
snips = "You can implement step definitions for undefined steps with these snippets:\n" + snips
}
@ -130,20 +135,20 @@ func (f *events) Summary() {
}{
"TestRunFinished",
status.String(),
timeNowFunc().UnixNano() / nanoSec,
utils.TimeNowFunc().UnixNano() / nanoSec,
snips,
"", // @TODO not sure that could be correctly implemented
})
}
func (f *events) step(pickle *messages.Pickle, pickleStep *messages.Pickle_PickleStep) {
feature := f.storage.mustGetFeature(pickle.Uri)
pickleStepResult := f.storage.mustGetPickleStepResult(pickleStep.Id)
step := feature.findStep(pickleStep.AstNodeIds[0])
func (f *eventsFormatter) step(pickle *messages.Pickle, pickleStep *messages.Pickle_PickleStep) {
feature := f.storage.MustGetFeature(pickle.Uri)
pickleStepResult := f.storage.MustGetPickleStepResult(pickleStep.Id)
step := feature.FindStep(pickleStep.AstNodeIds[0])
var errMsg string
if pickleStepResult.err != nil {
errMsg = pickleStepResult.err.Error()
if pickleStepResult.Err != nil {
errMsg = pickleStepResult.Err.Error()
}
f.event(&struct {
Event string `json:"event"`
@ -154,7 +159,7 @@ func (f *events) step(pickle *messages.Pickle, pickleStep *messages.Pickle_Pickl
}{
"TestStepFinished",
fmt.Sprintf("%s:%d", pickle.Uri, step.Location.Line),
timeNowFunc().UnixNano() / nanoSec,
utils.TimeNowFunc().UnixNano() / nanoSec,
pickleStepResult.Status.String(),
errMsg,
})
@ -162,17 +167,11 @@ func (f *events) step(pickle *messages.Pickle, pickleStep *messages.Pickle_Pickl
if isLastStep(pickle, pickleStep) {
var status string
pickleStepResults := f.storage.mustGetPickleStepResultsByPickleID(pickle.Id)
pickleStepResults := f.storage.MustGetPickleStepResultsByPickleID(pickle.Id)
for _, stepResult := range pickleStepResults {
switch stepResult.Status {
case passed:
status = passed.String()
case failed:
status = failed.String()
case undefined:
status = undefined.String()
case pending:
status = pending.String()
case passed, failed, undefined, pending:
status = stepResult.Status.String()
}
}
@ -184,20 +183,20 @@ func (f *events) step(pickle *messages.Pickle, pickleStep *messages.Pickle_Pickl
}{
"TestCaseFinished",
f.scenarioLocation(pickle),
timeNowFunc().UnixNano() / nanoSec,
utils.TimeNowFunc().UnixNano() / nanoSec,
status,
})
}
}
func (f *events) Defined(pickle *messages.Pickle, pickleStep *messages.Pickle_PickleStep, def *StepDefinition) {
f.basefmt.Defined(pickle, pickleStep, def)
func (f *eventsFormatter) Defined(pickle *messages.Pickle, pickleStep *messages.Pickle_PickleStep, def *models.StepDefinition) {
f.Basefmt.Defined(pickle, pickleStep, def)
f.lock.Lock()
defer f.lock.Unlock()
feature := f.storage.mustGetFeature(pickle.Uri)
step := feature.findStep(pickleStep.AstNodeIds[0])
feature := f.storage.MustGetFeature(pickle.Uri)
step := feature.FindStep(pickleStep.AstNodeIds[0])
if def != nil {
m := def.Expr.FindStringSubmatchIndex(pickleStep.Text)[2:]
@ -222,7 +221,7 @@ func (f *events) Defined(pickle *messages.Pickle, pickleStep *messages.Pickle_Pi
}{
"StepDefinitionFound",
fmt.Sprintf("%s:%d", pickle.Uri, step.Location.Line),
def.definitionID(),
DefinitionID(def),
args,
})
}
@ -234,12 +233,12 @@ func (f *events) Defined(pickle *messages.Pickle, pickleStep *messages.Pickle_Pi
}{
"TestStepStarted",
fmt.Sprintf("%s:%d", pickle.Uri, step.Location.Line),
timeNowFunc().UnixNano() / nanoSec,
utils.TimeNowFunc().UnixNano() / nanoSec,
})
}
func (f *events) Passed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
f.basefmt.Passed(pickle, step, match)
func (f *eventsFormatter) Passed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *models.StepDefinition) {
f.Basefmt.Passed(pickle, step, match)
f.lock.Lock()
defer f.lock.Unlock()
@ -247,8 +246,8 @@ func (f *events) Passed(pickle *messages.Pickle, step *messages.Pickle_PickleSte
f.step(pickle, step)
}
func (f *events) Skipped(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
f.basefmt.Skipped(pickle, step, match)
func (f *eventsFormatter) Skipped(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *models.StepDefinition) {
f.Basefmt.Skipped(pickle, step, match)
f.lock.Lock()
defer f.lock.Unlock()
@ -256,8 +255,8 @@ func (f *events) Skipped(pickle *messages.Pickle, step *messages.Pickle_PickleSt
f.step(pickle, step)
}
func (f *events) Undefined(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
f.basefmt.Undefined(pickle, step, match)
func (f *eventsFormatter) Undefined(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *models.StepDefinition) {
f.Basefmt.Undefined(pickle, step, match)
f.lock.Lock()
defer f.lock.Unlock()
@ -265,8 +264,8 @@ func (f *events) Undefined(pickle *messages.Pickle, step *messages.Pickle_Pickle
f.step(pickle, step)
}
func (f *events) Failed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition, err error) {
f.basefmt.Failed(pickle, step, match, err)
func (f *eventsFormatter) Failed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *models.StepDefinition, err error) {
f.Basefmt.Failed(pickle, step, match, err)
f.lock.Lock()
defer f.lock.Unlock()
@ -274,8 +273,8 @@ func (f *events) Failed(pickle *messages.Pickle, step *messages.Pickle_PickleSte
f.step(pickle, step)
}
func (f *events) Pending(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
f.basefmt.Pending(pickle, step, match)
func (f *eventsFormatter) Pending(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *models.StepDefinition) {
f.Basefmt.Pending(pickle, step, match)
f.lock.Lock()
defer f.lock.Unlock()
@ -283,15 +282,19 @@ func (f *events) Pending(pickle *messages.Pickle, step *messages.Pickle_PickleSt
f.step(pickle, step)
}
func (f *events) scenarioLocation(pickle *messages.Pickle) string {
feature := f.storage.mustGetFeature(pickle.Uri)
scenario := feature.findScenario(pickle.AstNodeIds[0])
func (f *eventsFormatter) scenarioLocation(pickle *messages.Pickle) string {
feature := f.storage.MustGetFeature(pickle.Uri)
scenario := feature.FindScenario(pickle.AstNodeIds[0])
line := scenario.Location.Line
if len(pickle.AstNodeIds) == 2 {
_, row := feature.findExample(pickle.AstNodeIds[1])
_, row := feature.FindExample(pickle.AstNodeIds[1])
line = row.Location.Line
}
return fmt.Sprintf("%s:%d", pickle.Uri, line)
}
func isLastStep(pickle *messages.Pickle, step *messages.Pickle_PickleStep) bool {
return pickle.Steps[len(pickle.Steps)-1].Id == step.Id
}

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

@ -1,4 +1,4 @@
package godog
package formatters
import (
"encoding/xml"
@ -8,18 +8,22 @@ import (
"sort"
"strconv"
"time"
"github.com/cucumber/godog/formatters"
"github.com/cucumber/godog/internal/utils"
)
func init() {
Format("junit", "Prints junit compatible xml to stdout", junitFunc)
formatters.Format("junit", "Prints junit compatible xml to stdout", JUnitFormatterFunc)
}
func junitFunc(suite string, out io.Writer) Formatter {
return &junitFormatter{basefmt: newBaseFmt(suite, out)}
// JUnitFormatterFunc implements the FormatterFunc for the junit formatter
func JUnitFormatterFunc(suite string, out io.Writer) formatters.Formatter {
return &junitFormatter{Basefmt: NewBaseFmt(suite, out)}
}
type junitFormatter struct {
*basefmt
*Basefmt
}
func (f *junitFormatter) Summary() {
@ -41,20 +45,20 @@ func junitTimeDuration(from, to time.Time) string {
return strconv.FormatFloat(to.Sub(from).Seconds(), 'f', -1, 64)
}
func (f *junitFormatter) buildJUNITPackageSuite() junitPackageSuite {
features := f.storage.mustGetFeatures()
func (f *junitFormatter) buildJUNITPackageSuite() JunitPackageSuite {
features := f.storage.MustGetFeatures()
sort.Sort(sortFeaturesByName(features))
testRunStartedAt := f.storage.mustGetTestRunStarted().StartedAt
testRunStartedAt := f.storage.MustGetTestRunStarted().StartedAt
suite := junitPackageSuite{
suite := JunitPackageSuite{
Name: f.suiteName,
TestSuites: make([]*junitTestSuite, len(features)),
Time: junitTimeDuration(testRunStartedAt, timeNowFunc()),
Time: junitTimeDuration(testRunStartedAt, utils.TimeNowFunc()),
}
for idx, feature := range features {
pickles := f.storage.mustGetPickles(feature.Uri)
pickles := f.storage.MustGetPickles(feature.Uri)
sort.Sort(sortPicklesByID(pickles))
ts := junitTestSuite{
@ -74,7 +78,7 @@ func (f *junitFormatter) buildJUNITPackageSuite() junitPackageSuite {
for idx, pickle := range pickles {
tc := junitTestCase{}
pickleResult := f.storage.mustGetPickleResult(pickle.Id)
pickleResult := f.storage.MustGetPickleResult(pickle.Id)
if idx == 0 {
firstPickleStartedAt = pickleResult.StartedAt
@ -84,8 +88,8 @@ func (f *junitFormatter) buildJUNITPackageSuite() junitPackageSuite {
if len(pickle.Steps) > 0 {
lastStep := pickle.Steps[len(pickle.Steps)-1]
lastPickleStepResult := f.storage.mustGetPickleStepResult(lastStep.Id)
lastPickleFinishedAt = lastPickleStepResult.finishedAt
lastPickleStepResult := f.storage.MustGetPickleStepResult(lastStep.Id)
lastPickleFinishedAt = lastPickleStepResult.FinishedAt
}
tc.Time = junitTimeDuration(pickleResult.StartedAt, lastPickleFinishedAt)
@ -99,9 +103,9 @@ func (f *junitFormatter) buildJUNITPackageSuite() junitPackageSuite {
ts.Tests++
suite.Tests++
pickleStepResults := f.storage.mustGetPickleStepResultsByPickleID(pickle.Id)
pickleStepResults := f.storage.MustGetPickleStepResultsByPickleID(pickle.Id)
for _, stepResult := range pickleStepResults {
pickleStep := f.storage.mustGetPickleStep(stepResult.PickleStepID)
pickleStep := f.storage.MustGetPickleStep(stepResult.PickleStepID)
switch stepResult.Status {
case passed:
@ -109,7 +113,7 @@ func (f *junitFormatter) buildJUNITPackageSuite() junitPackageSuite {
case failed:
tc.Status = failed.String()
tc.Failure = &junitFailure{
Message: fmt.Sprintf("Step %s: %s", pickleStep.Text, stepResult.err),
Message: fmt.Sprintf("Step %s: %s", pickleStep.Text, stepResult.Err),
}
case skipped:
tc.Error = append(tc.Error, &junitError{
@ -182,7 +186,8 @@ type junitTestSuite struct {
TestCases []*junitTestCase
}
type junitPackageSuite struct {
// JunitPackageSuite ...
type JunitPackageSuite struct {
XMLName xml.Name `xml:"testsuites"`
Name string `xml:"name,attr"`
Tests int `xml:"tests,attr"`

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

@ -1,4 +1,4 @@
package godog_test
package formatters_test
import (
"bytes"

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

@ -1,4 +1,4 @@
package godog
package formatters
import (
"fmt"
@ -11,26 +11,29 @@ import (
"github.com/cucumber/messages-go/v10"
"github.com/cucumber/godog/colors"
"github.com/cucumber/godog/formatters"
"github.com/cucumber/godog/internal/models"
)
func init() {
Format("pretty", "Prints every feature with runtime statuses.", prettyFunc)
formatters.Format("pretty", "Prints every feature with runtime statuses.", PrettyFormatterFunc)
}
func prettyFunc(suite string, out io.Writer) Formatter {
return &pretty{basefmt: newBaseFmt(suite, out)}
// PrettyFormatterFunc implements the FormatterFunc for the pretty formatter
func PrettyFormatterFunc(suite string, out io.Writer) formatters.Formatter {
return &pretty{Basefmt: NewBaseFmt(suite, out)}
}
var outlinePlaceholderRegexp = regexp.MustCompile("<[^>]+>")
// a built in default pretty formatter
type pretty struct {
*basefmt
*Basefmt
firstFeature *bool
}
func (f *pretty) TestRunStarted() {
f.basefmt.TestRunStarted()
f.Basefmt.TestRunStarted()
f.lock.Lock()
defer f.lock.Unlock()
@ -48,7 +51,7 @@ func (f *pretty) Feature(gd *messages.GherkinDocument, p string, c []byte) {
*f.firstFeature = false
f.lock.Unlock()
f.basefmt.Feature(gd, p, c)
f.Basefmt.Feature(gd, p, c)
f.lock.Lock()
defer f.lock.Unlock()
@ -58,7 +61,7 @@ func (f *pretty) Feature(gd *messages.GherkinDocument, p string, c []byte) {
// Pickle takes a gherkin node for formatting
func (f *pretty) Pickle(pickle *messages.Pickle) {
f.basefmt.Pickle(pickle)
f.Basefmt.Pickle(pickle)
f.lock.Lock()
defer f.lock.Unlock()
@ -69,8 +72,8 @@ func (f *pretty) Pickle(pickle *messages.Pickle) {
}
}
func (f *pretty) Passed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
f.basefmt.Passed(pickle, step, match)
func (f *pretty) Passed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *models.StepDefinition) {
f.Basefmt.Passed(pickle, step, match)
f.lock.Lock()
defer f.lock.Unlock()
@ -78,8 +81,8 @@ func (f *pretty) Passed(pickle *messages.Pickle, step *messages.Pickle_PickleSte
f.printStep(pickle, step)
}
func (f *pretty) Skipped(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
f.basefmt.Skipped(pickle, step, match)
func (f *pretty) Skipped(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *models.StepDefinition) {
f.Basefmt.Skipped(pickle, step, match)
f.lock.Lock()
defer f.lock.Unlock()
@ -87,8 +90,8 @@ func (f *pretty) Skipped(pickle *messages.Pickle, step *messages.Pickle_PickleSt
f.printStep(pickle, step)
}
func (f *pretty) Undefined(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
f.basefmt.Undefined(pickle, step, match)
func (f *pretty) Undefined(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *models.StepDefinition) {
f.Basefmt.Undefined(pickle, step, match)
f.lock.Lock()
defer f.lock.Unlock()
@ -96,8 +99,8 @@ func (f *pretty) Undefined(pickle *messages.Pickle, step *messages.Pickle_Pickle
f.printStep(pickle, step)
}
func (f *pretty) Failed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition, err error) {
f.basefmt.Failed(pickle, step, match, err)
func (f *pretty) Failed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *models.StepDefinition, err error) {
f.Basefmt.Failed(pickle, step, match, err)
f.lock.Lock()
defer f.lock.Unlock()
@ -105,8 +108,8 @@ func (f *pretty) Failed(pickle *messages.Pickle, step *messages.Pickle_PickleSte
f.printStep(pickle, step)
}
func (f *pretty) Pending(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
f.basefmt.Pending(pickle, step, match)
func (f *pretty) Pending(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *models.StepDefinition) {
f.Basefmt.Pending(pickle, step, match)
f.lock.Lock()
defer f.lock.Unlock()
@ -132,9 +135,9 @@ func keywordAndName(keyword, name string) string {
}
func (f *pretty) scenarioLengths(pickle *messages.Pickle) (scenarioHeaderLength int, maxLength int) {
feature := f.storage.mustGetFeature(pickle.Uri)
astScenario := feature.findScenario(pickle.AstNodeIds[0])
astBackground := feature.findBackground(pickle.AstNodeIds[0])
feature := f.storage.MustGetFeature(pickle.Uri)
astScenario := feature.FindScenario(pickle.AstNodeIds[0])
astBackground := feature.FindBackground(pickle.AstNodeIds[0])
scenarioHeaderLength = f.lengthPickle(astScenario.Keyword, astScenario.Name)
maxLength = f.longestStep(astScenario.Steps, scenarioHeaderLength)
@ -147,16 +150,16 @@ func (f *pretty) scenarioLengths(pickle *messages.Pickle) (scenarioHeaderLength
}
func (f *pretty) printScenarioHeader(pickle *messages.Pickle, astScenario *messages.GherkinDocument_Feature_Scenario, spaceFilling int) {
feature := f.storage.mustGetFeature(pickle.Uri)
feature := f.storage.MustGetFeature(pickle.Uri)
text := s(f.indent) + keywordAndName(astScenario.Keyword, astScenario.Name)
text += s(spaceFilling) + line(feature.Uri, astScenario.Location)
fmt.Fprintln(f.out, "\n"+text)
}
func (f *pretty) printUndefinedPickle(pickle *messages.Pickle) {
feature := f.storage.mustGetFeature(pickle.Uri)
astScenario := feature.findScenario(pickle.AstNodeIds[0])
astBackground := feature.findBackground(pickle.AstNodeIds[0])
feature := f.storage.MustGetFeature(pickle.Uri)
astScenario := feature.FindScenario(pickle.AstNodeIds[0])
astBackground := feature.FindBackground(pickle.AstNodeIds[0])
scenarioHeaderLength, maxLength := f.scenarioLengths(pickle)
@ -170,7 +173,7 @@ func (f *pretty) printUndefinedPickle(pickle *messages.Pickle) {
// do not print scenario headers and examples multiple times
if len(astScenario.Examples) > 0 {
exampleTable, exampleRow := feature.findExample(pickle.AstNodeIds[1])
exampleTable, exampleRow := feature.FindExample(pickle.AstNodeIds[1])
firstExampleRow := exampleTable.TableBody[0].Id == exampleRow.Id
firstExamplesTable := astScenario.Examples[0].Location.Line == exampleTable.Location.Line
@ -197,45 +200,45 @@ func (f *pretty) printUndefinedPickle(pickle *messages.Pickle) {
// Summary sumarize the feature formatter output
func (f *pretty) Summary() {
failedStepResults := f.storage.mustGetPickleStepResultsByStatus(failed)
failedStepResults := f.storage.MustGetPickleStepResultsByStatus(failed)
if len(failedStepResults) > 0 {
fmt.Fprintln(f.out, "\n--- "+red("Failed steps:")+"\n")
sort.Sort(sortPickleStepResultsByPickleStepID(failedStepResults))
for _, fail := range failedStepResults {
pickle := f.storage.mustGetPickle(fail.PickleID)
pickleStep := f.storage.mustGetPickleStep(fail.PickleStepID)
feature := f.storage.mustGetFeature(pickle.Uri)
pickle := f.storage.MustGetPickle(fail.PickleID)
pickleStep := f.storage.MustGetPickleStep(fail.PickleStepID)
feature := f.storage.MustGetFeature(pickle.Uri)
astScenario := feature.findScenario(pickle.AstNodeIds[0])
astScenario := feature.FindScenario(pickle.AstNodeIds[0])
scenarioDesc := fmt.Sprintf("%s: %s", astScenario.Keyword, pickle.Name)
astStep := feature.findStep(pickleStep.AstNodeIds[0])
astStep := feature.FindStep(pickleStep.AstNodeIds[0])
stepDesc := strings.TrimSpace(astStep.Keyword) + " " + pickleStep.Text
fmt.Fprintln(f.out, s(f.indent)+red(scenarioDesc)+line(feature.Uri, astScenario.Location))
fmt.Fprintln(f.out, s(f.indent*2)+red(stepDesc)+line(feature.Uri, astStep.Location))
fmt.Fprintln(f.out, s(f.indent*3)+red("Error: ")+redb(fmt.Sprintf("%+v", fail.err))+"\n")
fmt.Fprintln(f.out, s(f.indent*3)+red("Error: ")+redb(fmt.Sprintf("%+v", fail.Err))+"\n")
}
}
f.basefmt.Summary()
f.Basefmt.Summary()
}
func (f *pretty) printOutlineExample(pickle *messages.Pickle, backgroundSteps int) {
var errorMsg string
var clr = green
feature := f.storage.mustGetFeature(pickle.Uri)
astScenario := feature.findScenario(pickle.AstNodeIds[0])
feature := f.storage.MustGetFeature(pickle.Uri)
astScenario := feature.FindScenario(pickle.AstNodeIds[0])
scenarioHeaderLength, maxLength := f.scenarioLengths(pickle)
exampleTable, exampleRow := feature.findExample(pickle.AstNodeIds[1])
exampleTable, exampleRow := feature.FindExample(pickle.AstNodeIds[1])
printExampleHeader := exampleTable.TableBody[0].Id == exampleRow.Id
firstExamplesTable := astScenario.Examples[0].Location.Line == exampleTable.Location.Line
pickleStepResults := f.storage.mustGetPickleStepResultsByPickleID(pickle.Id)
pickleStepResults := f.storage.MustGetPickleStepResultsByPickleID(pickle.Id)
firstExecutedScenarioStep := len(pickleStepResults) == backgroundSteps+1
if firstExamplesTable && printExampleHeader && firstExecutedScenarioStep {
@ -257,10 +260,10 @@ func (f *pretty) printOutlineExample(pickle *messages.Pickle, backgroundSteps in
// determine example row status
switch {
case result.Status == failed:
errorMsg = result.err.Error()
clr = result.Status.clr()
errorMsg = result.Err.Error()
clr = result.Status.Color()
case result.Status == undefined || result.Status == pending:
clr = result.Status.clr()
clr = result.Status.Color()
case result.Status == skipped && clr == nil:
clr = cyan
}
@ -268,11 +271,11 @@ func (f *pretty) printOutlineExample(pickle *messages.Pickle, backgroundSteps in
if firstExamplesTable && printExampleHeader {
// in first example, we need to print steps
pickleStep := f.storage.mustGetPickleStep(result.PickleStepID)
astStep := feature.findStep(pickleStep.AstNodeIds[0])
pickleStep := f.storage.MustGetPickleStep(result.PickleStepID)
astStep := feature.FindStep(pickleStep.AstNodeIds[0])
var text = ""
if result.def != nil {
if result.Def != nil {
if m := outlinePlaceholderRegexp.FindAllStringIndex(astStep.Text, -1); len(m) > 0 {
var pos int
for i := 0; i < len(m); i++ {
@ -290,7 +293,7 @@ func (f *pretty) printOutlineExample(pickle *messages.Pickle, backgroundSteps in
stepLength := f.lengthPickleStep(astStep.Keyword, astStep.Text)
text += s(maxLength - stepLength)
text += " " + blackb("# "+result.def.definitionID())
text += " " + blackb("# "+DefinitionID(result.Def))
}
// print the step outline
@ -340,10 +343,10 @@ func (f *pretty) printTableHeader(row *messages.GherkinDocument_Feature_TableRow
}
func (f *pretty) printStep(pickle *messages.Pickle, pickleStep *messages.Pickle_PickleStep) {
feature := f.storage.mustGetFeature(pickle.Uri)
astBackground := feature.findBackground(pickle.AstNodeIds[0])
astScenario := feature.findScenario(pickle.AstNodeIds[0])
astStep := feature.findStep(pickleStep.AstNodeIds[0])
feature := f.storage.MustGetFeature(pickle.Uri)
astBackground := feature.FindBackground(pickle.AstNodeIds[0])
astScenario := feature.FindScenario(pickle.AstNodeIds[0])
astStep := feature.FindStep(pickleStep.AstNodeIds[0])
var astBackgroundStep bool
var firstExecutedBackgroundStep bool
@ -360,7 +363,7 @@ func (f *pretty) printStep(pickle *messages.Pickle, pickleStep *messages.Pickle_
}
}
firstPickle := feature.pickles[0].Id == pickle.Id
firstPickle := feature.Pickles[0].Id == pickle.Id
if astBackgroundStep && !firstPickle {
return
@ -383,11 +386,11 @@ func (f *pretty) printStep(pickle *messages.Pickle, pickleStep *messages.Pickle_
f.printScenarioHeader(pickle, astScenario, maxLength-scenarioHeaderLength)
}
pickleStepResult := f.storage.mustGetPickleStepResult(pickleStep.Id)
text := s(f.indent*2) + pickleStepResult.Status.clr()(strings.TrimSpace(astStep.Keyword)) + " " + pickleStepResult.Status.clr()(pickleStep.Text)
if pickleStepResult.def != nil {
pickleStepResult := f.storage.MustGetPickleStepResult(pickleStep.Id)
text := s(f.indent*2) + pickleStepResult.Status.Color()(strings.TrimSpace(astStep.Keyword)) + " " + pickleStepResult.Status.Color()(pickleStep.Text)
if pickleStepResult.Def != nil {
text += s(maxLength - stepLength + 1)
text += blackb("# " + pickleStepResult.def.definitionID())
text += blackb("# " + DefinitionID(pickleStepResult.Def))
}
fmt.Fprintln(f.out, text)
@ -399,8 +402,8 @@ func (f *pretty) printStep(pickle *messages.Pickle, pickleStep *messages.Pickle_
f.printDocString(docString)
}
if pickleStepResult.err != nil {
fmt.Fprintln(f.out, s(f.indent*2)+redb(fmt.Sprintf("%+v", pickleStepResult.err)))
if pickleStepResult.Err != nil {
fmt.Fprintln(f.out, s(f.indent*2)+redb(fmt.Sprintf("%+v", pickleStepResult.Err)))
}
if pickleStepResult.Status == pending {

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

@ -1,4 +1,4 @@
package godog
package formatters
import (
"fmt"
@ -8,23 +8,27 @@ import (
"strings"
"github.com/cucumber/messages-go/v10"
"github.com/cucumber/godog/formatters"
"github.com/cucumber/godog/internal/models"
)
func init() {
Format("progress", "Prints a character per step.", progressFunc)
formatters.Format("progress", "Prints a character per step.", ProgressFormatterFunc)
}
func progressFunc(suite string, out io.Writer) Formatter {
// ProgressFormatterFunc implements the FormatterFunc for the progress formatter
func ProgressFormatterFunc(suite string, out io.Writer) formatters.Formatter {
steps := 0
return &progress{
basefmt: newBaseFmt(suite, out),
Basefmt: NewBaseFmt(suite, out),
stepsPerRow: 70,
steps: &steps,
}
}
type progress struct {
*basefmt
*Basefmt
stepsPerRow int
steps *int
}
@ -41,20 +45,20 @@ func (f *progress) Summary() {
var failedStepsOutput []string
failedSteps := f.storage.mustGetPickleStepResultsByStatus(failed)
failedSteps := f.storage.MustGetPickleStepResultsByStatus(failed)
sort.Sort(sortPickleStepResultsByPickleStepID(failedSteps))
for _, sr := range failedSteps {
if sr.Status == failed {
pickle := f.storage.mustGetPickle(sr.PickleID)
pickleStep := f.storage.mustGetPickleStep(sr.PickleStepID)
feature := f.storage.mustGetFeature(pickle.Uri)
pickle := f.storage.MustGetPickle(sr.PickleID)
pickleStep := f.storage.MustGetPickleStep(sr.PickleStepID)
feature := f.storage.MustGetFeature(pickle.Uri)
sc := feature.findScenario(pickle.AstNodeIds[0])
sc := feature.FindScenario(pickle.AstNodeIds[0])
scenarioDesc := fmt.Sprintf("%s: %s", sc.Keyword, pickle.Name)
scenarioLine := fmt.Sprintf("%s:%d", pickle.Uri, sc.Location.Line)
step := feature.findStep(pickleStep.AstNodeIds[0])
step := feature.FindStep(pickleStep.AstNodeIds[0])
stepDesc := strings.TrimSpace(step.Keyword) + " " + pickleStep.Text
stepLine := fmt.Sprintf("%s:%d", pickle.Uri, step.Location.Line)
@ -62,7 +66,7 @@ func (f *progress) Summary() {
failedStepsOutput,
s(2)+red(scenarioDesc)+blackb(" # "+scenarioLine),
s(4)+red(stepDesc)+blackb(" # "+stepLine),
s(6)+red("Error: ")+redb(fmt.Sprintf("%+v", sr.err)),
s(6)+red("Error: ")+redb(fmt.Sprintf("%+v", sr.Err)),
"",
)
}
@ -74,11 +78,11 @@ func (f *progress) Summary() {
}
fmt.Fprintln(f.out, "")
f.basefmt.Summary()
f.Basefmt.Summary()
}
func (f *progress) step(pickleStepID string) {
pickleStepResult := f.storage.mustGetPickleStepResult(pickleStepID)
pickleStepResult := f.storage.MustGetPickleStepResult(pickleStepID)
switch pickleStepResult.Status {
case passed:
@ -100,8 +104,8 @@ func (f *progress) step(pickleStepID string) {
}
}
func (f *progress) Passed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
f.basefmt.Passed(pickle, step, match)
func (f *progress) Passed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *models.StepDefinition) {
f.Basefmt.Passed(pickle, step, match)
f.lock.Lock()
defer f.lock.Unlock()
@ -109,8 +113,8 @@ func (f *progress) Passed(pickle *messages.Pickle, step *messages.Pickle_PickleS
f.step(step.Id)
}
func (f *progress) Skipped(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
f.basefmt.Skipped(pickle, step, match)
func (f *progress) Skipped(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *models.StepDefinition) {
f.Basefmt.Skipped(pickle, step, match)
f.lock.Lock()
defer f.lock.Unlock()
@ -118,8 +122,8 @@ func (f *progress) Skipped(pickle *messages.Pickle, step *messages.Pickle_Pickle
f.step(step.Id)
}
func (f *progress) Undefined(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
f.basefmt.Undefined(pickle, step, match)
func (f *progress) Undefined(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *models.StepDefinition) {
f.Basefmt.Undefined(pickle, step, match)
f.lock.Lock()
defer f.lock.Unlock()
@ -127,8 +131,8 @@ func (f *progress) Undefined(pickle *messages.Pickle, step *messages.Pickle_Pick
f.step(step.Id)
}
func (f *progress) Failed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition, err error) {
f.basefmt.Failed(pickle, step, match, err)
func (f *progress) Failed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *models.StepDefinition, err error) {
f.Basefmt.Failed(pickle, step, match, err)
f.lock.Lock()
defer f.lock.Unlock()
@ -136,8 +140,8 @@ func (f *progress) Failed(pickle *messages.Pickle, step *messages.Pickle_PickleS
f.step(step.Id)
}
func (f *progress) Pending(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
f.basefmt.Pending(pickle, step, match)
func (f *progress) Pending(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *models.StepDefinition) {
f.Basefmt.Pending(pickle, step, match)
f.lock.Lock()
defer f.lock.Unlock()

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

@ -1,57 +1,57 @@
{"event":"TestRunStarted","version":"0.1.0","timestamp":-6795364578871,"suite":"events"}
{"event":"TestSource","location":"formatter-tests/features/scenario_outline.feature:2","source":"@outline @tag\nFeature: outline\n\n @scenario\n Scenario Outline: outline\n Given passing step\n When passing step\n Then odd \u003codd\u003e and even \u003ceven\u003e number\n\n @tagged\n Examples: tagged\n | odd | even |\n | 1 | 2 |\n | 2 | 0 |\n | 3 | 11 |\n\n @tag2\n Examples:\n | odd | even |\n | 1 | 14 |\n | 3 | 9 |\n"}
{"event":"TestCaseStarted","location":"formatter-tests/features/scenario_outline.feature:13","timestamp":-6795364578871}
{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:6","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:6","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/scenario_outline.feature:6","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/scenario_outline.feature:6","timestamp":-6795364578871,"status":"passed"}
{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:7","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:7","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/scenario_outline.feature:7","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/scenario_outline.feature:7","timestamp":-6795364578871,"status":"passed"}
{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:8","definition_id":"fmt_output_test.go:103 -\u003e github.com/cucumber/godog_test.oddEvenStepDef","arguments":[[4,5],[5,15]]}
{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:8","definition_id":"fmt_output_test.go:103 -\u003e github.com/cucumber/godog/internal/formatters_test.oddEvenStepDef","arguments":[[4,5],[5,15]]}
{"event":"TestStepStarted","location":"formatter-tests/features/scenario_outline.feature:8","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/scenario_outline.feature:8","timestamp":-6795364578871,"status":"passed"}
{"event":"TestCaseFinished","location":"formatter-tests/features/scenario_outline.feature:13","timestamp":-6795364578871,"status":"passed"}
{"event":"TestCaseStarted","location":"formatter-tests/features/scenario_outline.feature:14","timestamp":-6795364578871}
{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:6","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:6","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/scenario_outline.feature:6","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/scenario_outline.feature:6","timestamp":-6795364578871,"status":"passed"}
{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:7","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:7","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/scenario_outline.feature:7","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/scenario_outline.feature:7","timestamp":-6795364578871,"status":"passed"}
{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:8","definition_id":"fmt_output_test.go:103 -\u003e github.com/cucumber/godog_test.oddEvenStepDef","arguments":[[4,5],[5,15]]}
{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:8","definition_id":"fmt_output_test.go:103 -\u003e github.com/cucumber/godog/internal/formatters_test.oddEvenStepDef","arguments":[[4,5],[5,15]]}
{"event":"TestStepStarted","location":"formatter-tests/features/scenario_outline.feature:8","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/scenario_outline.feature:8","timestamp":-6795364578871,"status":"failed","summary":"2 is not odd"}
{"event":"TestCaseFinished","location":"formatter-tests/features/scenario_outline.feature:14","timestamp":-6795364578871,"status":"failed"}
{"event":"TestCaseStarted","location":"formatter-tests/features/scenario_outline.feature:15","timestamp":-6795364578871}
{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:6","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:6","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/scenario_outline.feature:6","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/scenario_outline.feature:6","timestamp":-6795364578871,"status":"passed"}
{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:7","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:7","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/scenario_outline.feature:7","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/scenario_outline.feature:7","timestamp":-6795364578871,"status":"passed"}
{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:8","definition_id":"fmt_output_test.go:103 -\u003e github.com/cucumber/godog_test.oddEvenStepDef","arguments":[[4,5],[5,15]]}
{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:8","definition_id":"fmt_output_test.go:103 -\u003e github.com/cucumber/godog/internal/formatters_test.oddEvenStepDef","arguments":[[4,5],[5,15]]}
{"event":"TestStepStarted","location":"formatter-tests/features/scenario_outline.feature:8","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/scenario_outline.feature:8","timestamp":-6795364578871,"status":"failed","summary":"11 is not even"}
{"event":"TestCaseFinished","location":"formatter-tests/features/scenario_outline.feature:15","timestamp":-6795364578871,"status":"failed"}
{"event":"TestCaseStarted","location":"formatter-tests/features/scenario_outline.feature:20","timestamp":-6795364578871}
{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:6","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:6","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/scenario_outline.feature:6","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/scenario_outline.feature:6","timestamp":-6795364578871,"status":"passed"}
{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:7","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:7","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/scenario_outline.feature:7","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/scenario_outline.feature:7","timestamp":-6795364578871,"status":"passed"}
{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:8","definition_id":"fmt_output_test.go:103 -\u003e github.com/cucumber/godog_test.oddEvenStepDef","arguments":[[4,5],[5,15]]}
{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:8","definition_id":"fmt_output_test.go:103 -\u003e github.com/cucumber/godog/internal/formatters_test.oddEvenStepDef","arguments":[[4,5],[5,15]]}
{"event":"TestStepStarted","location":"formatter-tests/features/scenario_outline.feature:8","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/scenario_outline.feature:8","timestamp":-6795364578871,"status":"passed"}
{"event":"TestCaseFinished","location":"formatter-tests/features/scenario_outline.feature:20","timestamp":-6795364578871,"status":"passed"}
{"event":"TestCaseStarted","location":"formatter-tests/features/scenario_outline.feature:21","timestamp":-6795364578871}
{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:6","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:6","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/scenario_outline.feature:6","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/scenario_outline.feature:6","timestamp":-6795364578871,"status":"passed"}
{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:7","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:7","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/scenario_outline.feature:7","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/scenario_outline.feature:7","timestamp":-6795364578871,"status":"passed"}
{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:8","definition_id":"fmt_output_test.go:103 -\u003e github.com/cucumber/godog_test.oddEvenStepDef","arguments":[[4,5],[5,15]]}
{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:8","definition_id":"fmt_output_test.go:103 -\u003e github.com/cucumber/godog/internal/formatters_test.oddEvenStepDef","arguments":[[4,5],[5,15]]}
{"event":"TestStepStarted","location":"formatter-tests/features/scenario_outline.feature:8","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/scenario_outline.feature:8","timestamp":-6795364578871,"status":"failed","summary":"9 is not even"}
{"event":"TestCaseFinished","location":"formatter-tests/features/scenario_outline.feature:21","timestamp":-6795364578871,"status":"failed"}

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

@ -1,16 +1,16 @@
{"event":"TestRunStarted","version":"0.1.0","timestamp":-6795364578871,"suite":"events"}
{"event":"TestSource","location":"formatter-tests/features/scenario_with_background.feature:1","source":"Feature: single scenario with background\n\n Background: named\n Given passing step\n And passing step\n\n Scenario: scenario\n When passing step\n Then passing step\n"}
{"event":"TestCaseStarted","location":"formatter-tests/features/scenario_with_background.feature:7","timestamp":-6795364578871}
{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_with_background.feature:4","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_with_background.feature:4","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/scenario_with_background.feature:4","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/scenario_with_background.feature:4","timestamp":-6795364578871,"status":"passed"}
{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_with_background.feature:5","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_with_background.feature:5","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/scenario_with_background.feature:5","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/scenario_with_background.feature:5","timestamp":-6795364578871,"status":"passed"}
{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_with_background.feature:8","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_with_background.feature:8","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/scenario_with_background.feature:8","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/scenario_with_background.feature:8","timestamp":-6795364578871,"status":"passed"}
{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_with_background.feature:9","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_with_background.feature:9","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/scenario_with_background.feature:9","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/scenario_with_background.feature:9","timestamp":-6795364578871,"status":"passed"}
{"event":"TestCaseFinished","location":"formatter-tests/features/scenario_with_background.feature:7","timestamp":-6795364578871,"status":"passed"}

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

@ -1,7 +1,7 @@
{"event":"TestRunStarted","version":"0.1.0","timestamp":-6795364578871,"suite":"events"}
{"event":"TestSource","location":"formatter-tests/features/single_scenario_with_passing_step.feature:1","source":"Feature: single passing scenario\n describes\n a single scenario\n feature\n\n Scenario: one step passing\n Given a passing step\n"}
{"event":"TestCaseStarted","location":"formatter-tests/features/single_scenario_with_passing_step.feature:6","timestamp":-6795364578871}
{"event":"StepDefinitionFound","location":"formatter-tests/features/single_scenario_with_passing_step.feature:7","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
{"event":"StepDefinitionFound","location":"formatter-tests/features/single_scenario_with_passing_step.feature:7","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/single_scenario_with_passing_step.feature:7","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/single_scenario_with_passing_step.feature:7","timestamp":-6795364578871,"status":"passed"}
{"event":"TestCaseFinished","location":"formatter-tests/features/single_scenario_with_passing_step.feature:6","timestamp":-6795364578871,"status":"passed"}

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

@ -1,28 +1,28 @@
{"event":"TestRunStarted","version":"0.1.0","timestamp":-6795364578871,"suite":"events"}
{"event":"TestSource","location":"formatter-tests/features/some_scenarions_including_failing.feature:1","source":"Feature: some scenarios\n\n Scenario: failing\n Given passing step\n When failing step\n Then passing step\n\n Scenario: pending\n When pending step\n Then passing step\n\n Scenario: undefined\n When undefined\n Then passing step\n"}
{"event":"TestCaseStarted","location":"formatter-tests/features/some_scenarions_including_failing.feature:3","timestamp":-6795364578871}
{"event":"StepDefinitionFound","location":"formatter-tests/features/some_scenarions_including_failing.feature:4","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
{"event":"StepDefinitionFound","location":"formatter-tests/features/some_scenarions_including_failing.feature:4","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/some_scenarions_including_failing.feature:4","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/some_scenarions_including_failing.feature:4","timestamp":-6795364578871,"status":"passed"}
{"event":"StepDefinitionFound","location":"formatter-tests/features/some_scenarions_including_failing.feature:5","definition_id":"fmt_output_test.go:117 -\u003e github.com/cucumber/godog_test.failingStepDef","arguments":[]}
{"event":"StepDefinitionFound","location":"formatter-tests/features/some_scenarions_including_failing.feature:5","definition_id":"fmt_output_test.go:117 -\u003e github.com/cucumber/godog/internal/formatters_test.failingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/some_scenarions_including_failing.feature:5","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/some_scenarions_including_failing.feature:5","timestamp":-6795364578871,"status":"failed","summary":"step failed"}
{"event":"StepDefinitionFound","location":"formatter-tests/features/some_scenarions_including_failing.feature:6","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
{"event":"StepDefinitionFound","location":"formatter-tests/features/some_scenarions_including_failing.feature:6","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/some_scenarions_including_failing.feature:6","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/some_scenarions_including_failing.feature:6","timestamp":-6795364578871,"status":"skipped"}
{"event":"TestCaseFinished","location":"formatter-tests/features/some_scenarions_including_failing.feature:3","timestamp":-6795364578871,"status":"failed"}
{"event":"TestCaseStarted","location":"formatter-tests/features/some_scenarions_including_failing.feature:8","timestamp":-6795364578871}
{"event":"StepDefinitionFound","location":"formatter-tests/features/some_scenarions_including_failing.feature:9","definition_id":"fmt_output_test.go:115 -\u003e github.com/cucumber/godog_test.pendingStepDef","arguments":[]}
{"event":"StepDefinitionFound","location":"formatter-tests/features/some_scenarions_including_failing.feature:9","definition_id":"fmt_output_test.go:115 -\u003e github.com/cucumber/godog/internal/formatters_test.pendingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/some_scenarions_including_failing.feature:9","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/some_scenarions_including_failing.feature:9","timestamp":-6795364578871,"status":"pending"}
{"event":"StepDefinitionFound","location":"formatter-tests/features/some_scenarions_including_failing.feature:10","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
{"event":"StepDefinitionFound","location":"formatter-tests/features/some_scenarions_including_failing.feature:10","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/some_scenarions_including_failing.feature:10","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/some_scenarions_including_failing.feature:10","timestamp":-6795364578871,"status":"skipped"}
{"event":"TestCaseFinished","location":"formatter-tests/features/some_scenarions_including_failing.feature:8","timestamp":-6795364578871,"status":"pending"}
{"event":"TestCaseStarted","location":"formatter-tests/features/some_scenarions_including_failing.feature:12","timestamp":-6795364578871}
{"event":"TestStepStarted","location":"formatter-tests/features/some_scenarions_including_failing.feature:13","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/some_scenarions_including_failing.feature:13","timestamp":-6795364578871,"status":"undefined"}
{"event":"StepDefinitionFound","location":"formatter-tests/features/some_scenarions_including_failing.feature:14","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
{"event":"StepDefinitionFound","location":"formatter-tests/features/some_scenarions_including_failing.feature:14","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/some_scenarions_including_failing.feature:14","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/some_scenarions_including_failing.feature:14","timestamp":-6795364578871,"status":"skipped"}
{"event":"TestCaseFinished","location":"formatter-tests/features/some_scenarions_including_failing.feature:12","timestamp":-6795364578871,"status":"undefined"}

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

@ -1,27 +1,27 @@
{"event":"TestRunStarted","version":"0.1.0","timestamp":-6795364578871,"suite":"events"}
{"event":"TestSource","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:1","source":"Feature: two scenarios with background fail\n\n Background:\n Given passing step\n And failing step\n\n Scenario: one\n When passing step\n Then passing step\n\n Scenario: two\n Then passing step\n"}
{"event":"TestCaseStarted","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:7","timestamp":-6795364578871}
{"event":"StepDefinitionFound","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:4","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
{"event":"StepDefinitionFound","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:4","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:4","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:4","timestamp":-6795364578871,"status":"passed"}
{"event":"StepDefinitionFound","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:5","definition_id":"fmt_output_test.go:117 -\u003e github.com/cucumber/godog_test.failingStepDef","arguments":[]}
{"event":"StepDefinitionFound","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:5","definition_id":"fmt_output_test.go:117 -\u003e github.com/cucumber/godog/internal/formatters_test.failingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:5","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:5","timestamp":-6795364578871,"status":"failed","summary":"step failed"}
{"event":"StepDefinitionFound","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:8","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
{"event":"StepDefinitionFound","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:8","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:8","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:8","timestamp":-6795364578871,"status":"skipped"}
{"event":"StepDefinitionFound","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:9","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
{"event":"StepDefinitionFound","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:9","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:9","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:9","timestamp":-6795364578871,"status":"skipped"}
{"event":"TestCaseFinished","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:7","timestamp":-6795364578871,"status":"failed"}
{"event":"TestCaseStarted","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:11","timestamp":-6795364578871}
{"event":"StepDefinitionFound","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:4","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
{"event":"StepDefinitionFound","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:4","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:4","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:4","timestamp":-6795364578871,"status":"passed"}
{"event":"StepDefinitionFound","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:5","definition_id":"fmt_output_test.go:117 -\u003e github.com/cucumber/godog_test.failingStepDef","arguments":[]}
{"event":"StepDefinitionFound","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:5","definition_id":"fmt_output_test.go:117 -\u003e github.com/cucumber/godog/internal/formatters_test.failingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:5","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:5","timestamp":-6795364578871,"status":"failed","summary":"step failed"}
{"event":"StepDefinitionFound","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:12","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog_test.passingStepDef","arguments":[]}
{"event":"StepDefinitionFound","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:12","definition_id":"fmt_output_test.go:101 -\u003e github.com/cucumber/godog/internal/formatters_test.passingStepDef","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:12","timestamp":-6795364578871}
{"event":"TestStepFinished","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:12","timestamp":-6795364578871,"status":"skipped"}
{"event":"TestCaseFinished","location":"formatter-tests/features/two_scenarios_with_background_fail.feature:11","timestamp":-6795364578871,"status":"failed"}

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

@ -1,9 +1,9 @@
<bold-white>Feature:</bold-white> outline
<bold-white>Scenario Outline:</bold-white> outline <bold-black># formatter-tests/features/scenario_outline.feature:5</bold-black>
<cyan>Given</cyan> <cyan>passing step</cyan> <bold-black># fmt_output_test.go:101 -> github.com/cucumber/godog_test.passingStepDef</bold-black>
<cyan>When</cyan> <cyan>passing step</cyan> <bold-black># fmt_output_test.go:101 -> github.com/cucumber/godog_test.passingStepDef</bold-black>
<cyan>Then</cyan> <cyan>odd </cyan><bold-cyan><odd></bold-cyan><cyan> and even </cyan><bold-cyan><even></bold-cyan><cyan> number</cyan> <bold-black># fmt_output_test.go:103 -> github.com/cucumber/godog_test.oddEvenStepDef</bold-black>
<cyan>Given</cyan> <cyan>passing step</cyan> <bold-black># fmt_output_test.go:101 -> github.com/cucumber/godog/internal/formatters_test.passingStepDef</bold-black>
<cyan>When</cyan> <cyan>passing step</cyan> <bold-black># fmt_output_test.go:101 -> github.com/cucumber/godog/internal/formatters_test.passingStepDef</bold-black>
<cyan>Then</cyan> <cyan>odd </cyan><bold-cyan><odd></bold-cyan><cyan> and even </cyan><bold-cyan><even></bold-cyan><cyan> number</cyan> <bold-black># fmt_output_test.go:103 -> github.com/cucumber/godog/internal/formatters_test.oddEvenStepDef</bold-black>
<bold-white>Examples:</bold-white> tagged
| <cyan>odd</cyan> | <cyan>even</cyan> |

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

@ -1,12 +1,12 @@
<bold-white>Feature:</bold-white> single scenario with background
<bold-white>Background:</bold-white> named
<green>Given</green> <green>passing step</green> <bold-black># fmt_output_test.go:101 -> github.com/cucumber/godog_test.passingStepDef</bold-black>
<green>And</green> <green>passing step</green> <bold-black># fmt_output_test.go:101 -> github.com/cucumber/godog_test.passingStepDef</bold-black>
<green>Given</green> <green>passing step</green> <bold-black># fmt_output_test.go:101 -> github.com/cucumber/godog/internal/formatters_test.passingStepDef</bold-black>
<green>And</green> <green>passing step</green> <bold-black># fmt_output_test.go:101 -> github.com/cucumber/godog/internal/formatters_test.passingStepDef</bold-black>
<bold-white>Scenario:</bold-white> scenario <bold-black># formatter-tests/features/scenario_with_background.feature:7</bold-black>
<green>When</green> <green>passing step</green> <bold-black># fmt_output_test.go:101 -> github.com/cucumber/godog_test.passingStepDef</bold-black>
<green>Then</green> <green>passing step</green> <bold-black># fmt_output_test.go:101 -> github.com/cucumber/godog_test.passingStepDef</bold-black>
<green>When</green> <green>passing step</green> <bold-black># fmt_output_test.go:101 -> github.com/cucumber/godog/internal/formatters_test.passingStepDef</bold-black>
<green>Then</green> <green>passing step</green> <bold-black># fmt_output_test.go:101 -> github.com/cucumber/godog/internal/formatters_test.passingStepDef</bold-black>
1 scenarios (<green>1 passed</green>)
4 steps (<green>4 passed</green>)

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

@ -4,7 +4,7 @@
feature
<bold-white>Scenario:</bold-white> one step passing <bold-black># formatter-tests/features/single_scenario_with_passing_step.feature:6</bold-black>
<green>Given</green> <green>a passing step</green> <bold-black># fmt_output_test.go:101 -> github.com/cucumber/godog_test.passingStepDef</bold-black>
<green>Given</green> <green>a passing step</green> <bold-black># fmt_output_test.go:101 -> github.com/cucumber/godog/internal/formatters_test.passingStepDef</bold-black>
1 scenarios (<green>1 passed</green>)
1 steps (<green>1 passed</green>)

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

@ -1,19 +1,19 @@
<bold-white>Feature:</bold-white> some scenarios
<bold-white>Scenario:</bold-white> failing <bold-black># formatter-tests/features/some_scenarions_including_failing.feature:3</bold-black>
<green>Given</green> <green>passing step</green> <bold-black># fmt_output_test.go:101 -> github.com/cucumber/godog_test.passingStepDef</bold-black>
<red>When</red> <red>failing step</red> <bold-black># fmt_output_test.go:117 -> github.com/cucumber/godog_test.failingStepDef</bold-black>
<green>Given</green> <green>passing step</green> <bold-black># fmt_output_test.go:101 -> github.com/cucumber/godog/internal/formatters_test.passingStepDef</bold-black>
<red>When</red> <red>failing step</red> <bold-black># fmt_output_test.go:117 -> github.com/cucumber/godog/internal/formatters_test.failingStepDef</bold-black>
<bold-red>step failed</bold-red>
<cyan>Then</cyan> <cyan>passing step</cyan> <bold-black># fmt_output_test.go:101 -> github.com/cucumber/godog_test.passingStepDef</bold-black>
<cyan>Then</cyan> <cyan>passing step</cyan> <bold-black># fmt_output_test.go:101 -> github.com/cucumber/godog/internal/formatters_test.passingStepDef</bold-black>
<bold-white>Scenario:</bold-white> pending <bold-black># formatter-tests/features/some_scenarions_including_failing.feature:8</bold-black>
<yellow>When</yellow> <yellow>pending step</yellow> <bold-black># fmt_output_test.go:115 -> github.com/cucumber/godog_test.pendingStepDef</bold-black>
<yellow>When</yellow> <yellow>pending step</yellow> <bold-black># fmt_output_test.go:115 -> github.com/cucumber/godog/internal/formatters_test.pendingStepDef</bold-black>
<yellow>TODO: write pending definition</yellow>
<cyan>Then</cyan> <cyan>passing step</cyan> <bold-black># fmt_output_test.go:101 -> github.com/cucumber/godog_test.passingStepDef</bold-black>
<cyan>Then</cyan> <cyan>passing step</cyan> <bold-black># fmt_output_test.go:101 -> github.com/cucumber/godog/internal/formatters_test.passingStepDef</bold-black>
<bold-white>Scenario:</bold-white> undefined <bold-black># formatter-tests/features/some_scenarions_including_failing.feature:12</bold-black>
<yellow>When</yellow> <yellow>undefined</yellow>
<cyan>Then</cyan> <cyan>passing step</cyan> <bold-black># fmt_output_test.go:101 -> github.com/cucumber/godog_test.passingStepDef</bold-black>
<cyan>Then</cyan> <cyan>passing step</cyan> <bold-black># fmt_output_test.go:101 -> github.com/cucumber/godog/internal/formatters_test.passingStepDef</bold-black>
--- <red>Failed steps:</red>

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

@ -1,16 +1,16 @@
<bold-white>Feature:</bold-white> two scenarios with background fail
<bold-white>Background:</bold-white>
<green>Given</green> <green>passing step</green> <bold-black># fmt_output_test.go:101 -> github.com/cucumber/godog_test.passingStepDef</bold-black>
<red>And</red> <red>failing step</red> <bold-black># fmt_output_test.go:117 -> github.com/cucumber/godog_test.failingStepDef</bold-black>
<green>Given</green> <green>passing step</green> <bold-black># fmt_output_test.go:101 -> github.com/cucumber/godog/internal/formatters_test.passingStepDef</bold-black>
<red>And</red> <red>failing step</red> <bold-black># fmt_output_test.go:117 -> github.com/cucumber/godog/internal/formatters_test.failingStepDef</bold-black>
<bold-red>step failed</bold-red>
<bold-white>Scenario:</bold-white> one <bold-black># formatter-tests/features/two_scenarios_with_background_fail.feature:7</bold-black>
<cyan>When</cyan> <cyan>passing step</cyan> <bold-black># fmt_output_test.go:101 -> github.com/cucumber/godog_test.passingStepDef</bold-black>
<cyan>Then</cyan> <cyan>passing step</cyan> <bold-black># fmt_output_test.go:101 -> github.com/cucumber/godog_test.passingStepDef</bold-black>
<cyan>When</cyan> <cyan>passing step</cyan> <bold-black># fmt_output_test.go:101 -> github.com/cucumber/godog/internal/formatters_test.passingStepDef</bold-black>
<cyan>Then</cyan> <cyan>passing step</cyan> <bold-black># fmt_output_test.go:101 -> github.com/cucumber/godog/internal/formatters_test.passingStepDef</bold-black>
<bold-white>Scenario:</bold-white> two <bold-black># formatter-tests/features/two_scenarios_with_background_fail.feature:11</bold-black>
<cyan>Then</cyan> <cyan>passing step</cyan> <bold-black># fmt_output_test.go:101 -> github.com/cucumber/godog_test.passingStepDef</bold-black>
<cyan>Then</cyan> <cyan>passing step</cyan> <bold-black># fmt_output_test.go:101 -> github.com/cucumber/godog/internal/formatters_test.passingStepDef</bold-black>
--- <red>Failed steps:</red>

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

@ -1,4 +1,4 @@
package godog
package formatters
import (
"fmt"

24
internal/formatters/utils_test.go Обычный файл
Просмотреть файл

@ -0,0 +1,24 @@
package formatters
import (
"testing"
"time"
"github.com/cucumber/godog/internal/utils"
)
// this zeroes the time throughout whole test suite
// and makes it easier to assert output
// activated only when godog tests are being run
func init() {
utils.TimeNowFunc = func() time.Time {
return time.Time{}
}
}
func TestTimeNowFunc(t *testing.T) {
now := utils.TimeNowFunc()
if !now.IsZero() {
t.Fatalf("expected zeroed time, but got: %s", now.Format(time.RFC3339))
}
}

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

@ -1,22 +1,20 @@
package godog
package models
import (
"github.com/cucumber/messages-go/v10"
)
type feature struct {
// Feature is an internal object to group together
// the parsed gherkin document, the pickles and the
// raw content.
type Feature struct {
*messages.GherkinDocument
pickles []*messages.Pickle
content []byte
Pickles []*messages.Pickle
Content []byte
}
type sortFeaturesByName []*feature
func (s sortFeaturesByName) Len() int { return len(s) }
func (s sortFeaturesByName) Less(i, j int) bool { return s[i].Feature.Name < s[j].Feature.Name }
func (s sortFeaturesByName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (f feature) findScenario(astScenarioID string) *messages.GherkinDocument_Feature_Scenario {
// FindScenario ...
func (f Feature) FindScenario(astScenarioID string) *messages.GherkinDocument_Feature_Scenario {
for _, child := range f.GherkinDocument.Feature.Children {
if sc := child.GetScenario(); sc != nil && sc.Id == astScenarioID {
return sc
@ -26,7 +24,8 @@ func (f feature) findScenario(astScenarioID string) *messages.GherkinDocument_Fe
return nil
}
func (f feature) findBackground(astScenarioID string) *messages.GherkinDocument_Feature_Background {
// FindBackground ...
func (f Feature) FindBackground(astScenarioID string) *messages.GherkinDocument_Feature_Background {
var bg *messages.GherkinDocument_Feature_Background
for _, child := range f.GherkinDocument.Feature.Children {
@ -42,7 +41,8 @@ func (f feature) findBackground(astScenarioID string) *messages.GherkinDocument_
return nil
}
func (f feature) findExample(exampleAstID string) (*messages.GherkinDocument_Feature_Scenario_Examples, *messages.GherkinDocument_Feature_TableRow) {
// FindExample ...
func (f Feature) FindExample(exampleAstID string) (*messages.GherkinDocument_Feature_Scenario_Examples, *messages.GherkinDocument_Feature_TableRow) {
for _, child := range f.GherkinDocument.Feature.Children {
if sc := child.GetScenario(); sc != nil {
for _, example := range sc.Examples {
@ -58,7 +58,8 @@ func (f feature) findExample(exampleAstID string) (*messages.GherkinDocument_Fea
return nil, nil
}
func (f feature) findStep(astStepID string) *messages.GherkinDocument_Feature_Step {
// FindStep ...
func (f Feature) FindStep(astStepID string) *messages.GherkinDocument_Feature_Step {
for _, child := range f.GherkinDocument.Feature.Children {
if sc := child.GetScenario(); sc != nil {
for _, step := range sc.GetSteps() {

60
internal/models/feature_test.go Обычный файл
Просмотреть файл

@ -0,0 +1,60 @@
package models_test
import (
"testing"
"github.com/cucumber/godog/internal/testutils"
"github.com/stretchr/testify/assert"
)
func Test_Find(t *testing.T) {
ft := testutils.BuildTestFeature(t)
t.Run("scenario", func(t *testing.T) {
sc := ft.FindScenario(ft.Pickles[0].AstNodeIds[0])
assert.NotNilf(t, sc, "expected scenario to not be nil")
})
t.Run("background", func(t *testing.T) {
bg := ft.FindBackground(ft.Pickles[0].AstNodeIds[0])
assert.NotNilf(t, bg, "expected background to not be nil")
})
t.Run("example", func(t *testing.T) {
example, row := ft.FindExample(ft.Pickles[1].AstNodeIds[1])
assert.NotNilf(t, example, "expected example to not be nil")
assert.NotNilf(t, row, "expected table row to not be nil")
})
t.Run("step", func(t *testing.T) {
for _, ps := range ft.Pickles[0].Steps {
step := ft.FindStep(ps.AstNodeIds[0])
assert.NotNilf(t, step, "expected step to not be nil")
}
})
}
func Test_NotFind(t *testing.T) {
ft := testutils.BuildTestFeature(t)
t.Run("scenario", func(t *testing.T) {
sc := ft.FindScenario("-")
assert.Nilf(t, sc, "expected scenario to be nil")
})
t.Run("background", func(t *testing.T) {
bg := ft.FindBackground("-")
assert.Nilf(t, bg, "expected background to be nil")
})
t.Run("example", func(t *testing.T) {
example, row := ft.FindExample("-")
assert.Nilf(t, example, "expected example to be nil")
assert.Nilf(t, row, "expected table row to be nil")
})
t.Run("step", func(t *testing.T) {
step := ft.FindStep("-")
assert.Nilf(t, step, "expected step to be nil")
})
}

84
internal/models/results.go Обычный файл
Просмотреть файл

@ -0,0 +1,84 @@
package models
import (
"time"
"github.com/cucumber/godog/colors"
"github.com/cucumber/godog/internal/utils"
)
// TestRunStarted ...
type TestRunStarted struct {
StartedAt time.Time
}
// PickleResult ...
type PickleResult struct {
PickleID string
StartedAt time.Time
}
// PickleStepResult ...
type PickleStepResult struct {
Status StepResultStatus
FinishedAt time.Time
Err error
PickleID string
PickleStepID string
Def *StepDefinition
}
// NewStepResult ...
func NewStepResult(pickleID, pickleStepID string, match *StepDefinition) PickleStepResult {
return PickleStepResult{FinishedAt: utils.TimeNowFunc(), PickleID: pickleID, PickleStepID: pickleStepID, Def: match}
}
// StepResultStatus ...
type StepResultStatus int
const (
// Passed ...
Passed StepResultStatus = iota
// Failed ...
Failed
// Skipped ...
Skipped
// Undefined ...
Undefined
// Pending ...
Pending
)
// Color ...
func (st StepResultStatus) Color() colors.ColorFunc {
switch st {
case Passed:
return colors.Green
case Failed:
return colors.Red
case Skipped:
return colors.Cyan
default:
return colors.Yellow
}
}
// String ...
func (st StepResultStatus) String() string {
switch st {
case Passed:
return "passed"
case Failed:
return "failed"
case Skipped:
return "skipped"
case Undefined:
return "undefined"
case Pending:
return "pending"
default:
return "unknown"
}
}

34
internal/models/results_test.go Обычный файл
Просмотреть файл

@ -0,0 +1,34 @@
package models_test
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/cucumber/godog/colors"
"github.com/cucumber/godog/internal/models"
)
type stepResultStatusTestCase struct {
st models.StepResultStatus
str string
clr colors.ColorFunc
}
var stepResultStatusTestCases = []stepResultStatusTestCase{
{st: models.Passed, str: "passed", clr: colors.Green},
{st: models.Failed, str: "failed", clr: colors.Red},
{st: models.Skipped, str: "skipped", clr: colors.Cyan},
{st: models.Undefined, str: "undefined", clr: colors.Yellow},
{st: models.Pending, str: "pending", clr: colors.Yellow},
{st: -1, str: "unknown", clr: colors.Yellow},
}
func Test_StepResultStatus(t *testing.T) {
for _, tc := range stepResultStatusTestCases {
t.Run(tc.str, func(t *testing.T) {
assert.Equal(t, tc.str, tc.st.String())
assert.Equal(t, tc.clr(tc.str), tc.st.Color()(tc.str))
})
}
}

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

@ -1,35 +1,15 @@
package godog
package models
import (
"fmt"
"os"
"path/filepath"
"reflect"
"regexp"
"runtime"
"strconv"
"strings"
"github.com/cucumber/messages-go/v10"
)
var matchFuncDefRef = regexp.MustCompile(`\(([^\)]+)\)`)
// Steps allows to nest steps
// instead of returning an error in step func
// it is possible to return combined steps:
//
// func multistep(name string) godog.Steps {
// return godog.Steps{
// fmt.Sprintf(`an user named "%s"`, name),
// fmt.Sprintf(`user "%s" is authenticated`, name),
// }
// }
//
// These steps will be matched and executed in
// sequential order. The first one which fails
// will result in main step failure.
type Steps []string
var typeOfBytes = reflect.TypeOf([]byte(nil))
// StepDefinition is a registered step definition
// contains a StepHandler and regexp which
@ -40,50 +20,21 @@ type Steps []string
// when step is matched and is either failed
// or successful
type StepDefinition struct {
args []interface{}
hv reflect.Value
Expr *regexp.Regexp
Handler interface{}
Args []interface{}
HandlerValue reflect.Value
Expr *regexp.Regexp
Handler interface{}
// multistep related
nested bool
undefined []string
Nested bool
Undefined []string
}
func (sd *StepDefinition) definitionID() string {
ptr := sd.hv.Pointer()
f := runtime.FuncForPC(ptr)
file, line := f.FileLine(ptr)
dir := filepath.Dir(file)
fn := strings.Replace(f.Name(), dir, "", -1)
var parts []string
for _, gr := range matchFuncDefRef.FindAllStringSubmatch(fn, -1) {
parts = append(parts, strings.Trim(gr[1], "_."))
}
if len(parts) > 0 {
// case when suite is a structure with methods
fn = strings.Join(parts, ".")
} else {
// case when steps are just plain funcs
fn = strings.Trim(fn, "_.")
}
if pkg := os.Getenv("GODOG_TESTED_PACKAGE"); len(pkg) > 0 {
fn = strings.Replace(fn, pkg, "", 1)
fn = strings.TrimLeft(fn, ".")
fn = strings.Replace(fn, "..", ".", -1)
}
return fmt.Sprintf("%s:%d -> %s", filepath.Base(file), line, fn)
}
// run a step with the matched arguments using
// reflect
func (sd *StepDefinition) run() interface{} {
typ := sd.hv.Type()
if len(sd.args) < typ.NumIn() {
return fmt.Errorf("func expects %d arguments, which is more than %d matched from step", typ.NumIn(), len(sd.args))
// Run a step with the matched arguments using reflect
func (sd *StepDefinition) Run() interface{} {
typ := sd.HandlerValue.Type()
if len(sd.Args) < typ.NumIn() {
return fmt.Errorf("func expects %d arguments, which is more than %d matched from step", typ.NumIn(), len(sd.Args))
}
var values []reflect.Value
for i := 0; i < typ.NumIn(); i++ {
@ -166,7 +117,7 @@ func (sd *StepDefinition) run() interface{} {
}
values = append(values, reflect.ValueOf(float32(v)))
case reflect.Ptr:
arg := sd.args[i]
arg := sd.Args[i]
switch param.Elem().String() {
case "messages.PickleStepArgument_PickleDocString":
if v, ok := arg.(*messages.PickleStepArgument); ok {
@ -211,11 +162,11 @@ func (sd *StepDefinition) run() interface{} {
}
}
return sd.hv.Call(values)[0].Interface()
return sd.HandlerValue.Call(values)[0].Interface()
}
func (sd *StepDefinition) shouldBeString(idx int) (string, error) {
arg := sd.args[idx]
arg := sd.Args[idx]
s, ok := arg.(string)
if !ok {
return "", fmt.Errorf(`cannot convert argument %d: "%v" of type "%T" to string`, idx, arg, arg)

107
internal/models/stepdef_test.go Обычный файл
Просмотреть файл

@ -0,0 +1,107 @@
package models_test
import (
"reflect"
"strings"
"testing"
"github.com/cucumber/godog/internal/models"
"github.com/cucumber/messages-go/v10"
)
func TestShouldSupportIntTypes(t *testing.T) {
fn := func(a int64, b int32, c int16, d int8) error { return nil }
def := &models.StepDefinition{
Handler: fn,
HandlerValue: reflect.ValueOf(fn),
}
def.Args = []interface{}{"1", "1", "1", "1"}
if err := def.Run(); err != nil {
t.Fatalf("unexpected error: %v", err)
}
def.Args = []interface{}{"1", "1", "1", strings.Repeat("1", 9)}
if err := def.Run(); err == nil {
t.Fatalf("expected convertion fail for int8, but got none")
}
}
func TestShouldSupportFloatTypes(t *testing.T) {
fn := func(a float64, b float32) error { return nil }
def := &models.StepDefinition{
Handler: fn,
HandlerValue: reflect.ValueOf(fn),
}
def.Args = []interface{}{"1.1", "1.09"}
if err := def.Run(); err != nil {
t.Fatalf("unexpected error: %v", err)
}
def.Args = []interface{}{"1.08", strings.Repeat("1", 65) + ".67"}
if err := def.Run(); err == nil {
t.Fatalf("expected convertion fail for float32, but got none")
}
}
func TestShouldNotSupportOtherPointerTypesThanGherkin(t *testing.T) {
fn1 := func(a *int) error { return nil }
fn2 := func(a *messages.PickleStepArgument_PickleDocString) error { return nil }
fn3 := func(a *messages.PickleStepArgument_PickleTable) error { return nil }
def1 := &models.StepDefinition{Handler: fn1, HandlerValue: reflect.ValueOf(fn1), Args: []interface{}{(*int)(nil)}}
def2 := &models.StepDefinition{Handler: fn2, HandlerValue: reflect.ValueOf(fn2), Args: []interface{}{&messages.PickleStepArgument_PickleDocString{}}}
def3 := &models.StepDefinition{Handler: fn3, HandlerValue: reflect.ValueOf(fn3), Args: []interface{}{(*messages.PickleStepArgument_PickleTable)(nil)}}
if err := def1.Run(); err == nil {
t.Fatalf("expected conversion error, but got none")
}
if err := def2.Run(); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if err := def3.Run(); err != nil {
t.Fatalf("unexpected error: %v", err)
}
}
func TestShouldSupportOnlyByteSlice(t *testing.T) {
fn1 := func(a []byte) error { return nil }
fn2 := func(a []string) error { return nil }
def1 := &models.StepDefinition{Handler: fn1, HandlerValue: reflect.ValueOf(fn1), Args: []interface{}{"str"}}
def2 := &models.StepDefinition{Handler: fn2, HandlerValue: reflect.ValueOf(fn2), Args: []interface{}{[]string{}}}
if err := def1.Run(); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if err := def2.Run(); err == nil {
t.Fatalf("expected conversion error, but got none")
}
}
func TestUnexpectedArguments(t *testing.T) {
fn := func(a, b int) error { return nil }
def := &models.StepDefinition{Handler: fn, HandlerValue: reflect.ValueOf(fn)}
def.Args = []interface{}{"1"}
if err := def.Run(); err == nil {
t.Fatalf("expected an error due to wrong number of arguments, but got none")
}
def.Args = []interface{}{"one", "two"}
if err := def.Run(); err == nil {
t.Fatalf("expected conversion error, but got none")
}
// @TODO maybe we should support duration
// fn2 := func(err time.Duration) error { return nil }
// def = &models.StepDefinition{Handler: fn2, HandlerValue: reflect.ValueOf(fn2)}
// def.Args = []interface{}{"1"}
// if err := def.Run(); err == nil {
// t.Fatalf("expected an error due to wrong argument type, but got none")
// }
}

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

@ -1,4 +1,4 @@
package godog
package storage
import (
"fmt"
@ -6,6 +6,8 @@ import (
"github.com/cucumber/messages-go/v10"
"github.com/hashicorp/go-memdb"
"github.com/cucumber/godog/internal/models"
)
const (
@ -31,15 +33,17 @@ const (
tablePickleStepResultIndexStatus string = "status"
)
type storage struct {
// Storage is a thread safe in-mem storage
type Storage struct {
db *memdb.MemDB
testRunStarted testRunStarted
lock *sync.Mutex
testRunStarted models.TestRunStarted
testRunStartedLock *sync.Mutex
}
func newStorage() *storage {
// Create the DB schema
// NewStorage will create an in-mem storage that
// is used across concurrent runners and formatters
func NewStorage() *Storage {
schema := memdb.DBSchema{
Tables: map[string]*memdb.TableSchema{
tableFeature: {
@ -115,10 +119,12 @@ func newStorage() *storage {
panic(err)
}
return &storage{db: db, lock: new(sync.Mutex)}
return &Storage{db: db, testRunStartedLock: new(sync.Mutex)}
}
func (s *storage) mustInsertPickle(p *messages.Pickle) {
// MustInsertPickle will insert a pickle and it's steps,
// will panic on error.
func (s *Storage) MustInsertPickle(p *messages.Pickle) {
txn := s.db.Txn(writeMode)
if err := txn.Insert(tablePickle, p); err != nil {
@ -134,12 +140,14 @@ func (s *storage) mustInsertPickle(p *messages.Pickle) {
txn.Commit()
}
func (s *storage) mustGetPickle(id string) *messages.Pickle {
// MustGetPickle will retrieve a pickle by id and panic on error.
func (s *Storage) MustGetPickle(id string) *messages.Pickle {
v := s.mustFirst(tablePickle, tablePickleIndexID, id)
return v.(*messages.Pickle)
}
func (s *storage) mustGetPickles(uri string) (ps []*messages.Pickle) {
// MustGetPickles will retrieve pickles by URI and panic on error.
func (s *Storage) MustGetPickles(uri string) (ps []*messages.Pickle) {
it := s.mustGet(tablePickle, tablePickleIndexURI, uri)
for v := it.Next(); v != nil; v = it.Next() {
ps = append(ps, v.(*messages.Pickle))
@ -148,89 +156,102 @@ func (s *storage) mustGetPickles(uri string) (ps []*messages.Pickle) {
return
}
func (s *storage) mustGetPickleStep(id string) *messages.Pickle_PickleStep {
// MustGetPickleStep will retrieve a pickle step and panic on error.
func (s *Storage) MustGetPickleStep(id string) *messages.Pickle_PickleStep {
v := s.mustFirst(tablePickleStep, tablePickleStepIndexID, id)
return v.(*messages.Pickle_PickleStep)
}
func (s *storage) mustInsertTestRunStarted(trs testRunStarted) {
s.lock.Lock()
defer s.lock.Unlock()
// MustInsertTestRunStarted will set the test run started event and panic on error.
func (s *Storage) MustInsertTestRunStarted(trs models.TestRunStarted) {
s.testRunStartedLock.Lock()
defer s.testRunStartedLock.Unlock()
s.testRunStarted = trs
}
func (s *storage) mustGetTestRunStarted() testRunStarted {
s.lock.Lock()
defer s.lock.Unlock()
// MustGetTestRunStarted will retrieve the test run started event and panic on error.
func (s *Storage) MustGetTestRunStarted() models.TestRunStarted {
s.testRunStartedLock.Lock()
defer s.testRunStartedLock.Unlock()
return s.testRunStarted
}
func (s *storage) mustInsertPickleResult(pr pickleResult) {
// MustInsertPickleResult will instert a pickle result and panic on error.
func (s *Storage) MustInsertPickleResult(pr models.PickleResult) {
s.mustInsert(tablePickleResult, pr)
}
func (s *storage) mustInsertPickleStepResult(psr pickleStepResult) {
// MustInsertPickleStepResult will insert a pickle step result and panic on error.
func (s *Storage) MustInsertPickleStepResult(psr models.PickleStepResult) {
s.mustInsert(tablePickleStepResult, psr)
}
func (s *storage) mustGetPickleResult(id string) pickleResult {
// MustGetPickleResult will retrieve a pickle result by id and panic on error.
func (s *Storage) MustGetPickleResult(id string) models.PickleResult {
v := s.mustFirst(tablePickleResult, tablePickleResultIndexPickleID, id)
return v.(pickleResult)
return v.(models.PickleResult)
}
func (s *storage) mustGetPickleResults() (prs []pickleResult) {
// MustGetPickleResults will retrieve all pickle results and panic on error.
func (s *Storage) MustGetPickleResults() (prs []models.PickleResult) {
it := s.mustGet(tablePickleResult, tablePickleResultIndexPickleID)
for v := it.Next(); v != nil; v = it.Next() {
prs = append(prs, v.(pickleResult))
prs = append(prs, v.(models.PickleResult))
}
return prs
}
func (s *storage) mustGetPickleStepResult(id string) pickleStepResult {
// MustGetPickleStepResult will retrieve a pickle strep result by id and panic on error.
func (s *Storage) MustGetPickleStepResult(id string) models.PickleStepResult {
v := s.mustFirst(tablePickleStepResult, tablePickleStepResultIndexPickleStepID, id)
return v.(pickleStepResult)
return v.(models.PickleStepResult)
}
func (s *storage) mustGetPickleStepResultsByPickleID(pickleID string) (psrs []pickleStepResult) {
// MustGetPickleStepResultsByPickleID will retrieve pickle strep results by pickle id and panic on error.
func (s *Storage) MustGetPickleStepResultsByPickleID(pickleID string) (psrs []models.PickleStepResult) {
it := s.mustGet(tablePickleStepResult, tablePickleStepResultIndexPickleID, pickleID)
for v := it.Next(); v != nil; v = it.Next() {
psrs = append(psrs, v.(pickleStepResult))
psrs = append(psrs, v.(models.PickleStepResult))
}
return psrs
}
func (s *storage) mustGetPickleStepResultsByStatus(status stepResultStatus) (psrs []pickleStepResult) {
// MustGetPickleStepResultsByStatus will retrieve pickle strep results by status and panic on error.
func (s *Storage) MustGetPickleStepResultsByStatus(status models.StepResultStatus) (psrs []models.PickleStepResult) {
it := s.mustGet(tablePickleStepResult, tablePickleStepResultIndexStatus, status)
for v := it.Next(); v != nil; v = it.Next() {
psrs = append(psrs, v.(pickleStepResult))
psrs = append(psrs, v.(models.PickleStepResult))
}
return psrs
}
func (s *storage) mustInsertFeature(f *feature) {
// MustInsertFeature will insert a feature and panic on error.
func (s *Storage) MustInsertFeature(f *models.Feature) {
s.mustInsert(tableFeature, f)
}
func (s *storage) mustGetFeature(uri string) *feature {
// MustGetFeature will retrieve a feature by URI and panic on error.
func (s *Storage) MustGetFeature(uri string) *models.Feature {
v := s.mustFirst(tableFeature, tableFeatureIndexURI, uri)
return v.(*feature)
return v.(*models.Feature)
}
func (s *storage) mustGetFeatures() (fs []*feature) {
// MustGetFeatures will retrieve all features by and panic on error.
func (s *Storage) MustGetFeatures() (fs []*models.Feature) {
it := s.mustGet(tableFeature, tableFeatureIndexURI)
for v := it.Next(); v != nil; v = it.Next() {
fs = append(fs, v.(*feature))
fs = append(fs, v.(*models.Feature))
}
return
}
func (s *storage) mustInsert(table string, obj interface{}) {
func (s *Storage) mustInsert(table string, obj interface{}) {
txn := s.db.Txn(writeMode)
if err := txn.Insert(table, obj); err != nil {
@ -240,7 +261,7 @@ func (s *storage) mustInsert(table string, obj interface{}) {
txn.Commit()
}
func (s *storage) mustFirst(table, index string, args ...interface{}) interface{} {
func (s *Storage) mustFirst(table, index string, args ...interface{}) interface{} {
txn := s.db.Txn(readMode)
defer txn.Abort()
@ -255,7 +276,7 @@ func (s *storage) mustFirst(table, index string, args ...interface{}) interface{
return v
}
func (s *storage) mustGet(table, index string, args ...interface{}) memdb.ResultIterator {
func (s *Storage) mustGet(table, index string, args ...interface{}) memdb.ResultIterator {
txn := s.db.Txn(readMode)
defer txn.Abort()

187
internal/storage/storage_test.go Обычный файл
Просмотреть файл

@ -0,0 +1,187 @@
package storage_test
import (
"testing"
"time"
"github.com/cucumber/messages-go/v10"
"github.com/stretchr/testify/assert"
"github.com/cucumber/godog/internal/models"
"github.com/cucumber/godog/internal/storage"
"github.com/cucumber/godog/internal/testutils"
)
func Test_MustGetPickle(t *testing.T) {
s := storage.NewStorage()
ft := testutils.BuildTestFeature(t)
expected := ft.Pickles[0]
s.MustInsertPickle(expected)
actual := s.MustGetPickle(expected.Id)
assert.Equal(t, expected, actual)
}
func Test_MustGetPickles(t *testing.T) {
s := storage.NewStorage()
ft := testutils.BuildTestFeature(t)
expected := ft.Pickles
for _, pickle := range expected {
s.MustInsertPickle(pickle)
}
actual := s.MustGetPickles(ft.Uri)
assert.Equal(t, expected, actual)
}
func Test_MustGetPickleStep(t *testing.T) {
s := storage.NewStorage()
ft := testutils.BuildTestFeature(t)
for _, pickle := range ft.Pickles {
s.MustInsertPickle(pickle)
}
for _, pickle := range ft.Pickles {
for _, expected := range pickle.Steps {
actual := s.MustGetPickleStep(expected.Id)
assert.Equal(t, expected, actual)
}
}
}
func Test_MustGetTestRunStarted(t *testing.T) {
s := storage.NewStorage()
expected := models.TestRunStarted{StartedAt: time.Now()}
s.MustInsertTestRunStarted(expected)
actual := s.MustGetTestRunStarted()
assert.Equal(t, expected, actual)
}
func Test_MustGetPickleResult(t *testing.T) {
s := storage.NewStorage()
const pickleID = "1"
expected := models.PickleResult{PickleID: pickleID}
s.MustInsertPickleResult(expected)
actual := s.MustGetPickleResult(pickleID)
assert.Equal(t, expected, actual)
}
func Test_MustGetPickleResults(t *testing.T) {
s := storage.NewStorage()
expected := []models.PickleResult{{PickleID: "1"}, {PickleID: "2"}}
for _, pr := range expected {
s.MustInsertPickleResult(pr)
}
actual := s.MustGetPickleResults()
assert.Equal(t, expected, actual)
}
func Test_MustGetPickleStepResult(t *testing.T) {
s := storage.NewStorage()
const pickleID = "1"
const stepID = "2"
expected := models.PickleStepResult{
Status: models.Passed,
PickleID: pickleID,
PickleStepID: stepID,
}
s.MustInsertPickleStepResult(expected)
actual := s.MustGetPickleStepResult(stepID)
assert.Equal(t, expected, actual)
}
func Test_MustGetPickleStepResultsByPickleID(t *testing.T) {
s := storage.NewStorage()
const pickleID = "p1"
expected := []models.PickleStepResult{
{
Status: models.Passed,
PickleID: pickleID,
PickleStepID: "s1",
},
{
Status: models.Passed,
PickleID: pickleID,
PickleStepID: "s2",
},
}
for _, psr := range expected {
s.MustInsertPickleStepResult(psr)
}
actual := s.MustGetPickleStepResultsByPickleID(pickleID)
assert.Equal(t, expected, actual)
}
func Test_MustGetPickleStepResultsByStatus(t *testing.T) {
s := storage.NewStorage()
const pickleID = "p1"
expected := []models.PickleStepResult{
{
Status: models.Passed,
PickleID: pickleID,
PickleStepID: "s1",
},
}
testdata := []models.PickleStepResult{
expected[0],
{
Status: models.Failed,
PickleID: pickleID,
PickleStepID: "s2",
},
}
for _, psr := range testdata {
s.MustInsertPickleStepResult(psr)
}
actual := s.MustGetPickleStepResultsByStatus(models.Passed)
assert.Equal(t, expected, actual)
}
func Test_MustGetFeature(t *testing.T) {
s := storage.NewStorage()
const uri = "<uri>"
expected := &models.Feature{GherkinDocument: &messages.GherkinDocument{Uri: uri}}
s.MustInsertFeature(expected)
actual := s.MustGetFeature(uri)
assert.Equal(t, expected, actual)
}
func Test_MustGetFeatures(t *testing.T) {
s := storage.NewStorage()
expected := []*models.Feature{
{GherkinDocument: &messages.GherkinDocument{Uri: "uri1"}},
{GherkinDocument: &messages.GherkinDocument{Uri: "uri2"}},
}
for _, f := range expected {
s.MustInsertFeature(f)
}
actual := s.MustGetFeatures()
assert.Equal(t, expected, actual)
}

60
internal/testutils/utils.go Обычный файл
Просмотреть файл

@ -0,0 +1,60 @@
package testutils
import (
"strings"
"testing"
"github.com/cucumber/gherkin-go/v11"
"github.com/cucumber/messages-go/v10"
"github.com/stretchr/testify/require"
"github.com/cucumber/godog/internal/models"
)
// BuildTestFeature creates a feature for testing purpose.
//
// The created feature includes:
// - a background
// - one normal scenario with three steps
// - one outline scenario with one example and three steps
func BuildTestFeature(t *testing.T) models.Feature {
newIDFunc := (&messages.Incrementing{}).NewId
gherkinDocument, err := gherkin.ParseGherkinDocument(strings.NewReader(featureContent), newIDFunc)
require.NoError(t, err)
path := t.Name()
gherkinDocument.Uri = path
pickles := gherkin.Pickles(*gherkinDocument, path, newIDFunc)
ft := models.Feature{GherkinDocument: gherkinDocument, Pickles: pickles, Content: []byte(featureContent)}
require.Len(t, ft.Pickles, 2)
require.Len(t, ft.Pickles[0].AstNodeIds, 1)
require.Len(t, ft.Pickles[0].Steps, 3)
require.Len(t, ft.Pickles[1].AstNodeIds, 2)
require.Len(t, ft.Pickles[1].Steps, 3)
return ft
}
const featureContent = `Feature: eat godogs
In order to be happy
As a hungry gopher
I need to be able to eat godogs
Background:
Given there are <begin> godogs
Scenario: Eat 5 out of 12
When I eat 5
Then there should be 7 remaining
Scenario Outline: Eat <dec> out of <beginning>
When I eat <dec>
Then there should be <remain> remaining
Examples:
| begin | dec | remain |
| 12 | 5 | 7 |`

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

@ -0,0 +1,21 @@
package utils
import (
"strings"
"time"
)
// S repeats a space n times
func S(n int) string {
if n < 0 {
n = 1
}
return strings.Repeat(" ", n)
}
// TimeNowFunc is a utility function to simply testing
// by allowing TimeNowFunc to be defined to zero time
// to remove the time domain from tests
var TimeNowFunc = func() time.Time {
return time.Now()
}

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

@ -13,6 +13,7 @@ import (
"github.com/cucumber/gherkin-go/v11"
"github.com/cucumber/messages-go/v10"
"github.com/cucumber/godog/internal/models"
"github.com/cucumber/godog/internal/tags"
)
@ -30,7 +31,7 @@ func extractFeaturePathLine(p string) (string, int) {
return retPath, line
}
func parseFeatureFile(path string, newIDFunc func() string) (*feature, error) {
func parseFeatureFile(path string, newIDFunc func() string) (*models.Feature, error) {
reader, err := os.Open(path)
if err != nil {
return nil, err
@ -47,12 +48,12 @@ func parseFeatureFile(path string, newIDFunc func() string) (*feature, error) {
gherkinDocument.Uri = path
pickles := gherkin.Pickles(*gherkinDocument, path, newIDFunc)
f := feature{GherkinDocument: gherkinDocument, pickles: pickles, content: buf.Bytes()}
f := models.Feature{GherkinDocument: gherkinDocument, Pickles: pickles, Content: buf.Bytes()}
return &f, nil
}
func parseFeatureDir(dir string, newIDFunc func() string) ([]*feature, error) {
var features []*feature
func parseFeatureDir(dir string, newIDFunc func() string) ([]*models.Feature, error) {
var features []*models.Feature
return features, filepath.Walk(dir, func(p string, f os.FileInfo, err error) error {
if err != nil {
return err
@ -76,8 +77,8 @@ func parseFeatureDir(dir string, newIDFunc func() string) ([]*feature, error) {
})
}
func parsePath(path string) ([]*feature, error) {
var features []*feature
func parsePath(path string) ([]*models.Feature, error) {
var features []*models.Feature
path, line := extractFeaturePathLine(path)
@ -99,23 +100,23 @@ func parsePath(path string) ([]*feature, error) {
// filter scenario by line number
var pickles []*messages.Pickle
for _, pickle := range ft.pickles {
sc := ft.findScenario(pickle.AstNodeIds[0])
for _, pickle := range ft.Pickles {
sc := ft.FindScenario(pickle.AstNodeIds[0])
if line == -1 || uint32(line) == sc.Location.Line {
pickles = append(pickles, pickle)
}
}
ft.pickles = pickles
ft.Pickles = pickles
return append(features, ft), nil
}
func parseFeatures(filter string, paths []string) ([]*feature, error) {
func parseFeatures(filter string, paths []string) ([]*models.Feature, error) {
var order int
featureIdxs := make(map[string]int)
uniqueFeatureURI := make(map[string]*feature)
uniqueFeatureURI := make(map[string]*models.Feature)
for _, path := range paths {
feats, err := parsePath(path)
@ -140,7 +141,7 @@ func parseFeatures(filter string, paths []string) ([]*feature, error) {
}
}
var features = make([]*feature, len(uniqueFeatureURI))
var features = make([]*models.Feature, len(uniqueFeatureURI))
for uri, feature := range uniqueFeatureURI {
idx := featureIdxs[uri]
features[idx] = feature
@ -151,11 +152,11 @@ func parseFeatures(filter string, paths []string) ([]*feature, error) {
return features, nil
}
func filterFeatures(filter string, features []*feature) (result []*feature) {
func filterFeatures(filter string, features []*models.Feature) (result []*models.Feature) {
for _, ft := range features {
ft.pickles = tags.ApplyTagFilter(filter, ft.pickles)
ft.Pickles = tags.ApplyTagFilter(filter, ft.Pickles)
if ft.Feature != nil && len(ft.pickles) > 0 {
if ft.Feature != nil && len(ft.Pickles) > 0 {
result = append(result, ft)
}
}

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

@ -1,81 +0,0 @@
package godog
import (
"time"
"github.com/cucumber/godog/colors"
)
type testRunStarted struct {
StartedAt time.Time
}
type pickleResult struct {
PickleID string
StartedAt time.Time
}
type pickleStepResult struct {
Status stepResultStatus
finishedAt time.Time
err error
PickleID string
PickleStepID string
def *StepDefinition
}
func newStepResult(pickleID, pickleStepID string, match *StepDefinition) pickleStepResult {
return pickleStepResult{finishedAt: timeNowFunc(), PickleID: pickleID, PickleStepID: pickleStepID, def: match}
}
type sortPickleStepResultsByPickleStepID []pickleStepResult
func (s sortPickleStepResultsByPickleStepID) Len() int { return len(s) }
func (s sortPickleStepResultsByPickleStepID) Less(i, j int) bool {
iID := mustConvertStringToInt(s[i].PickleStepID)
jID := mustConvertStringToInt(s[j].PickleStepID)
return iID < jID
}
func (s sortPickleStepResultsByPickleStepID) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
type stepResultStatus int
const (
passed stepResultStatus = iota
failed
skipped
undefined
pending
)
func (st stepResultStatus) clr() colors.ColorFunc {
switch st {
case passed:
return green
case failed:
return red
case skipped:
return cyan
default:
return yellow
}
}
func (st stepResultStatus) String() string {
switch st {
case passed:
return "passed"
case failed:
return "failed"
case skipped:
return "skipped"
case undefined:
return "undefined"
case pending:
return "pending"
default:
return "unknown"
}
}

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

@ -12,8 +12,12 @@ import (
"strings"
"sync"
"github.com/cucumber/godog/colors"
"github.com/cucumber/messages-go/v10"
"github.com/cucumber/godog/colors"
"github.com/cucumber/godog/internal/models"
"github.com/cucumber/godog/internal/storage"
"github.com/cucumber/godog/internal/utils"
)
const (
@ -29,12 +33,12 @@ type runner struct {
randomSeed int64
stopOnFailure, strict bool
features []*feature
features []*models.Feature
testSuiteInitializer testSuiteInitializer
scenarioInitializer scenarioInitializer
storage *storage
storage *storage.Storage
fmt Formatter
}
@ -42,7 +46,7 @@ func (r *runner) concurrent(rate int) (failed bool) {
var copyLock sync.Mutex
if fmt, ok := r.fmt.(storageFormatter); ok {
fmt.setStorage(r.storage)
fmt.SetStorage(r.storage)
}
testSuiteContext := TestSuiteContext{}
@ -50,8 +54,8 @@ func (r *runner) concurrent(rate int) (failed bool) {
r.testSuiteInitializer(&testSuiteContext)
}
testRunStarted := testRunStarted{StartedAt: timeNowFunc()}
r.storage.mustInsertTestRunStarted(testRunStarted)
testRunStarted := models.TestRunStarted{StartedAt: utils.TimeNowFunc()}
r.storage.MustInsertTestRunStarted(testRunStarted)
r.fmt.TestRunStarted()
// run before suite handlers
@ -61,15 +65,15 @@ func (r *runner) concurrent(rate int) (failed bool) {
queue := make(chan int, rate)
for _, ft := range r.features {
pickles := make([]*messages.Pickle, len(ft.pickles))
pickles := make([]*messages.Pickle, len(ft.Pickles))
if r.randomSeed != 0 {
r := rand.New(rand.NewSource(r.randomSeed))
perm := r.Perm(len(ft.pickles))
perm := r.Perm(len(ft.Pickles))
for i, v := range perm {
pickles[v] = ft.pickles[i]
pickles[v] = ft.Pickles[i]
}
} else {
copy(pickles, ft.pickles)
copy(pickles, ft.Pickles)
}
for i, p := range pickles {
@ -78,7 +82,7 @@ func (r *runner) concurrent(rate int) (failed bool) {
queue <- i // reserve space in queue
if i == 0 {
r.fmt.Feature(ft.GherkinDocument, ft.Uri, ft.content)
r.fmt.Feature(ft.GherkinDocument, ft.Uri, ft.Content)
}
go func(fail *bool, pickle *messages.Pickle) {
@ -181,12 +185,12 @@ func runWithOptions(suiteName string, runner runner, opt Options) int {
return exitOptionError
}
runner.storage = newStorage()
runner.storage = storage.NewStorage()
for _, feat := range runner.features {
runner.storage.mustInsertFeature(feat)
runner.storage.MustInsertFeature(feat)
for _, pickle := range feat.pickles {
runner.storage.mustInsertPickle(pickle)
for _, pickle := range feat.Pickles {
runner.storage.MustInsertPickle(pickle)
}
}

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

@ -11,6 +11,9 @@ import (
"github.com/stretchr/testify/require"
"github.com/cucumber/godog/colors"
"github.com/cucumber/godog/internal/formatters"
"github.com/cucumber/godog/internal/models"
"github.com/cucumber/godog/internal/storage"
)
var basicGherkinFeature = `
@ -28,31 +31,31 @@ func Test_ProgressFormatterWhenStepPanics(t *testing.T) {
require.NoError(t, err)
gd.Uri = path
ft := feature{GherkinDocument: gd}
ft.pickles = gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId)
ft := models.Feature{GherkinDocument: gd}
ft.Pickles = gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId)
var buf bytes.Buffer
w := colors.Uncolored(&buf)
r := runner{
fmt: progressFunc("progress", w),
features: []*feature{&ft},
fmt: formatters.ProgressFormatterFunc("progress", w),
features: []*models.Feature{&ft},
scenarioInitializer: func(ctx *ScenarioContext) {
ctx.Step(`^one$`, func() error { return nil })
ctx.Step(`^two$`, func() error { panic("omg") })
},
}
r.storage = newStorage()
r.storage.mustInsertFeature(&ft)
for _, pickle := range ft.pickles {
r.storage.mustInsertPickle(pickle)
r.storage = storage.NewStorage()
r.storage.MustInsertFeature(&ft)
for _, pickle := range ft.Pickles {
r.storage.MustInsertPickle(pickle)
}
failed := r.concurrent(1)
require.True(t, failed)
actual := buf.String()
assert.Contains(t, actual, "godog/fmt_progress_test.go:41")
assert.Contains(t, actual, "godog/run_progress_test.go:44")
}
func Test_ProgressFormatterWithPanicInMultistep(t *testing.T) {
@ -62,14 +65,14 @@ func Test_ProgressFormatterWithPanicInMultistep(t *testing.T) {
require.NoError(t, err)
gd.Uri = path
ft := feature{GherkinDocument: gd}
ft.pickles = gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId)
ft := models.Feature{GherkinDocument: gd}
ft.Pickles = gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId)
var buf bytes.Buffer
w := colors.Uncolored(&buf)
r := runner{
fmt: progressFunc("progress", w),
features: []*feature{&ft},
fmt: formatters.ProgressFormatterFunc("progress", w),
features: []*models.Feature{&ft},
scenarioInitializer: func(ctx *ScenarioContext) {
ctx.Step(`^sub1$`, func() error { return nil })
ctx.Step(`^sub-sub$`, func() error { return nil })
@ -79,10 +82,10 @@ func Test_ProgressFormatterWithPanicInMultistep(t *testing.T) {
},
}
r.storage = newStorage()
r.storage.mustInsertFeature(&ft)
for _, pickle := range ft.pickles {
r.storage.mustInsertPickle(pickle)
r.storage = storage.NewStorage()
r.storage.MustInsertFeature(&ft)
for _, pickle := range ft.Pickles {
r.storage.MustInsertPickle(pickle)
}
failed := r.concurrent(1)
@ -96,14 +99,14 @@ func Test_ProgressFormatterMultistepTemplates(t *testing.T) {
require.NoError(t, err)
gd.Uri = path
ft := feature{GherkinDocument: gd}
ft.pickles = gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId)
ft := models.Feature{GherkinDocument: gd}
ft.Pickles = gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId)
var buf bytes.Buffer
w := colors.Uncolored(&buf)
r := runner{
fmt: progressFunc("progress", w),
features: []*feature{&ft},
fmt: formatters.ProgressFormatterFunc("progress", w),
features: []*models.Feature{&ft},
scenarioInitializer: func(ctx *ScenarioContext) {
ctx.Step(`^sub-sub$`, func() error { return nil })
ctx.Step(`^substep$`, func() Steps { return Steps{"sub-sub", `unavailable "John" cost 5`, "one", "three"} })
@ -112,10 +115,10 @@ func Test_ProgressFormatterMultistepTemplates(t *testing.T) {
},
}
r.storage = newStorage()
r.storage.mustInsertFeature(&ft)
for _, pickle := range ft.pickles {
r.storage.mustInsertPickle(pickle)
r.storage = storage.NewStorage()
r.storage.MustInsertFeature(&ft)
for _, pickle := range ft.Pickles {
r.storage.MustInsertPickle(pickle)
}
failed := r.concurrent(1)
@ -172,24 +175,24 @@ Feature: basic
require.NoError(t, err)
gd.Uri = path
ft := feature{GherkinDocument: gd}
ft.pickles = gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId)
ft := models.Feature{GherkinDocument: gd}
ft.Pickles = gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId)
var buf bytes.Buffer
w := colors.Uncolored(&buf)
r := runner{
fmt: progressFunc("progress", w),
features: []*feature{&ft},
fmt: formatters.ProgressFormatterFunc("progress", w),
features: []*models.Feature{&ft},
scenarioInitializer: func(ctx *ScenarioContext) {
ctx.Step(`^one$`, func() error { return nil })
ctx.Step(`^two:$`, func(doc *messages.PickleStepArgument_PickleDocString) Steps { return Steps{"one"} })
},
}
r.storage = newStorage()
r.storage.mustInsertFeature(&ft)
for _, pickle := range ft.pickles {
r.storage.mustInsertPickle(pickle)
r.storage = storage.NewStorage()
r.storage.MustInsertFeature(&ft)
for _, pickle := range ft.Pickles {
r.storage.MustInsertPickle(pickle)
}
failed := r.concurrent(1)
@ -210,8 +213,8 @@ Feature: basic
require.NoError(t, err)
gd.Uri = path
ft := feature{GherkinDocument: gd}
ft.pickles = gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId)
ft := models.Feature{GherkinDocument: gd}
ft.Pickles = gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId)
var subStep = `three:
"""
@ -221,8 +224,8 @@ Feature: basic
var buf bytes.Buffer
w := colors.Uncolored(&buf)
r := runner{
fmt: progressFunc("progress", w),
features: []*feature{&ft},
fmt: formatters.ProgressFormatterFunc("progress", w),
features: []*models.Feature{&ft},
scenarioInitializer: func(ctx *ScenarioContext) {
ctx.Step(`^one$`, func() error { return nil })
ctx.Step(`^two$`, func() Steps { return Steps{subStep} })
@ -230,10 +233,10 @@ Feature: basic
},
}
r.storage = newStorage()
r.storage.mustInsertFeature(&ft)
for _, pickle := range ft.pickles {
r.storage.mustInsertPickle(pickle)
r.storage = storage.NewStorage()
r.storage.MustInsertFeature(&ft)
for _, pickle := range ft.Pickles {
r.storage.MustInsertPickle(pickle)
}
failed := r.concurrent(1)

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

@ -17,6 +17,9 @@ import (
"github.com/stretchr/testify/require"
"github.com/cucumber/godog/colors"
"github.com/cucumber/godog/internal/formatters"
"github.com/cucumber/godog/internal/models"
"github.com/cucumber/godog/internal/storage"
)
func okStep() error {
@ -70,22 +73,22 @@ func Test_FailsOrPassesBasedOnStrictModeWhenHasPendingSteps(t *testing.T) {
require.NoError(t, err)
gd.Uri = path
ft := feature{GherkinDocument: gd}
ft.pickles = gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId)
ft := models.Feature{GherkinDocument: gd}
ft.Pickles = gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId)
r := runner{
fmt: progressFunc("progress", ioutil.Discard),
features: []*feature{&ft},
fmt: formatters.ProgressFormatterFunc("progress", ioutil.Discard),
features: []*models.Feature{&ft},
scenarioInitializer: func(ctx *ScenarioContext) {
ctx.Step(`^one$`, func() error { return nil })
ctx.Step(`^two$`, func() error { return ErrPending })
},
}
r.storage = newStorage()
r.storage.mustInsertFeature(&ft)
for _, pickle := range ft.pickles {
r.storage.mustInsertPickle(pickle)
r.storage = storage.NewStorage()
r.storage.MustInsertFeature(&ft)
for _, pickle := range ft.Pickles {
r.storage.MustInsertPickle(pickle)
}
failed := r.concurrent(1)
@ -103,22 +106,22 @@ func Test_FailsOrPassesBasedOnStrictModeWhenHasUndefinedSteps(t *testing.T) {
require.NoError(t, err)
gd.Uri = path
ft := feature{GherkinDocument: gd}
ft.pickles = gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId)
ft := models.Feature{GherkinDocument: gd}
ft.Pickles = gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId)
r := runner{
fmt: progressFunc("progress", ioutil.Discard),
features: []*feature{&ft},
fmt: formatters.ProgressFormatterFunc("progress", ioutil.Discard),
features: []*models.Feature{&ft},
scenarioInitializer: func(ctx *ScenarioContext) {
ctx.Step(`^one$`, func() error { return nil })
// two - is undefined
},
}
r.storage = newStorage()
r.storage.mustInsertFeature(&ft)
for _, pickle := range ft.pickles {
r.storage.mustInsertPickle(pickle)
r.storage = storage.NewStorage()
r.storage.MustInsertFeature(&ft)
for _, pickle := range ft.Pickles {
r.storage.MustInsertPickle(pickle)
}
failed := r.concurrent(1)
@ -136,22 +139,22 @@ func Test_ShouldFailOnError(t *testing.T) {
require.NoError(t, err)
gd.Uri = path
ft := feature{GherkinDocument: gd}
ft.pickles = gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId)
ft := models.Feature{GherkinDocument: gd}
ft.Pickles = gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId)
r := runner{
fmt: progressFunc("progress", ioutil.Discard),
features: []*feature{&ft},
fmt: formatters.ProgressFormatterFunc("progress", ioutil.Discard),
features: []*models.Feature{&ft},
scenarioInitializer: func(ctx *ScenarioContext) {
ctx.Step(`^one$`, func() error { return nil })
ctx.Step(`^two$`, func() error { return fmt.Errorf("error") })
ctx.Step(`^one$`, func() error { return nil })
},
}
r.storage = newStorage()
r.storage.mustInsertFeature(&ft)
for _, pickle := range ft.pickles {
r.storage.mustInsertPickle(pickle)
r.storage = storage.NewStorage()
r.storage.MustInsertFeature(&ft)
for _, pickle := range ft.Pickles {
r.storage.MustInsertPickle(pickle)
}
failed := r.concurrent(1)
@ -284,7 +287,7 @@ func Test_RandomizeRun(t *testing.T) {
const createRandomSeedFlag = -1
const noConcurrencyFlag = 1
const formatter = "pretty"
const featurePath = "formatter-tests/features/with_few_empty_scenarios.feature"
const featurePath = "internal/formatters/formatter-tests/features/with_few_empty_scenarios.feature"
fmtOutputScenarioInitializer := func(ctx *ScenarioContext) {
ctx.Step(`^(?:a )?failing step`, failingStepDef)
@ -367,7 +370,7 @@ func Test_FormatterConcurrencyRun(t *testing.T) {
"cucumber",
}
featurePaths := []string{"formatter-tests/features"}
featurePaths := []string{"internal/formatters/formatter-tests/features"}
const concurrency = 100
const noRandomFlag = 0

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

@ -24,7 +24,7 @@ func callstack3() *stack {
return &st
}
func TestStacktrace(t *testing.T) {
func Test_Stacktrace(t *testing.T) {
err := &traceError{
msg: "err msg",
stack: callstack1(),

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

@ -1,106 +0,0 @@
package godog
import (
"reflect"
"strings"
"testing"
"github.com/cucumber/messages-go/v10"
)
func TestShouldSupportIntTypes(t *testing.T) {
fn := func(a int64, b int32, c int16, d int8) error { return nil }
def := &StepDefinition{
Handler: fn,
hv: reflect.ValueOf(fn),
}
def.args = []interface{}{"1", "1", "1", "1"}
if err := def.run(); err != nil {
t.Fatalf("unexpected error: %v", err)
}
def.args = []interface{}{"1", "1", "1", strings.Repeat("1", 9)}
if err := def.run(); err == nil {
t.Fatalf("expected convertion fail for int8, but got none")
}
}
func TestShouldSupportFloatTypes(t *testing.T) {
fn := func(a float64, b float32) error { return nil }
def := &StepDefinition{
Handler: fn,
hv: reflect.ValueOf(fn),
}
def.args = []interface{}{"1.1", "1.09"}
if err := def.run(); err != nil {
t.Fatalf("unexpected error: %v", err)
}
def.args = []interface{}{"1.08", strings.Repeat("1", 65) + ".67"}
if err := def.run(); err == nil {
t.Fatalf("expected convertion fail for float32, but got none")
}
}
func TestShouldNotSupportOtherPointerTypesThanGherkin(t *testing.T) {
fn1 := func(a *int) error { return nil }
fn2 := func(a *messages.PickleStepArgument_PickleDocString) error { return nil }
fn3 := func(a *messages.PickleStepArgument_PickleTable) error { return nil }
def1 := &StepDefinition{Handler: fn1, hv: reflect.ValueOf(fn1), args: []interface{}{(*int)(nil)}}
def2 := &StepDefinition{Handler: fn2, hv: reflect.ValueOf(fn2), args: []interface{}{&messages.PickleStepArgument_PickleDocString{}}}
def3 := &StepDefinition{Handler: fn3, hv: reflect.ValueOf(fn3), args: []interface{}{(*messages.PickleStepArgument_PickleTable)(nil)}}
if err := def1.run(); err == nil {
t.Fatalf("expected conversion error, but got none")
}
if err := def2.run(); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if err := def3.run(); err != nil {
t.Fatalf("unexpected error: %v", err)
}
}
func TestShouldSupportOnlyByteSlice(t *testing.T) {
fn1 := func(a []byte) error { return nil }
fn2 := func(a []string) error { return nil }
def1 := &StepDefinition{Handler: fn1, hv: reflect.ValueOf(fn1), args: []interface{}{"str"}}
def2 := &StepDefinition{Handler: fn2, hv: reflect.ValueOf(fn2), args: []interface{}{[]string{}}}
if err := def1.run(); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if err := def2.run(); err == nil {
t.Fatalf("expected conversion error, but got none")
}
}
func TestUnexpectedArguments(t *testing.T) {
fn := func(a, b int) error { return nil }
def := &StepDefinition{Handler: fn, hv: reflect.ValueOf(fn)}
def.args = []interface{}{"1"}
if err := def.run(); err == nil {
t.Fatalf("expected an error due to wrong number of arguments, but got none")
}
def.args = []interface{}{"one", "two"}
if err := def.run(); err == nil {
t.Fatalf("expected conversion error, but got none")
}
// @TODO maybe we should support duration
// fn2 := func(err time.Duration) error { return nil }
// def = &StepDefinition{Handler: fn2, hv: reflect.ValueOf(fn2)}
// def.args = []interface{}{"1"}
// if err := def.run(); err == nil {
// t.Fatalf("expected an error due to wrong argument type, but got none")
// }
}

Показаны не все изменённые файлы, т.к. их слишком много Показать больше