Skip to content

Commit

Permalink
feat: Add support for step data tables
Browse files Browse the repository at this point in the history
  • Loading branch information
ckoerckel committed Sep 11, 2024
1 parent b8e6b8c commit 46fcac2
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 26 deletions.
12 changes: 12 additions & 0 deletions features/datatable.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Feature: dataTable feature
Scenario: compare text with dataTable
When I concat all the columns and row together using " - " to separate the columns
|r1c1|r1c2|r1c3|
|r2c1|r2c2|r2c3|
|r3c1|r3c2|r3c3|
Then the result should equal argument:
"""
r1c1 - r1c2 - r1c3
r2c1 - r2c2 - r2c3
r3c1 - r3c2 - r3c3
"""
108 changes: 82 additions & 26 deletions gobdd.go
Original file line number Diff line number Diff line change
Expand Up @@ -572,9 +572,17 @@ func (s *Suite) runStep(ctx Context, t *testing.T, step *msgs.Step) {
t.Fatalf("cannot find step definition for step: %s%s", step.Keyword, step.Text)
}

params := def.expr.FindSubmatch([]byte(step.Text))[1:]
matches := def.expr.FindSubmatch([]byte(step.Text))[1:]
var params []interface{}

Check failure on line 576 in gobdd.go

View workflow job for this annotation

GitHub Actions / Lint

Consider pre-allocating `params` (prealloc)
for _, m := range matches {

Check failure on line 577 in gobdd.go

View workflow job for this annotation

GitHub Actions / Lint

only one cuddle assignment allowed before range statement (wsl)
params = append(params, m)
}

if step.DocString != nil {
params = append(params, []byte(step.DocString.Content))
params = append(params, step.DocString.Content)
}
if step.DataTable != nil {

Check failure on line 584 in gobdd.go

View workflow job for this annotation

GitHub Actions / Lint

if statements should only be cuddled with assignments (wsl)
params = append(params, *step.DataTable)
}

t.Run(fmt.Sprintf("%s %s", strings.TrimSpace(step.Keyword), step.Text), func(t *testing.T) {
Expand All @@ -589,7 +597,7 @@ func (s *Suite) runStep(ctx Context, t *testing.T, step *msgs.Step) {
})
}

