Skip to content
This repository has been archived by the owner on Sep 7, 2021. It is now read-only.

Commit

Permalink
Groundbreaking commit
Browse files Browse the repository at this point in the history
  • Loading branch information
nhatthm committed Jul 21, 2021
1 parent c41009f commit 8acee8e
Show file tree
Hide file tree
Showing 13 changed files with 997 additions and 27 deletions.
16 changes: 16 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
root = true

[*]
charset = utf-8
end_of_line = lf
indent_size = 4
insert_final_newline = true
max_line_length = 160
tab_width = 4
trim_trailing_whitespace = true

[Makefile]
indent_style = space

[*.feature]
indent_style = space
12 changes: 6 additions & 6 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ jobs:
file: ./unit.coverprofile
flags: unittests-${{ runner.os }}

# - name: Upload code coverage (features)
# if: matrix.go-version == env.GO_LATEST_VERSION
# uses: codecov/codecov-action@v1
# with:
# file: ./features.coverprofile
# flags: featurestests-${{ runner.os }}
- name: Upload code coverage (features)
if: matrix.go-version == env.GO_LATEST_VERSION
uses: codecov/codecov-action@v1
with:
file: ./features.coverprofile
flags: featurestests-${{ runner.os }}
8 changes: 4 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ $(VENDOR_DIR):
lint:
@$(GOLANGCI_LINT) run

test: test-unit
test: test-unit test-integration

## Run unit tests
test-unit:
@echo ">> unit test"
@$(GO) test -gcflags=-l -coverprofile=unit.coverprofile -covermode=atomic -race ./...

#test-integration:
# @echo ">> integration test"
# @$(GO) test ./features/... -gcflags=-l -coverprofile=features.coverprofile -coverpkg ./... -godog -race
test-integration:
@echo ">> integration test"
@$(GO) test ./features/... -gcflags=-l -coverprofile=features.coverprofile -coverpkg ./... -godog -race -v
157 changes: 143 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
# @nhatthm/{name}

