reorganize flag handling and handle tag filters
Этот коммит содержится в:
родитель
972f8025d8
коммит
54126fcfc8
6 изменённых файлов: 204 добавлений и 201 удалений
|
@ -119,6 +119,11 @@ See **.travis.yml** for supported **go** versions.
|
||||||
|
|
||||||
The public API is stable enough, but it may break until **1.0.0** version, see `godog --version`.
|
The public API is stable enough, but it may break until **1.0.0** version, see `godog --version`.
|
||||||
|
|
||||||
|
### FAQ
|
||||||
|
|
||||||
|
**Q:** Where can I configure common options globally?
|
||||||
|
**A:** You can't. Alias your common or project based commands: `alias mygodog="godog --format=progress"`
|
||||||
|
|
||||||
### Contributions
|
### Contributions
|
||||||
|
|
||||||
Feel free to open a pull request. Note, if you wish to contribute an extension to public (exported methods or types) -
|
Feel free to open a pull request. Note, if you wish to contribute an extension to public (exported methods or types) -
|
||||||
|
|
178
config.go
178
config.go
|
@ -1,178 +0,0 @@
|
||||||
package godog
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/DATA-DOG/godog/gherkin"
|
|
||||||
)
|
|
||||||
|
|
||||||
type registeredFormatter struct {
|
|
||||||
name string
|
|
||||||
fmt Formatter
|
|
||||||
description string
|
|
||||||
}
|
|
||||||
|
|
||||||
var formatters []*registeredFormatter
|
|
||||||
|
|
||||||
// RegisterFormatter registers a feature suite output
|
|
||||||
// Formatter as the name and descriptiongiven.
|
|
||||||
// Formatter is used to represent suite output
|
|
||||||
func RegisterFormatter(name, description string, f Formatter) {
|
|
||||||
formatters = append(formatters, ®isteredFormatter{
|
|
||||||
name: name,
|
|
||||||
fmt: f,
|
|
||||||
description: description,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
var cfg *config
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
cfg = &config{}
|
|
||||||
|
|
||||||
flag.StringVar(&cfg.format, "format", "pretty", "")
|
|
||||||
flag.StringVar(&cfg.format, "f", "pretty", "")
|
|
||||||
flag.BoolVar(&cfg.definitions, "definitions", false, "")
|
|
||||||
flag.BoolVar(&cfg.definitions, "d", false, "")
|
|
||||||
flag.BoolVar(&cfg.stopOnFailure, "stop-on-failure", false, "")
|
|
||||||
flag.BoolVar(&cfg.version, "version", false, "")
|
|
||||||
|
|
||||||
flag.Usage = func() {
|
|
||||||
// prints an option or argument with a description, or only description
|
|
||||||
opt := func(name, desc string) string {
|
|
||||||
if len(name) > 0 {
|
|
||||||
name += ":"
|
|
||||||
}
|
|
||||||
return s(2) + cl(name, green) + s(30-len(name)) + desc
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- GENERAL ---
|
|
||||||
fmt.Println(cl("Usage:", yellow))
|
|
||||||
fmt.Println(s(2) + "godog [options] [<paths>]\n")
|
|
||||||
|
|
||||||
// --- ARGUMENTS ---
|
|
||||||
fmt.Println(cl("Arguments:", yellow))
|
|
||||||
// --> paths
|
|
||||||
fmt.Println(opt("paths", "Optional path(s) to execute. Can be:"))
|
|
||||||
fmt.Println(opt("", s(4)+"- dir "+cl("(features/)", yellow)))
|
|
||||||
fmt.Println(opt("", s(4)+"- feature "+cl("(*.feature)", yellow)))
|
|
||||||
fmt.Println(opt("", s(4)+"- scenario at specific line "+cl("(*.feature:10)", yellow)))
|
|
||||||
fmt.Println(opt("", "If no paths are listed, suite tries "+cl("features", yellow)+" path by default."))
|
|
||||||
fmt.Println("")
|
|
||||||
|
|
||||||
// --- OPTIONS ---
|
|
||||||
fmt.Println(cl("Options:", yellow))
|
|
||||||
// --> step definitions
|
|
||||||
fmt.Println(opt("-d, --definitions", "Print all available step definitions."))
|
|
||||||
// --> format
|
|
||||||
fmt.Println(opt("-f, --format=pretty", "How to format tests output. Available formats:"))
|
|
||||||
for _, f := range formatters {
|
|
||||||
fmt.Println(opt("", s(4)+"- "+cl(f.name, yellow)+": "+f.description))
|
|
||||||
}
|
|
||||||
// --> stop on failure
|
|
||||||
fmt.Println(opt("--stop-on-failure", "Stop processing on first failed scenario."))
|
|
||||||
// --> version
|
|
||||||
fmt.Println(opt("--version", "Show current "+cl("godog", yellow)+" version."))
|
|
||||||
fmt.Println("")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type config struct {
|
|
||||||
paths []string
|
|
||||||
format string
|
|
||||||
|
|
||||||
definitions bool
|
|
||||||
stopOnFailure bool
|
|
||||||
version bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *config) validate() error {
|
|
||||||
c.paths = flag.Args()
|
|
||||||
// check the default path
|
|
||||||
if len(c.paths) == 0 {
|
|
||||||
inf, err := os.Stat("features")
|
|
||||||
if err == nil && inf.IsDir() {
|
|
||||||
c.paths = []string{"features"}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// formatter
|
|
||||||
var found bool
|
|
||||||
var names []string
|
|
||||||
for _, f := range formatters {
|
|
||||||
if f.name == c.format {
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
names = append(names, f.name)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !found {
|
|
||||||
return fmt.Errorf(`unregistered formatter name: "%s", use one of: %s`, c.format, strings.Join(names, ", "))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *config) features() (lst []*gherkin.Feature, err error) {
|
|
||||||
for _, pat := range c.paths {
|
|
||||||
// check if line number is specified
|
|
||||||
parts := strings.Split(pat, ":")
|
|
||||||
path := parts[0]
|
|
||||||
line := -1
|
|
||||||
if len(parts) > 1 {
|
|
||||||
line, err = strconv.Atoi(parts[1])
|
|
||||||
if err != nil {
|
|
||||||
return lst, fmt.Errorf("line number should follow after colon path delimiter")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// parse features
|
|
||||||
err = filepath.Walk(path, func(p string, f os.FileInfo, err error) error {
|
|
||||||
if err == nil && !f.IsDir() && strings.HasSuffix(p, ".feature") {
|
|
||||||
ft, err := gherkin.ParseFile(p)
|
|
||||||
switch {
|
|
||||||
case err == gherkin.ErrEmpty:
|
|
||||||
// its ok, just skip it
|
|
||||||
case err != nil:
|
|
||||||
return err
|
|
||||||
default:
|
|
||||||
lst = append(lst, ft)
|
|
||||||
}
|
|
||||||
// filter scenario by line number
|
|
||||||
if line != -1 {
|
|
||||||
var scenarios []*gherkin.Scenario
|
|
||||||
for _, s := range ft.Scenarios {
|
|
||||||
if s.Token.Line == line {
|
|
||||||
scenarios = append(scenarios, s)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ft.Scenarios = scenarios
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
// check error
|
|
||||||
switch {
|
|
||||||
case os.IsNotExist(err):
|
|
||||||
return lst, fmt.Errorf(`feature path "%s" is not available`, path)
|
|
||||||
case os.IsPermission(err):
|
|
||||||
return lst, fmt.Errorf(`feature path "%s" is not accessible`, path)
|
|
||||||
case err != nil:
|
|
||||||
return lst, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *config) formatter() (f Formatter) {
|
|
||||||
for _, fmt := range formatters {
|
|
||||||
if fmt.name == cfg.format {
|
|
||||||
return fmt.fmt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
panic("formatter name had to be validated")
|
|
||||||
}
|
|
64
flags.go
Обычный файл
64
flags.go
Обычный файл
|
@ -0,0 +1,64 @@
|
||||||
|
package godog
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func flags(s *suite) *flag.FlagSet {
|
||||||
|
set := flag.NewFlagSet("godog", flag.ExitOnError)
|
||||||
|
set.StringVar(&s.format, "format", "pretty", "")
|
||||||
|
set.StringVar(&s.format, "f", "pretty", "")
|
||||||
|
set.StringVar(&s.tags, "tags", "", "")
|
||||||
|
set.StringVar(&s.tags, "t", "", "")
|
||||||
|
set.BoolVar(&s.definitions, "definitions", false, "")
|
||||||
|
set.BoolVar(&s.definitions, "d", false, "")
|
||||||
|
set.BoolVar(&s.stopOnFailure, "stop-on-failure", false, "")
|
||||||
|
set.BoolVar(&s.version, "version", false, "")
|
||||||
|
set.Usage = usage
|
||||||
|
return set
|
||||||
|
}
|
||||||
|
|
||||||
|
func usage() {
|
||||||
|
// prints an option or argument with a description, or only description
|
||||||
|
opt := func(name, desc string) string {
|
||||||
|
if len(name) > 0 {
|
||||||
|
name += ":"
|
||||||
|
}
|
||||||
|
return s(2) + cl(name, green) + s(30-len(name)) + desc
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- GENERAL ---
|
||||||
|
fmt.Println(cl("Usage:", yellow))
|
||||||
|
fmt.Println(s(2) + "godog [options] [<paths>]\n")
|
||||||
|
|
||||||
|
// --- ARGUMENTS ---
|
||||||
|
fmt.Println(cl("Arguments:", yellow))
|
||||||
|
// --> paths
|
||||||
|
fmt.Println(opt("paths", "Optional path(s) to execute. Can be:"))
|
||||||
|
fmt.Println(opt("", s(4)+"- dir "+cl("(features/)", yellow)))
|
||||||
|
fmt.Println(opt("", s(4)+"- feature "+cl("(*.feature)", yellow)))
|
||||||
|
fmt.Println(opt("", s(4)+"- scenario at specific line "+cl("(*.feature:10)", yellow)))
|
||||||
|
fmt.Println(opt("", "If no paths are listed, suite tries "+cl("features", yellow)+" path by default."))
|
||||||
|
fmt.Println("")
|
||||||
|
|
||||||
|
// --- OPTIONS ---
|
||||||
|
fmt.Println(cl("Options:", yellow))
|
||||||
|
// --> step definitions
|
||||||
|
fmt.Println(opt("-d, --definitions", "Print all available step definitions."))
|
||||||
|
// --> format
|
||||||
|
fmt.Println(opt("-f, --format=pretty", "How to format tests output. Available formats:"))
|
||||||
|
for _, f := range formatters {
|
||||||
|
fmt.Println(opt("", s(4)+"- "+cl(f.name, yellow)+": "+f.description))
|
||||||
|
}
|
||||||
|
// --> tags
|
||||||
|
fmt.Println(opt("-t, --tags", "Filter scenarios by tags. Expression can be:"))
|
||||||
|
fmt.Println(opt("", s(4)+"- "+cl(`"wip"`, yellow)+": run all scenarios with wip tag"))
|
||||||
|
fmt.Println(opt("", s(4)+"- "+cl(`"!wip"`, yellow)+": exclude all scenarios with wip tag"))
|
||||||
|
fmt.Println(opt("", s(4)+"- "+cl(`"wip !new"`, yellow)+": run wip scenarios, but exclude new"))
|
||||||
|
// --> stop on failure
|
||||||
|
fmt.Println(opt("--stop-on-failure", "Stop processing on first failed scenario."))
|
||||||
|
// --> version
|
||||||
|
fmt.Println(opt("--version", "Show current "+cl("godog", yellow)+" version."))
|
||||||
|
fmt.Println("")
|
||||||
|
}
|
19
fmt.go
19
fmt.go
|
@ -6,6 +6,25 @@ import (
|
||||||
"github.com/DATA-DOG/godog/gherkin"
|
"github.com/DATA-DOG/godog/gherkin"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type registeredFormatter struct {
|
||||||
|
name string
|
||||||
|
fmt Formatter
|
||||||
|
description string
|
||||||
|
}
|
||||||
|
|
||||||
|
var formatters []*registeredFormatter
|
||||||
|
|
||||||
|
// RegisterFormatter registers a feature suite output
|
||||||
|
// Formatter as the name and descriptiongiven.
|
||||||
|
// Formatter is used to represent suite output
|
||||||
|
func RegisterFormatter(name, description string, f Formatter) {
|
||||||
|
formatters = append(formatters, ®isteredFormatter{
|
||||||
|
name: name,
|
||||||
|
fmt: f,
|
||||||
|
description: description,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Formatter is an interface for feature runner
|
// Formatter is an interface for feature runner
|
||||||
// output summary presentation.
|
// output summary presentation.
|
||||||
//
|
//
|
||||||
|
|
128
suite.go
128
suite.go
|
@ -1,12 +1,13 @@
|
||||||
package godog
|
package godog
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/DATA-DOG/godog/gherkin"
|
"github.com/DATA-DOG/godog/gherkin"
|
||||||
|
@ -76,6 +77,14 @@ type suite struct {
|
||||||
|
|
||||||
failed bool
|
failed bool
|
||||||
|
|
||||||
|
// options
|
||||||
|
paths []string
|
||||||
|
format string
|
||||||
|
tags string
|
||||||
|
definitions bool
|
||||||
|
stopOnFailure bool
|
||||||
|
version bool
|
||||||
|
|
||||||
// suite event handlers
|
// suite event handlers
|
||||||
beforeSuiteHandlers []func()
|
beforeSuiteHandlers []func()
|
||||||
beforeScenarioHandlers []func(*gherkin.Scenario)
|
beforeScenarioHandlers []func(*gherkin.Scenario)
|
||||||
|
@ -175,27 +184,43 @@ func (s *suite) AfterSuite(f func()) {
|
||||||
|
|
||||||
// Run starts the Godog feature suite
|
// Run starts the Godog feature suite
|
||||||
func (s *suite) Run() {
|
func (s *suite) Run() {
|
||||||
var err error
|
flagSet := flags(s)
|
||||||
if !flag.Parsed() {
|
fatal(flagSet.Parse(os.Args[1:]))
|
||||||
flag.Parse()
|
|
||||||
|
s.paths = flagSet.Args()
|
||||||
|
// check the default path
|
||||||
|
if len(s.paths) == 0 {
|
||||||
|
inf, err := os.Stat("features")
|
||||||
|
if err == nil && inf.IsDir() {
|
||||||
|
s.paths = []string{"features"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// validate formatter
|
||||||
|
var names []string
|
||||||
|
for _, f := range formatters {
|
||||||
|
if f.name == s.format {
|
||||||
|
s.fmt = f.fmt
|
||||||
|
break
|
||||||
|
}
|
||||||
|
names = append(names, f.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.fmt == nil {
|
||||||
|
fatal(fmt.Errorf(`unregistered formatter name: "%s", use one of: %s`, s.format, strings.Join(names, ", ")))
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if we need to just show something first
|
// check if we need to just show something first
|
||||||
switch {
|
switch {
|
||||||
case cfg.version:
|
case s.version:
|
||||||
fmt.Println(cl("Godog", green) + " version is " + cl(Version, yellow))
|
fmt.Println(cl("Godog", green) + " version is " + cl(Version, yellow))
|
||||||
return
|
return
|
||||||
case cfg.definitions:
|
case s.definitions:
|
||||||
s.printStepDefinitions()
|
s.printStepDefinitions()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fatal(s.parseFeatures())
|
||||||
// run a feature suite
|
// run a feature suite
|
||||||
fatal(cfg.validate())
|
|
||||||
s.fmt = cfg.formatter()
|
|
||||||
s.features, err = cfg.features()
|
|
||||||
fatal(err)
|
|
||||||
|
|
||||||
s.run()
|
s.run()
|
||||||
|
|
||||||
if s.failed {
|
if s.failed {
|
||||||
|
@ -211,7 +236,7 @@ func (s *suite) run() {
|
||||||
// run features
|
// run features
|
||||||
for _, f := range s.features {
|
for _, f := range s.features {
|
||||||
s.runFeature(f)
|
s.runFeature(f)
|
||||||
if s.failed && cfg.stopOnFailure {
|
if s.failed && s.stopOnFailure {
|
||||||
// stop on first failure
|
// stop on first failure
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -320,7 +345,7 @@ func (s *suite) runOutline(scenario *gherkin.Scenario) (err error) {
|
||||||
scenario.Steps = steps
|
scenario.Steps = steps
|
||||||
if err = s.runScenario(scenario); err != nil && err != ErrUndefined {
|
if err = s.runScenario(scenario); err != nil && err != ErrUndefined {
|
||||||
s.failed = true
|
s.failed = true
|
||||||
if cfg.stopOnFailure {
|
if s.stopOnFailure {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -340,7 +365,7 @@ func (s *suite) runFeature(f *gherkin.Feature) {
|
||||||
}
|
}
|
||||||
if err != nil && err != ErrUndefined {
|
if err != nil && err != ErrUndefined {
|
||||||
s.failed = true
|
s.failed = true
|
||||||
if cfg.stopOnFailure {
|
if s.stopOnFailure {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -394,3 +419,78 @@ func (s *suite) printStepDefinitions() {
|
||||||
fmt.Println("there were no contexts registered, could not find any step definition..")
|
fmt.Println("there were no contexts registered, could not find any step definition..")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *suite) parseFeatures() (err error) {
|
||||||
|
for _, pat := range s.paths {
|
||||||
|
// check if line number is specified
|
||||||
|
parts := strings.Split(pat, ":")
|
||||||
|
path := parts[0]
|
||||||
|
line := -1
|
||||||
|
if len(parts) > 1 {
|
||||||
|
line, err = strconv.Atoi(parts[1])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("line number should follow after colon path delimiter")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// parse features
|
||||||
|
err = filepath.Walk(path, func(p string, f os.FileInfo, err error) error {
|
||||||
|
if err == nil && !f.IsDir() && strings.HasSuffix(p, ".feature") {
|
||||||
|
ft, err := gherkin.ParseFile(p)
|
||||||
|
switch {
|
||||||
|
case err == gherkin.ErrEmpty:
|
||||||
|
// its ok, just skip it
|
||||||
|
case err != nil:
|
||||||
|
return err
|
||||||
|
default:
|
||||||
|
s.features = append(s.features, ft)
|
||||||
|
}
|
||||||
|
// filter scenario by line number
|
||||||
|
if line != -1 {
|
||||||
|
var scenarios []*gherkin.Scenario
|
||||||
|
for _, s := range ft.Scenarios {
|
||||||
|
if s.Token.Line == line {
|
||||||
|
scenarios = append(scenarios, s)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ft.Scenarios = scenarios
|
||||||
|
}
|
||||||
|
s.applyTagFilter(ft)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
// check error
|
||||||
|
switch {
|
||||||
|
case os.IsNotExist(err):
|
||||||
|
return fmt.Errorf(`feature path "%s" is not available`, path)
|
||||||
|
case os.IsPermission(err):
|
||||||
|
return fmt.Errorf(`feature path "%s" is not accessible`, path)
|
||||||
|
case err != nil:
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *suite) applyTagFilter(ft *gherkin.Feature) {
|
||||||
|
if len(s.tags) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tag := range strings.Split(s.tags, " ") {
|
||||||
|
var scenarios []*gherkin.Scenario
|
||||||
|
var inverse bool
|
||||||
|
if tag[0] == '!' {
|
||||||
|
tag = tag[1:]
|
||||||
|
inverse = true
|
||||||
|
}
|
||||||
|
for _, scenario := range ft.Scenarios {
|
||||||
|
if inverse && !scenario.Tags.Has(gherkin.Tag(tag)) {
|
||||||
|
scenarios = append(scenarios, scenario)
|
||||||
|
} else if !inverse && scenario.Tags.Has(gherkin.Tag(tag)) {
|
||||||
|
scenarios = append(scenarios, scenario)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ft.Scenarios = scenarios
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -47,8 +47,6 @@ func (s *suiteContext) HandleBeforeScenario(*gherkin.Scenario) {
|
||||||
s.testedSuite = &suite{fmt: s.fmt}
|
s.testedSuite = &suite{fmt: s.fmt}
|
||||||
// our tested suite will have the same context registered
|
// our tested suite will have the same context registered
|
||||||
SuiteContext(s.testedSuite)
|
SuiteContext(s.testedSuite)
|
||||||
// reset feature paths
|
|
||||||
cfg.paths = []string{}
|
|
||||||
// reset all fired events
|
// reset all fired events
|
||||||
s.events = []*firedEvent{}
|
s.events = []*firedEvent{}
|
||||||
}
|
}
|
||||||
|
@ -147,17 +145,12 @@ func (s *suiteContext) aFeatureFile(args ...*Arg) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *suiteContext) featurePath(args ...*Arg) error {
|
func (s *suiteContext) featurePath(args ...*Arg) error {
|
||||||
cfg.paths = append(cfg.paths, args[0].String())
|
s.testedSuite.paths = append(s.testedSuite.paths, args[0].String())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *suiteContext) parseFeatures(args ...*Arg) error {
|
func (s *suiteContext) parseFeatures(args ...*Arg) error {
|
||||||
features, err := cfg.features()
|
return s.testedSuite.parseFeatures()
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
s.testedSuite.features = append(s.testedSuite.features, features...)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *suiteContext) theSuiteShouldHave(args ...*Arg) error {
|
func (s *suiteContext) theSuiteShouldHave(args ...*Arg) error {
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче