Skip to content

Commit

Permalink
added support for Attachments (aka Embedddings) (#623)
Browse files Browse the repository at this point in the history
* added support for attachments in the cucumber json and events output formats - done by sneaking the attachment into the context.Context object.
  • Loading branch information
Johnlon authored May 28, 2024
1 parent 4ade331 commit 201e526
Show file tree
Hide file tree
Showing 11 changed files with 218 additions and 21 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@ Gopkg.toml
.idea
.vscode

_artifacts
_artifacts

vendor
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ This document is formatted according to the principles of [Keep A CHANGELOG](htt
## [v0.14.1]

### Added
- Provide support for attachments / embeddings - ([623](https://github.com/cucumber/godog/pull/623) - [johnlon](https://github.com/johnlon))
- Provide testing.T-compatible interface on test context, allowing usage of assertion libraries such as testify's assert/require - ([571](https://github.com/cucumber/godog/pull/571) - [mrsheepuk](https://github.com/mrsheepuk))
- Created releasing guidelines - ([608](https://github.com/cucumber/godog/pull/608) - [glibas](https://github.com/glibas))

Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -580,3 +580,6 @@ A simple example can be [found here](/_examples/custom-formatter).
[contributing guide]: https://github.com/cucumber/godog/blob/main/CONTRIBUTING.md
[releasing guide]: https://github.com/cucumber/godog/blob/main/RELEASING.md
[community Slack]: https://cucumber.io/community#slack



37 changes: 30 additions & 7 deletions internal/formatters/fmt_cucumber.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ package formatters
*/

import (
"encoding/base64"
"encoding/json"
"fmt"
"io"
Expand Down Expand Up @@ -139,14 +140,21 @@ type cukeMatch struct {
Location string `json:"location"`
}

type cukeEmbedding struct {
Name string `json:"name"`
MimeType string `json:"mime_type"`
Data string `json:"data"`
}

type cukeStep struct {
Keyword string `json:"keyword"`
Name string `json:"name"`
Line int `json:"line"`
Docstring *cukeDocstring `json:"doc_string,omitempty"`
Match cukeMatch `json:"match"`
Result cukeResult `json:"result"`
DataTable []*cukeDataTableRow `json:"rows,omitempty"`
Keyword string `json:"keyword"`
Name string `json:"name"`
Line int `json:"line"`
Docstring *cukeDocstring `json:"doc_string,omitempty"`
Match cukeMatch `json:"match"`
Result cukeResult `json:"result"`
DataTable []*cukeDataTableRow `json:"rows,omitempty"`
Embeddings []*cukeEmbedding `json:"embeddings,omitempty"`
}

type cukeDataTableRow struct {
Expand Down Expand Up @@ -294,6 +302,21 @@ func (f *Cuke) buildCukeStep(pickle *messages.Pickle, stepResult models.PickleSt
cukeStep.Match.Location = fmt.Sprintf("%s:%d", pickle.Uri, step.Location.Line)
}

if stepResult.Attachments != nil {
attachments := []*cukeEmbedding{}

for _, a := range stepResult.Attachments {
attachments = append(attachments, &cukeEmbedding{
Name: a.Name,
Data: base64.RawStdEncoding.EncodeToString(a.Data),
MimeType: a.MimeType,
})
}

if len(attachments) > 0 {
cukeStep.Embeddings = attachments
}
}
return cukeStep
}

Expand Down
25 changes: 25 additions & 0 deletions internal/formatters/fmt_events.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,31 @@ func (f *Events) step(pickle *messages.Pickle, pickleStep *messages.PickleStep)
if pickleStepResult.Err != nil {
errMsg = pickleStepResult.Err.Error()
}

if pickleStepResult.Attachments != nil {
for _, attachment := range pickleStepResult.Attachments {

f.event(&struct {
Event string `json:"event"`
Location string `json:"location"`
Timestamp int64 `json:"timestamp"`
ContentEncoding string `json:"contentEncoding"`
FileName string `json:"fileName"`
MimeType string `json:"mimeType"`
Body string `json:"body"`
}{
"Attachment",
fmt.Sprintf("%s:%d", pickle.Uri, step.Location.Line),
utils.TimeNowFunc().UnixNano() / nanoSec,
messages.AttachmentContentEncoding_BASE64.String(),
attachment.Name,
attachment.MimeType,
string(attachment.Data),
})

}
}

f.event(&struct {
Event string `json:"event"`
Location string `json:"location"`
Expand Down
32 changes: 26 additions & 6 deletions internal/formatters/fmt_output_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ package formatters_test

import (
"bytes"
"context"
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"regexp"
"strings"
"testing"

Expand All @@ -24,9 +25,7 @@ func Test_FmtOutput(t *testing.T) {

featureFiles, err := listFmtOutputTestsFeatureFiles()
require.Nil(t, err)

formatters := []string{"cucumber", "events", "junit", "pretty", "progress", "junit,pretty"}

for _, fmtName := range formatters {
for _, featureFile := range featureFiles {
testName := fmt.Sprintf("%s/%s", fmtName, featureFile)
Expand Down Expand Up @@ -65,6 +64,7 @@ func fmtOutputTest(fmtName, testName, featureFilePath string) func(*testing.T) {
ctx.Step(`^(?:a )?pending step$`, pendingStepDef)
ctx.Step(`^(?:a )?passing step$`, passingStepDef)
ctx.Step(`^odd (\d+) and even (\d+) number$`, oddEvenStepDef)
ctx.Step(`^(?:a )?a step with attachment$`, stepWithAttachment)
}

return func(t *testing.T) {
Expand All @@ -74,7 +74,7 @@ func fmtOutputTest(fmtName, testName, featureFilePath string) func(*testing.T) {
t.Skipf("Couldn't find expected output file %q", expectOutputPath)
}

expectedOutput, err := ioutil.ReadFile(expectOutputPath)
expectedOutput, err := os.ReadFile(expectOutputPath)
require.NoError(t, err)

var buf bytes.Buffer
Expand All @@ -92,12 +92,23 @@ func fmtOutputTest(fmtName, testName, featureFilePath string) func(*testing.T) {
Options: &opts,
}.Run()

expected := string(expectedOutput)
actual := buf.String()
// normalise on unix line ending so expected vs actual works cross platform
expected := normalise(string(expectedOutput))
actual := normalise(buf.String())
assert.Equalf(t, expected, actual, "path: %s", expectOutputPath)
}
}

func normalise(s string) string {

m := regexp.MustCompile("fmt_output_test.go:[0-9]+")
normalised := m.ReplaceAllString(s, "fmt_output_test.go:XXX")
normalised = strings.Replace(normalised, "\r\n", "\n", -1)
normalised = strings.Replace(normalised, "\\r\\n", "\\n", -1)

return normalised
}

func passingStepDef() error { return nil }

func oddEvenStepDef(odd, even int) error { return oddOrEven(odd, even) }
Expand All @@ -115,3 +126,12 @@ func oddOrEven(odd, even int) error {
func pendingStepDef() error { return godog.ErrPending }

func failingStepDef() error { return fmt.Errorf("step failed") }

func stepWithAttachment(ctx context.Context) (context.Context, error) {
ctxOut := godog.Attach(ctx,
godog.Attachment{Body: []byte("TheData1"), FileName: "TheFilename1", MediaType: "text/plain"},
godog.Attachment{Body: []byte("TheData2"), FileName: "TheFilename2", MediaType: "text/plain"},
)

return ctxOut, nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
[
{
"uri": "formatter-tests/features/scenario_with_attachment.feature",
"id": "scenario-with-attachment",
"keyword": "Feature",
"name": "scenario with attachment",
"description": " describes\n an attachment\n feature",
"line": 1,
"elements": [
{
"id": "scenario-with-attachment;step-with-attachment",
"keyword": "Scenario",
"name": "step with attachment",
"description": "",
"line": 6,
"type": "scenario",
"steps": [
{
"keyword": "Given ",
"name": "a step with attachment",
"line": 7,
"match": {
"location": "fmt_output_test.go:119"
},
"result": {
"status": "passed",
"duration": 0
},
"embeddings": [
{
"name": "TheFilename1",
"mime_type": "text/plain",
"data": "VGhlRGF0YTE"
},
{
"name": "TheFilename2",
"mime_type": "text/plain",
"data": "VGhlRGF0YTI"
}
]
}
]
}
]
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{"event":"TestRunStarted","version":"0.1.0","timestamp":-6795364578871,"suite":"events"}
{"event":"TestSource","location":"formatter-tests/features/scenario_with_attachment.feature:1","source":"Feature: scenario with attachment\n describes\n an attachment\n feature\n\n Scenario: step with attachment\n Given a step with attachment\n"}
{"event":"TestCaseStarted","location":"formatter-tests/features/scenario_with_attachment.feature:6","timestamp":-6795364578871}
{"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_with_attachment.feature:7","definition_id":"fmt_output_test.go:XXX -\u003e github.com/cucumber/godog/internal/formatters_test.stepWithAttachment","arguments":[]}
{"event":"TestStepStarted","location":"formatter-tests/features/scenario_with_attachment.feature:7","timestamp":-6795364578871}
{"event":"Attachment","location":"formatter-tests/features/scenario_with_attachment.feature:7","timestamp":-6795364578871,"contentEncoding":"BASE64","fileName":"TheFilename1","mimeType":"text/plain","body":"TheData1"}
{"event":"Attachment","location":"formatter-tests/features/scenario_with_attachment.feature:7","timestamp":-6795364578871,"contentEncoding":"BASE64","fileName":"TheFilename2","mimeType":"text/plain","body":"TheData2"}
{"event":"TestStepFinished","location":"formatter-tests/features/scenario_with_attachment.feature:7","timestamp":-6795364578871,"status":"passed"}
{"event":"TestCaseFinished","location":"formatter-tests/features/scenario_with_attachment.feature:6","timestamp":-6795364578871,"status":"passed"}
{"event":"TestRunFinished","status":"passed","timestamp":-6795364578871,"snippets":"","memory":""}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Feature: scenario with attachment
describes
an attachment
feature

Scenario: step with attachment
Given a step with attachment
11 changes: 11 additions & 0 deletions internal/models/results.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ type PickleResult struct {
StartedAt time.Time
}

// PickleAttachment ...
type PickleAttachment struct {
Name string
MimeType string
Data []byte
}

// PickleStepResult ...
type PickleStepResult struct {
Status StepResultStatus
Expand All @@ -28,13 +35,16 @@ type PickleStepResult struct {
PickleStepID string

Def *StepDefinition

Attachments []*PickleAttachment
}

// NewStepResult ...
func NewStepResult(
status StepResultStatus,
pickleID, pickleStepID string,
match *StepDefinition,
attachments []*PickleAttachment,
err error,
) PickleStepResult {
return PickleStepResult{
Expand All @@ -44,6 +54,7 @@ func NewStepResult(
PickleID: pickleID,
PickleStepID: pickleStepID,
Def: match,
Attachments: attachments,
}
}

Expand Down
Loading

0 comments on commit 201e526

Please sign in to comment.