<!--
[![GitHub Releases](https://img.shields.io/github/v/release/nhatthm/{name})](https://github.com/nhatthm/{name}/releases/latest)
[![Build Status](https://github.com/nhatthm/{name}/actions/workflows/test.yaml/badge.svg)](https://github.com/nhatthm/{name}/actions/workflows/test.yaml)
[![codecov](https://codecov.io/gh/nhatthm/{name}/branch/master/graph/badge.svg?token=eTdAgDE2vR)](https://codecov.io/gh/nhatthm/{name})
[![Go Report Card](https://goreportcard.com/badge/github.com/nhatthm/{name})](https://goreportcard.com/report/github.com/nhatthm/{name})
[![GoDevDoc](https://img.shields.io/badge/dev-doc-00ADD8?logo=go)](https://pkg.go.dev/github.com/nhatthm/{name})
# Variables Expander for Cucumber Steps

[![GitHub Releases](https://img.shields.io/github/v/release/nhatthm/expandog)](https://github.com/nhatthm/expandog/releases/latest)
[![Build Status](https://github.com/nhatthm/expandog/actions/workflows/test.yaml/badge.svg)](https://github.com/nhatthm/expandog/actions/workflows/test.yaml)
[![codecov](https://codecov.io/gh/nhatthm/expandog/branch/master/graph/badge.svg?token=eTdAgDE2vR)](https://codecov.io/gh/nhatthm/expandog)
[![Go Report Card](https://goreportcard.com/badge/github.com/nhatthm/expandog)](https://goreportcard.com/report/github.com/nhatthm/expandog)
[![GoDevDoc](https://img.shields.io/badge/dev-doc-00ADD8?logo=go)](https://pkg.go.dev/github.com/nhatthm/expandog)
[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/donate/?hosted_button_id=PJZSGJN57TDJY)
-->

TBD
A lifesaver expander for [`cucumber/godog`](https://github.com/cucumber/godog) because, sometimes, you have to use variables in your steps.

## Prerequisites

Expand All @@ -18,16 +16,147 @@ TBD
## Install

```bash
go get github.com/nhatthm/{name}
go get github.com/nhatthm/expandog
```

## Usage

TBD
Initiate a new `StepExpander` with `expandog.NewStepExpander()` then add it to `ScenarioInitializer` by
calling `StepExpander.RegisterContext(*testing.T, *godog.ScenarioContext)`

```go
package main

import (
"fmt"
"math/rand"
"strings"
"testing"

"github.com/cucumber/godog"
"github.com/nhatthm/expandog"
"github.com/stretchr/testify/assert"
)

func TestIntegration(t *testing.T) {
expander := expandog.NewStepExpander(
strings.NewReplacer("$TO", "Berlin"),
expandog.Pairs{
"HUSBAND": "John",
},
func() expandog.Pairs {
return expandog.Pairs{
"RAND": fmt.Sprintf("%d", rand.Int63()),
}
},
expandog.BeforeScenario(func() expandog.Pairs {
return expandog.Pairs{
"SCENARIO_RAND": fmt.Sprintf("%d", rand.Int63()),
}
}),
func(s string) string {
return strings.ReplaceAll(s, "$FROM", "Paris")
},
expandog.Expander(func(s string) string {
return strings.ReplaceAll(s, "$TRANSPORT", "by bus")
}),
// OS env vars.
expandog.EnvExpander,
)

suite := godog.TestSuite{
Name: "Integration",
ScenarioInitializer: func(ctx *godog.ScenarioContext) {
expander.RegisterContext(ctx)
},
Options: &godog.Options{
Strict: true,
Randomize: rand.Int63(),
},
}

// Run the suite.
}
```

In your tests, just use `$VARIABLE_NAME` in the step or the argument, like this:

```gherkin
Scenario: var is replaced
Given var NAME is replaced in step text: $NAME
Then step text is:
"""
map var NAME is replaced in step text: John
"""
Given var NAME is replaced in step argument (string)
"""
NAME=$NAME
"""
Then step argument is a string:
"""
NAME=John
"""
Given env var NAME is replaced in step argument (table)
| col 1 | col 2 | col 3 |
| value 1 | $NAME | value 3 |
Then step argument is a table:
| col 1 | col 2 | col 3 |
| value 1 | John | value 3 |
```

```gherkin
Scenario: .github files
Then there should be only these files in "$TEST_DIR/.github":
"""
- workflows:
- golangci-lint.yaml
- test.yaml
"""
```

### Expanders

The expanders could be any of these:

1. A `Replacer` interface

```go
type Replacer interface {
Replace(string) string
}
```

2. A `Replacer` `func(string) string` function. <br/><br/>
For example, you could use `os.ExpandEnv` or its alias `expandog.EnvExpander`

3. A map or vars (without the `$`) `map[string]string`

```go
var _ = expandog.NewStepExpander(map[string]string{
"HUSBAND": "John",
"WIFE": "Jane",
})
```

4. A provider that provides a map of vars (without the `$`) `map[string]string`. The provider will be called every step.

```go
var _ = expandog.NewStepExpander(func() map[string]string {
return map[string]string{
"RAND": fmt.Sprintf("%d", rand.Int63()),
}
})
```

## Examples
5. A `BeforeScenario` provides a map of vars (without the `$`) `map[string]string`. The provider will be called only once before every scenario.

TBA
**Note**: If you need `expandog.EnvExpander` or `os.ExpandEnv`, put it in the end of the chain. Because it replaces not-found vars with empty strings, other
expanders won't have a chance to do their jobs if you put it in the beginning.

## Donation

Expand Down
4 changes: 2 additions & 2 deletions doc.go
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
// Package main
package main
// Package expandog provides functionalities to expand variables in cucumber steps.
package expandog
89 changes: 89 additions & 0 deletions expander.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package expandog

import (
"errors"
"fmt"
"os"
"strings"
)

// ErrUnsupportedExpander indicates that the provided expander is not supported.
var ErrUnsupportedExpander = errors.New("unsupported expander")

// EnvExpander expands variables using env vars.
var EnvExpander Expander = os.ExpandEnv

// Pairs is a pair of old and new to be replaced.
type Pairs = map[string]string

// Expander expands the variables in a string.
type Expander func(string) string

// Replacer replace string.
type Replacer interface {
Replace(string) string
}

// BeforeScenario expands variables from a provider that provide only before running a scenario.
func BeforeScenario(provide func() Pairs) Expander {
return mapExpander(provide())
}

func placeholder(name string) string {
return fmt.Sprintf("$%s", name)
}

// runtimeExpander expands variables from a provider.
func runtimeExpander(provide func() Pairs) Expander {
return func(s string) string {
return mapExpander(provide())(s)
}
}

// mapExpander initiates a new variable expander from a map of values.
func mapExpander(pairs Pairs) Expander {
oldNew := make([]string, 0, 2*len(pairs))

for k, v := range pairs {
oldNew = append(oldNew, placeholder(k), v)
}

return strings.NewReplacer(oldNew...).Replace
}

func chainExpanders(expanders ...interface{}) Expander {
l := make([]Expander, 0, len(expanders))

for _, e := range expanders {
l = append(l, newExpander(e))
}

return func(s string) string {
for _, expand := range l {
s = expand(s)
}

return s
}
}

func newExpander(e interface{}) Expander {
switch e := e.(type) {
case Pairs:
return mapExpander(e)

case func() map[string]string:
return runtimeExpander(e)

case Replacer:
return e.Replace

case Expander:
return e

case func(string) string:
return e
}

panic(fmt.Errorf("%w: got %T", ErrUnsupportedExpander, e))
}
Loading

0 comments on commit 8acee8e

Please sign in to comment.