
Issue: It is not possible to use function without return when matching steps, resulting in a lot of Nil only error returns. Fix: Allows to provide empty result function by correctly matching reflect Calls on step Handler. When nothing is returned by the Handler, it will return nil as if errors was nil.
190 строки
5,1 КиБ
Go
190 строки
5,1 КиБ
Go
package models
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"strconv"
|
|
|
|
"github.com/cucumber/messages-go/v16"
|
|
|
|
"github.com/cucumber/godog/formatters"
|
|
)
|
|
|
|
var typeOfBytes = reflect.TypeOf([]byte(nil))
|
|
|
|
// StepDefinition ...
|
|
type StepDefinition struct {
|
|
formatters.StepDefinition
|
|
|
|
Args []interface{}
|
|
HandlerValue reflect.Value
|
|
|
|
// multistep related
|
|
Nested bool
|
|
Undefined []string
|
|
}
|
|
|
|
// 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++ {
|
|
param := typ.In(i)
|
|
switch param.Kind() {
|
|
case reflect.Int:
|
|
s, err := sd.shouldBeString(i)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
v, err := strconv.ParseInt(s, 10, 0)
|
|
if err != nil {
|
|
return fmt.Errorf(`cannot convert argument %d: "%s" to int: %s`, i, s, err)
|
|
}
|
|
values = append(values, reflect.ValueOf(int(v)))
|
|
case reflect.Int64:
|
|
s, err := sd.shouldBeString(i)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
v, err := strconv.ParseInt(s, 10, 64)
|
|
if err != nil {
|
|
return fmt.Errorf(`cannot convert argument %d: "%s" to int64: %s`, i, s, err)
|
|
}
|
|
values = append(values, reflect.ValueOf(int64(v)))
|
|
case reflect.Int32:
|
|
s, err := sd.shouldBeString(i)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
v, err := strconv.ParseInt(s, 10, 32)
|
|
if err != nil {
|
|
return fmt.Errorf(`cannot convert argument %d: "%s" to int32: %s`, i, s, err)
|
|
}
|
|
values = append(values, reflect.ValueOf(int32(v)))
|
|
case reflect.Int16:
|
|
s, err := sd.shouldBeString(i)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
v, err := strconv.ParseInt(s, 10, 16)
|
|
if err != nil {
|
|
return fmt.Errorf(`cannot convert argument %d: "%s" to int16: %s`, i, s, err)
|
|
}
|
|
values = append(values, reflect.ValueOf(int16(v)))
|
|
case reflect.Int8:
|
|
s, err := sd.shouldBeString(i)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
v, err := strconv.ParseInt(s, 10, 8)
|
|
if err != nil {
|
|
return fmt.Errorf(`cannot convert argument %d: "%s" to int8: %s`, i, s, err)
|
|
}
|
|
values = append(values, reflect.ValueOf(int8(v)))
|
|
case reflect.String:
|
|
s, err := sd.shouldBeString(i)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
values = append(values, reflect.ValueOf(s))
|
|
case reflect.Float64:
|
|
s, err := sd.shouldBeString(i)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
v, err := strconv.ParseFloat(s, 64)
|
|
if err != nil {
|
|
return fmt.Errorf(`cannot convert argument %d: "%s" to float64: %s`, i, s, err)
|
|
}
|
|
values = append(values, reflect.ValueOf(v))
|
|
case reflect.Float32:
|
|
s, err := sd.shouldBeString(i)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
v, err := strconv.ParseFloat(s, 32)
|
|
if err != nil {
|
|
return fmt.Errorf(`cannot convert argument %d: "%s" to float32: %s`, i, s, err)
|
|
}
|
|
values = append(values, reflect.ValueOf(float32(v)))
|
|
case reflect.Ptr:
|
|
arg := sd.Args[i]
|
|
switch param.Elem().String() {
|
|
case "messages.PickleDocString":
|
|
if v, ok := arg.(*messages.PickleStepArgument); ok {
|
|
values = append(values, reflect.ValueOf(v.DocString))
|
|
break
|
|
}
|
|
|
|
if v, ok := arg.(*messages.PickleDocString); ok {
|
|
values = append(values, reflect.ValueOf(v))
|
|
break
|
|
}
|
|
|
|
return fmt.Errorf(`cannot convert argument %d: "%v" of type "%T" to *messages.PickleStepArgument_PickleDocString`, i, arg, arg)
|
|
case "messages.PickleTable":
|
|
if v, ok := arg.(*messages.PickleStepArgument); ok {
|
|
values = append(values, reflect.ValueOf(v.DataTable))
|
|
break
|
|
}
|
|
|
|
if v, ok := arg.(*messages.PickleTable); ok {
|
|
values = append(values, reflect.ValueOf(v))
|
|
break
|
|
}
|
|
|
|
return fmt.Errorf(`cannot convert argument %d: "%v" of type "%T" to *messages.PickleStepArgument_PickleTable`, i, arg, arg)
|
|
default:
|
|
return fmt.Errorf("the argument %d type %T is not supported %s", i, arg, param.Elem().String())
|
|
}
|
|
case reflect.Slice:
|
|
switch param {
|
|
case typeOfBytes:
|
|
s, err := sd.shouldBeString(i)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
values = append(values, reflect.ValueOf([]byte(s)))
|
|
default:
|
|
return fmt.Errorf("the slice argument %d type %s is not supported", i, param.Kind())
|
|
}
|
|
default:
|
|
return fmt.Errorf("the argument %d type %s is not supported", i, param.Kind())
|
|
}
|
|
}
|
|
|
|
res := sd.HandlerValue.Call(values)
|
|
if len(res) == 0 {
|
|
return nil
|
|
}
|
|
return res[0].Interface()
|
|
}
|
|
|
|
func (sd *StepDefinition) shouldBeString(idx int) (string, error) {
|
|
arg := sd.Args[idx]
|
|
switch arg := arg.(type) {
|
|
case string:
|
|
return arg, nil
|
|
case *messages.PickleStepArgument:
|
|
if arg.DocString == nil {
|
|
return "", fmt.Errorf(`cannot convert DocString is not set`)
|
|
}
|
|
return arg.DocString.Content, nil
|
|
case *messages.PickleDocString:
|
|
return arg.Content, nil
|
|
default:
|
|
return "", fmt.Errorf(`cannot convert argument %d: "%v" of type "%T" to string`, idx, arg, arg)
|
|
}
|
|
}
|
|
|
|
// GetInternalStepDefinition ...
|
|
func (sd *StepDefinition) GetInternalStepDefinition() *formatters.StepDefinition {
|
|
if sd == nil {
|
|
return nil
|
|
}
|
|
|
|
return &sd.StepDefinition
|
|
}
|