implementation of event stream formatter
Этот коммит содержится в:
		
							родитель
							
								
									49e59d9296
								
							
						
					
					
						коммит
						34acb056ea
					
				
					 7 изменённых файлов: 302 добавлений и 14 удалений
				
			
		
							
								
								
									
										26
									
								
								fmt.go
									
										
									
									
									
								
							
							
						
						
									
										26
									
								
								fmt.go
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -84,8 +84,9 @@ func Format(name, description string, f Formatter) {
 | 
			
		|||
// formatters needs to be registered with a
 | 
			
		||||
// godog.Format function call
 | 
			
		||||
type Formatter interface {
 | 
			
		||||
	Feature(*gherkin.Feature, string)
 | 
			
		||||
	Feature(*gherkin.Feature, string, []byte)
 | 
			
		||||
	Node(interface{})
 | 
			
		||||
	Defined(*gherkin.Step, *StepDef)
 | 
			
		||||
	Failed(*gherkin.Step, *StepDef, error)
 | 
			
		||||
	Passed(*gherkin.Step, *StepDef)
 | 
			
		||||
	Skipped(*gherkin.Step)
 | 
			
		||||
| 
						 | 
				
			
			@ -117,6 +118,23 @@ func (st stepType) clr() color {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (st stepType) 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"
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type stepResult struct {
 | 
			
		||||
	typ     stepType
 | 
			
		||||
	feature *feature
 | 
			
		||||
| 
						 | 
				
			
			@ -152,7 +170,11 @@ func (f *basefmt) Node(n interface{}) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *basefmt) Feature(ft *gherkin.Feature, p string) {
 | 
			
		||||
func (f *basefmt) Defined(*gherkin.Step, *StepDef) {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *basefmt) Feature(ft *gherkin.Feature, p string, c []byte) {
 | 
			
		||||
	f.features = append(f.features, &feature{Path: p, Feature: ft})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										256
									
								
								fmt_events.go
									
										
									
									
									
										Обычный файл
									
								
							
							
						
						
									
										256
									
								
								fmt_events.go
									
										
									
									
									
										Обычный файл
									
								
							| 
						 | 
				
			
			@ -0,0 +1,256 @@
 | 
			
		|||
package godog
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/sha1"
 | 
			
		||||
	"encoding/hex"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"gopkg.in/cucumber/gherkin-go.v3"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const nanoSec = 1000000
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	Format("events", "Produces a JSON event stream.", &events{
 | 
			
		||||
		basefmt: basefmt{
 | 
			
		||||
			started: time.Now(),
 | 
			
		||||
			indent:  2,
 | 
			
		||||
		},
 | 
			
		||||
		runID: sha1RunID(),
 | 
			
		||||
		suite: "main",
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func sha1RunID() string {
 | 
			
		||||
	data, err := json.Marshal(&struct {
 | 
			
		||||
		Time   int64
 | 
			
		||||
		Runner string
 | 
			
		||||
	}{time.Now().UnixNano(), "godog"})
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic("failed to marshal run id")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	hasher := sha1.New()
 | 
			
		||||
	hasher.Write(data)
 | 
			
		||||
	return hex.EncodeToString(hasher.Sum(nil))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type events struct {
 | 
			
		||||
	sync.Once
 | 
			
		||||
	basefmt
 | 
			
		||||
 | 
			
		||||
	runID string
 | 
			
		||||
	suite string
 | 
			
		||||
 | 
			
		||||
	// currently running feature path, to be part of id.
 | 
			
		||||
	// this is sadly not passed by gherkin nodes.
 | 
			
		||||
	// it restricts this formatter to run only in synchronous single
 | 
			
		||||
	// threaded execution. Unless running a copy of formatter for each feature
 | 
			
		||||
	path string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *events) event(ev interface{}) {
 | 
			
		||||
	data, err := json.Marshal(ev)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(fmt.Sprintf("failed to marshal stream event: %+v - %v", ev, err))
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Println(string(data))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *events) started() {
 | 
			
		||||
	f.event(&struct {
 | 
			
		||||
		Event     string `json:"event"`
 | 
			
		||||
		RunID     string `json:"run_id"`
 | 
			
		||||
		Version   string `json:"version"`
 | 
			
		||||
		Timestamp int64  `json:"timestamp"`
 | 
			
		||||
	}{
 | 
			
		||||
		"TestRunStarted",
 | 
			
		||||
		f.runID,
 | 
			
		||||
		"0.1.0",
 | 
			
		||||
		time.Now().UnixNano() / nanoSec,
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *events) Node(n interface{}) {
 | 
			
		||||
	f.basefmt.Node(n)
 | 
			
		||||
	f.Do(f.started)
 | 
			
		||||
 | 
			
		||||
	switch t := n.(type) {
 | 
			
		||||
	case *gherkin.Scenario:
 | 
			
		||||
		f.event(&struct {
 | 
			
		||||
			Event     string `json:"event"`
 | 
			
		||||
			RunID     string `json:"run_id"`
 | 
			
		||||
			Suite     string `json:"suite"`
 | 
			
		||||
			Location  string `json:"location"`
 | 
			
		||||
			Timestamp int64  `json:"timestamp"`
 | 
			
		||||
		}{
 | 
			
		||||
			"TestCaseStarted",
 | 
			
		||||
			f.runID,
 | 
			
		||||
			"main",
 | 
			
		||||
			fmt.Sprintf("%s:%d", f.path, t.Location.Line),
 | 
			
		||||
			time.Now().UnixNano() / nanoSec,
 | 
			
		||||
		})
 | 
			
		||||
	case *gherkin.TableRow:
 | 
			
		||||
		f.event(&struct {
 | 
			
		||||
			Event     string `json:"event"`
 | 
			
		||||
			RunID     string `json:"run_id"`
 | 
			
		||||
			Suite     string `json:"suite"`
 | 
			
		||||
			Location  string `json:"location"`
 | 
			
		||||
			Timestamp int64  `json:"timestamp"`
 | 
			
		||||
		}{
 | 
			
		||||
			"TestCaseStarted",
 | 
			
		||||
			f.runID,
 | 
			
		||||
			"main",
 | 
			
		||||
			fmt.Sprintf("%s:%d", f.path, t.Location.Line),
 | 
			
		||||
			time.Now().UnixNano() / nanoSec,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *events) Feature(ft *gherkin.Feature, p string, c []byte) {
 | 
			
		||||
	f.basefmt.Feature(ft, p, c)
 | 
			
		||||
	f.path = p
 | 
			
		||||
	f.event(&struct {
 | 
			
		||||
		Event    string `json:"event"`
 | 
			
		||||
		RunID    string `json:"run_id"`
 | 
			
		||||
		Location string `json:"location"`
 | 
			
		||||
		Source   string `json:"source"`
 | 
			
		||||
	}{
 | 
			
		||||
		"TestSource",
 | 
			
		||||
		f.runID,
 | 
			
		||||
		fmt.Sprintf("%s:%d", p, ft.Location.Line),
 | 
			
		||||
		string(c),
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *events) Summary() {
 | 
			
		||||
	// determine status
 | 
			
		||||
	status := passed
 | 
			
		||||
	if len(f.failed) > 0 {
 | 
			
		||||
		status = failed
 | 
			
		||||
	} else if len(f.passed) == 0 {
 | 
			
		||||
		if len(f.undefined) > len(f.pending) {
 | 
			
		||||
			status = undefined
 | 
			
		||||
		} else {
 | 
			
		||||
			status = pending
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	f.event(&struct {
 | 
			
		||||
		Event     string `json:"event"`
 | 
			
		||||
		RunID     string `json:"run_id"`
 | 
			
		||||
		Status    string `json:"status"`
 | 
			
		||||
		Timestamp int64  `json:"timestamp"`
 | 
			
		||||
	}{
 | 
			
		||||
		"TestRunFinished",
 | 
			
		||||
		f.runID,
 | 
			
		||||
		status.String(),
 | 
			
		||||
		time.Now().UnixNano() / nanoSec,
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *events) step(res *stepResult) {
 | 
			
		||||
	var errMsg string
 | 
			
		||||
	if res.err != nil {
 | 
			
		||||
		errMsg = res.err.Error()
 | 
			
		||||
	}
 | 
			
		||||
	f.event(&struct {
 | 
			
		||||
		Event     string `json:"event"`
 | 
			
		||||
		RunID     string `json:"run_id"`
 | 
			
		||||
		Suite     string `json:"suite"`
 | 
			
		||||
		Location  string `json:"location"`
 | 
			
		||||
		Timestamp int64  `json:"timestamp"`
 | 
			
		||||
		Status    string `json:"status"`
 | 
			
		||||
		Summary   string `json:"summary,omitempty"`
 | 
			
		||||
	}{
 | 
			
		||||
		"TestStepFinished",
 | 
			
		||||
		f.runID,
 | 
			
		||||
		"main",
 | 
			
		||||
		fmt.Sprintf("%s:%d", f.path, res.step.Location.Line),
 | 
			
		||||
		time.Now().UnixNano() / nanoSec,
 | 
			
		||||
		res.typ.String(),
 | 
			
		||||
		errMsg,
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	switch t := f.owner.(type) {
 | 
			
		||||
	case *gherkin.ScenarioOutline:
 | 
			
		||||
	case *gherkin.Scenario:
 | 
			
		||||
		for i, s := range t.Steps {
 | 
			
		||||
			if s.Location.Line == res.step.Location.Line && i == len(t.Steps)-1 {
 | 
			
		||||
				// what if scenario is empty
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *events) Defined(step *gherkin.Step, def *StepDef) {
 | 
			
		||||
	if def != nil {
 | 
			
		||||
		m := def.Expr.FindStringSubmatchIndex(step.Text)[2:]
 | 
			
		||||
		args := make([][2]int, 0)
 | 
			
		||||
		for i := 0; i < len(m)/2; i++ {
 | 
			
		||||
			pair := m[i : i*2+2]
 | 
			
		||||
			var idxs [2]int
 | 
			
		||||
			idxs[0] = pair[0]
 | 
			
		||||
			idxs[1] = pair[1]
 | 
			
		||||
			args = append(args, idxs)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		f.event(&struct {
 | 
			
		||||
			Event    string   `json:"event"`
 | 
			
		||||
			RunID    string   `json:"run_id"`
 | 
			
		||||
			Suite    string   `json:"suite"`
 | 
			
		||||
			Location string   `json:"location"`
 | 
			
		||||
			DefID    string   `json:"definition_id"`
 | 
			
		||||
			Args     [][2]int `json:"arguments"`
 | 
			
		||||
		}{
 | 
			
		||||
			"StepDefinitionFound",
 | 
			
		||||
			f.runID,
 | 
			
		||||
			"main",
 | 
			
		||||
			fmt.Sprintf("%s:%d", f.path, step.Location.Line),
 | 
			
		||||
			def.definitionID(),
 | 
			
		||||
			args,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	f.event(&struct {
 | 
			
		||||
		Event     string `json:"event"`
 | 
			
		||||
		RunID     string `json:"run_id"`
 | 
			
		||||
		Suite     string `json:"suite"`
 | 
			
		||||
		Location  string `json:"location"`
 | 
			
		||||
		Timestamp int64  `json:"timestamp"`
 | 
			
		||||
	}{
 | 
			
		||||
		"TestStepStarted",
 | 
			
		||||
		f.runID,
 | 
			
		||||
		"main",
 | 
			
		||||
		fmt.Sprintf("%s:%d", f.path, step.Location.Line),
 | 
			
		||||
		time.Now().UnixNano() / nanoSec,
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *events) Passed(step *gherkin.Step, match *StepDef) {
 | 
			
		||||
	f.basefmt.Passed(step, match)
 | 
			
		||||
	f.step(f.passed[len(f.passed)-1])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *events) Skipped(step *gherkin.Step) {
 | 
			
		||||
	f.basefmt.Skipped(step)
 | 
			
		||||
	f.step(f.skipped[len(f.skipped)-1])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *events) Undefined(step *gherkin.Step) {
 | 
			
		||||
	f.basefmt.Undefined(step)
 | 
			
		||||
	f.step(f.undefined[len(f.undefined)-1])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *events) Failed(step *gherkin.Step, match *StepDef, err error) {
 | 
			
		||||
	f.basefmt.Failed(step, match, err)
 | 
			
		||||
	f.step(f.failed[len(f.failed)-1])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *events) Pending(step *gherkin.Step, match *StepDef) {
 | 
			
		||||
	f.basefmt.Pending(step, match)
 | 
			
		||||
	f.step(f.pending[len(f.pending)-1])
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -32,7 +32,7 @@ type junitFormatter struct {
 | 
			
		|||
	outlineExample int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (j *junitFormatter) Feature(feature *gherkin.Feature, path string) {
 | 
			
		||||
func (j *junitFormatter) Feature(feature *gherkin.Feature, path string, c []byte) {
 | 
			
		||||
	testSuite := &junitTestSuite{
 | 
			
		||||
		TestCases: make([]*junitTestCase, 0),
 | 
			
		||||
		Name:      feature.Name,
 | 
			
		||||
| 
						 | 
				
			
			@ -45,6 +45,10 @@ func (j *junitFormatter) Feature(feature *gherkin.Feature, path string) {
 | 
			
		|||
	j.suite.TestSuites = append(j.suite.TestSuites, testSuite)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *junitFormatter) Defined(*gherkin.Step, *StepDef) {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (j *junitFormatter) Node(node interface{}) {
 | 
			
		||||
	suite := j.current()
 | 
			
		||||
	tcase := &junitTestCase{}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -45,7 +45,7 @@ type pretty struct {
 | 
			
		|||
	outlineNumExamples int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *pretty) Feature(ft *gherkin.Feature, p string) {
 | 
			
		||||
func (f *pretty) Feature(ft *gherkin.Feature, p string, c []byte) {
 | 
			
		||||
	if len(f.features) != 0 {
 | 
			
		||||
		// not a first feature, add a newline
 | 
			
		||||
		fmt.Println("")
 | 
			
		||||
| 
						 | 
				
			
			@ -168,7 +168,7 @@ func (f *pretty) printOutlineExample(outline *gherkin.ScenarioOutline) {
 | 
			
		|||
				} else {
 | 
			
		||||
					text = cl(ostep.Text, cyan)
 | 
			
		||||
				}
 | 
			
		||||
				text += s(f.commentPos-f.length(ostep)+1) + cl(fmt.Sprintf("# %s", res.def.funcName()), black)
 | 
			
		||||
				text += s(f.commentPos-f.length(ostep)+1) + cl(fmt.Sprintf("# %s", res.def.definitionID()), black)
 | 
			
		||||
			} else {
 | 
			
		||||
				text = cl(ostep.Text, cyan)
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -207,7 +207,7 @@ func (f *pretty) printStep(step *gherkin.Step, def *StepDef, c color) {
 | 
			
		|||
	text := s(f.indent*2) + cl(strings.TrimSpace(step.Keyword), c) + " "
 | 
			
		||||
	switch {
 | 
			
		||||
	case def != nil:
 | 
			
		||||
		if m := (def.Expr.FindStringSubmatchIndex(step.Text))[2:]; len(m) > 0 {
 | 
			
		||||
		if m := def.Expr.FindStringSubmatchIndex(step.Text)[2:]; len(m) > 0 {
 | 
			
		||||
			var pos, i int
 | 
			
		||||
			for pos, i = 0, 0; i < len(m); i++ {
 | 
			
		||||
				if math.Mod(float64(i), 2) == 0 {
 | 
			
		||||
| 
						 | 
				
			
			@ -221,7 +221,7 @@ func (f *pretty) printStep(step *gherkin.Step, def *StepDef, c color) {
 | 
			
		|||
		} else {
 | 
			
		||||
			text += cl(step.Text, c)
 | 
			
		||||
		}
 | 
			
		||||
		text += s(f.commentPos-f.length(step)+1) + cl(fmt.Sprintf("# %s", def.funcName()), black)
 | 
			
		||||
		text += s(f.commentPos-f.length(step)+1) + cl(fmt.Sprintf("# %s", def.definitionID()), black)
 | 
			
		||||
	default:
 | 
			
		||||
		text += cl(step.Text, c)
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,10 +32,10 @@ func (f *progress) Node(n interface{}) {
 | 
			
		|||
	f.basefmt.Node(n)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *progress) Feature(ft *gherkin.Feature, p string) {
 | 
			
		||||
func (f *progress) Feature(ft *gherkin.Feature, p string, c []byte) {
 | 
			
		||||
	f.Lock()
 | 
			
		||||
	defer f.Unlock()
 | 
			
		||||
	f.basefmt.Feature(ft, p)
 | 
			
		||||
	f.basefmt.Feature(ft, p, c)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *progress) Summary() {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,7 +29,7 @@ type StepDef struct {
 | 
			
		|||
	Handler interface{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (sd *StepDef) funcName() string {
 | 
			
		||||
func (sd *StepDef) definitionID() string {
 | 
			
		||||
	ptr := sd.hv.Pointer()
 | 
			
		||||
	f := runtime.FuncForPC(ptr)
 | 
			
		||||
	file, line := f.FileLine(ptr)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										12
									
								
								suite.go
									
										
									
									
									
								
							
							
						
						
									
										12
									
								
								suite.go
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1,7 +1,9 @@
 | 
			
		|||
package godog
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"reflect"
 | 
			
		||||
| 
						 | 
				
			
			@ -19,6 +21,7 @@ var typeOfBytes = reflect.TypeOf([]byte(nil))
 | 
			
		|||
 | 
			
		||||
type feature struct {
 | 
			
		||||
	*gherkin.Feature
 | 
			
		||||
	Content []byte `json:"-"`
 | 
			
		||||
	Path    string `json:"path"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -193,11 +196,13 @@ func (s *Suite) matchStep(step *gherkin.Step) *StepDef {
 | 
			
		|||
			return h
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// @TODO can handle ambiguous
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Suite) runStep(step *gherkin.Step, prevStepErr error) (err error) {
 | 
			
		||||
	match := s.matchStep(step)
 | 
			
		||||
	s.fmt.Defined(step, match)
 | 
			
		||||
	if match == nil {
 | 
			
		||||
		s.fmt.Undefined(step)
 | 
			
		||||
		return ErrUndefined
 | 
			
		||||
| 
						 | 
				
			
			@ -325,7 +330,7 @@ func (s *Suite) runOutline(outline *gherkin.ScenarioOutline, b *gherkin.Backgrou
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (s *Suite) runFeature(f *feature) {
 | 
			
		||||
	s.fmt.Feature(f.Feature, f.Path)
 | 
			
		||||
	s.fmt.Feature(f.Feature, f.Path, f.Content)
 | 
			
		||||
	for _, scenario := range f.ScenarioDefinitions {
 | 
			
		||||
		var err error
 | 
			
		||||
		if f.Background != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -408,12 +413,13 @@ func parseFeatures(filter string, paths []string) (features []*feature, err erro
 | 
			
		|||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
				ft, err := gherkin.ParseFeature(reader)
 | 
			
		||||
				var buf bytes.Buffer
 | 
			
		||||
				ft, err := gherkin.ParseFeature(io.TeeReader(reader, &buf))
 | 
			
		||||
				reader.Close()
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
				features = append(features, &feature{Path: p, Feature: ft})
 | 
			
		||||
				features = append(features, &feature{Path: p, Feature: ft, Content: buf.Bytes()})
 | 
			
		||||
				// filter scenario by line number
 | 
			
		||||
				if line != -1 {
 | 
			
		||||
					var scenarios []interface{}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Загрузка…
	
	Создание таблицы
		
		Сослаться в новой задаче