func (def *stepDef) run(ctx Context, t TestingT, params [][]byte) { // nolint:interfacer
func (def *stepDef) run(ctx Context, t TestingT, params []interface{}) { // nolint:interfacer
defer func() {
if r := recover(); r != nil {
t.Errorf("%+v", r)
Expand All @@ -611,38 +619,86 @@ func (def *stepDef) run(ctx Context, t TestingT, params [][]byte) { // nolint:in
}

inType := d.Type().In(i + 2)
paramType := paramType(v, inType)
paramType, err := paramType(v, inType)
if err != nil {

Check failure on line 623 in gobdd.go

View workflow job for this annotation

GitHub Actions / Lint

only one cuddle assignment allowed before if statement (wsl)
t.Fatal(err)
}
in = append(in, paramType)

Check failure on line 626 in gobdd.go

View workflow job for this annotation

GitHub Actions / Lint

append only allowed to cuddle with appended value (wsl)
}

d.Call(in)
}

func paramType(param []byte, inType reflect.Type) reflect.Value {
paramType := reflect.ValueOf(param)
if inType.Kind() == reflect.String {
paramType = reflect.ValueOf(string(paramType.Interface().([]uint8)))
}

if inType.Kind() == reflect.Int {
s := paramType.Interface().([]uint8)
p, _ := strconv.Atoi(string(s))
paramType = reflect.ValueOf(p)
}

if inType.Kind() == reflect.Float32 {
s := paramType.Interface().([]uint8)
p, _ := strconv.ParseFloat(string(s), 32)
paramType = reflect.ValueOf(float32(p))
func paramType(param interface{}, inType reflect.Type) (reflect.Value, error) {
switch inType.Kind() {
case reflect.String:
s, err := shouldBeString(param)
if err != nil {
return reflect.Value{}, err
}
return reflect.ValueOf(s), nil

Check failure on line 639 in gobdd.go

View workflow job for this annotation

GitHub Actions / Lint

return statements should not be cuddled if block has more than two lines (wsl)
case reflect.Int:
s, err := shouldBeString(param)
if err != nil {
return reflect.Value{}, err
}
p, err := strconv.Atoi(s)

Check failure on line 645 in gobdd.go

View workflow job for this annotation

GitHub Actions / Lint

assignments should only be cuddled with other assignments (wsl)
if err != nil {

Check failure on line 646 in gobdd.go

View workflow job for this annotation

GitHub Actions / Lint

only one cuddle assignment allowed before if statement (wsl)
return reflect.Value{}, err
}
return reflect.ValueOf(p), nil

Check failure on line 649 in gobdd.go

View workflow job for this annotation

GitHub Actions / Lint

return statements should not be cuddled if block has more than two lines (wsl)
case reflect.Float32:
s, err := shouldBeString(param)
if err != nil {
return reflect.Value{}, err
}
p, err := strconv.ParseFloat(s, 32)

Check failure on line 655 in gobdd.go

View workflow job for this annotation

GitHub Actions / Lint

assignments should only be cuddled with other assignments (wsl)
if err != nil {
return reflect.Value{}, err
}
return reflect.ValueOf(float32(p)), nil
case reflect.Float64:
s, err := shouldBeString(param)
if err != nil {
return reflect.Value{}, err
}
p, err := strconv.ParseFloat(s, 64)
if err != nil {
return reflect.Value{}, err
}
return reflect.ValueOf(p), nil
case reflect.Slice:
// only []byte is supported
if inType != reflect.TypeOf([]byte(nil)) {
return reflect.Value{}, fmt.Errorf("the slice argument type %s is not supported", inType.Kind())
}
if v, ok := param.([]byte); ok {
return reflect.ValueOf(v), nil
}
return reflect.Value{}, fmt.Errorf("cannot convert %v of type %T to []byte", param, param)
case reflect.Struct:
// the only struct supported is the one introduced by cucumber
if inType != reflect.TypeOf(msgs.DataTable{}) {
return reflect.Value{}, fmt.Errorf("the struct argument type %s is not supported", inType.Kind())
}
if v, ok := param.(msgs.DataTable); ok {
return reflect.ValueOf(v), nil
}
return reflect.Value{}, fmt.Errorf("cannot convert %v of type %T to messages.DataTable", param, param)
default:
return reflect.Value{}, fmt.Errorf("the type %s is not supported", inType.Kind())
}
}

if inType.Kind() == reflect.Float64 {
s := paramType.Interface().([]uint8)
p, _ := strconv.ParseFloat(string(s), 32)
paramType = reflect.ValueOf(p)
func shouldBeString(input interface{}) (string, error) {
switch v := input.(type) {
case string:
return v, nil
case []byte:
return string(v), nil
default:
return "", fmt.Errorf("cannot convert %v of type %T to string", input, input)
}

return paramType
}

func (s *Suite) findStepDef(text string) (stepDef, error) {
Expand Down
21 changes: 21 additions & 0 deletions gobdd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"errors"
"fmt"
"regexp"
"strings"
"testing"

msgs "github.com/cucumber/messages/go/v24"
Expand Down Expand Up @@ -84,6 +85,14 @@ func TestArguments(t *testing.T) {
suite.Run()
}

func TestDatatable(t *testing.T) {
suite := NewSuite(t, WithFeaturesPath("features/datatable.feature"))
suite.AddStep(`I concat all the columns and row together using {text} to separate the columns`, concatTable)
suite.AddStep(`the result should equal argument:`, checkt)

suite.Run()
}

func TestScenarioOutlineExecutesAllTests(t *testing.T) {
c := 0
suite := NewSuite(t, WithFeaturesPath("features/outline.feature"))
Expand Down Expand Up @@ -299,6 +308,18 @@ func concat(_ StepTest, ctx Context, var1, var2 string) {
ctx.Set("stringRes", var1+var2)
}

func concatTable(_ StepTest, ctx Context, separator string, table msgs.DataTable) {
rows := make([]string, 0, len(table.Rows))
for _, row := range table.Rows {
values := make([]string, 0, len(row.Cells))
for _, cell := range row.Cells {
values = append(values, cell.Value)
}
rows = append(rows, strings.Join(values, separator))
}
ctx.Set("stringRes", strings.Join(rows, "\n"))
}

func checkt(t StepTest, ctx Context, text string) {
received, err := ctx.GetString("stringRes")
if err != nil {
Expand Down

0 comments on commit 46fcac2

Please sign in to comment.