Skip to content

Commit 201e526

Browse files
authored
added support for Attachments (aka Embedddings) (#623)
* added support for attachments in the cucumber json and events output formats - done by sneaking the attachment into the context.Context object.
1 parent 4ade331 commit 201e526

File tree

11 files changed

+218
-21
lines changed

11 files changed

+218
-21
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,6 @@ Gopkg.toml
88
.idea
99
.vscode
1010

11-
_artifacts
11+
_artifacts
12+
13+
vendor

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ This document is formatted according to the principles of [Keep A CHANGELOG](htt
1111
## [v0.14.1]
1212

1313
### Added
14+
- Provide support for attachments / embeddings - ([623](https://github.com/cucumber/godog/pull/623) - [johnlon](https://github.com/johnlon))
1415
- 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))
1516
- Created releasing guidelines - ([608](https://github.com/cucumber/godog/pull/608) - [glibas](https://github.com/glibas))
1617

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -580,3 +580,6 @@ A simple example can be [found here](/_examples/custom-formatter).
580580
[contributing guide]: https://github.com/cucumber/godog/blob/main/CONTRIBUTING.md
581581
[releasing guide]: https://github.com/cucumber/godog/blob/main/RELEASING.md
582582
[community Slack]: https://cucumber.io/community#slack
583+
584+
585+

internal/formatters/fmt_cucumber.go

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ package formatters
1212
*/
1313

1414
import (
15+
"encoding/base64"
1516
"encoding/json"
1617
"fmt"
1718
"io"
@@ -139,14 +140,21 @@ type cukeMatch struct {
139140
Location string `json:"location"`
140141
}
141142

143+
type cukeEmbedding struct {
144+
Name string `json:"name"`
145+
MimeType string `json:"mime_type"`
146+
Data string `json:"data"`
147+
}
148+
142149
type cukeStep struct {
143-
Keyword string `json:"keyword"`
144-
Name string `json:"name"`
145-
Line int `json:"line"`
146-
Docstring *cukeDocstring `json:"doc_string,omitempty"`
147-
Match cukeMatch `json:"match"`
148-
Result cukeResult `json:"result"`
149-
DataTable []*cukeDataTableRow `json:"rows,omitempty"`
150+
Keyword string `json:"keyword"`
151+
Name string `json:"name"`
152+
Line int `json:"line"`
153+
Docstring *cukeDocstring `json:"doc_string,omitempty"`
154+
Match cukeMatch `json:"match"`
155+
Result cukeResult `json:"result"`
156+
DataTable []*cukeDataTableRow `json:"rows,omitempty"`
157+
Embeddings []*cukeEmbedding `json:"embeddings,omitempty"`
150158
}
151159

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

305+
if stepResult.Attachments != nil {
306+
attachments := []*cukeEmbedding{}
307+
308+
for _, a := range stepResult.Attachments {
309+
attachments = append(attachments, &cukeEmbedding{
310+
Name: a.Name,
311+
Data: base64.RawStdEncoding.EncodeToString(a.Data),
312+
MimeType: a.MimeType,
313+
})
314+
}
315+
316+
if len(attachments) > 0 {
317+
cukeStep.Embeddings = attachments
318+
}
319+
}
297320
return cukeStep
298321
}
299322

internal/formatters/fmt_events.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,31 @@ func (f *Events) step(pickle *messages.Pickle, pickleStep *messages.PickleStep)
153153
if pickleStepResult.Err != nil {
154154
errMsg = pickleStepResult.Err.Error()
155155
}
156+
157+
if pickleStepResult.Attachments != nil {
158+
for _, attachment := range pickleStepResult.Attachments {
159+
160+
f.event(&struct {
161+
Event string `json:"event"`
162+
Location string `json:"location"`
163+
Timestamp int64 `json:"timestamp"`
164+
ContentEncoding string `json:"contentEncoding"`
165+
FileName string `json:"fileName"`
166+
MimeType string `json:"mimeType"`
167+
Body string `json:"body"`
168+
}{
169+
"Attachment",
170+
fmt.Sprintf("%s:%d", pickle.Uri, step.Location.Line),
171+
utils.TimeNowFunc().UnixNano() / nanoSec,
172+
messages.AttachmentContentEncoding_BASE64.String(),
173+
attachment.Name,
174+
attachment.MimeType,
175+
string(attachment.Data),
176+
})
177+
178+
}
179+
}
180+
156181
f.event(&struct {
157182
Event string `json:"event"`
158183
Location string `json:"location"`

internal/formatters/fmt_output_test.go

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@ package formatters_test
22

33
import (
44
"bytes"
5+
"context"
56
"fmt"
6-
"io/ioutil"
77
"os"
88
"path"
99
"path/filepath"
10+
"regexp"
1011
"strings"
1112
"testing"
1213

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

2526
featureFiles, err := listFmtOutputTestsFeatureFiles()
2627
require.Nil(t, err)
27-
2828
formatters := []string{"cucumber", "events", "junit", "pretty", "progress", "junit,pretty"}
29-
3029
for _, fmtName := range formatters {
3130
for _, featureFile := range featureFiles {
3231
testName := fmt.Sprintf("%s/%s", fmtName, featureFile)
@@ -65,6 +64,7 @@ func fmtOutputTest(fmtName, testName, featureFilePath string) func(*testing.T) {
6564
ctx.Step(`^(?:a )?pending step$`, pendingStepDef)
6665
ctx.Step(`^(?:a )?passing step$`, passingStepDef)
6766
ctx.Step(`^odd (\d+) and even (\d+) number$`, oddEvenStepDef)
67+
ctx.Step(`^(?:a )?a step with attachment$`, stepWithAttachment)
6868
}
6969

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

77-
expectedOutput, err := ioutil.ReadFile(expectOutputPath)
77+
expectedOutput, err := os.ReadFile(expectOutputPath)
7878
require.NoError(t, err)
7979

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

95-
expected := string(expectedOutput)
96-
actual := buf.String()
95+
// normalise on unix line ending so expected vs actual works cross platform
96+
expected := normalise(string(expectedOutput))
97+
actual := normalise(buf.String())
9798
assert.Equalf(t, expected, actual, "path: %s", expectOutputPath)
9899
}
99100
}
100101

102+
func normalise(s string) string {
103+
104+
m := regexp.MustCompile("fmt_output_test.go:[0-9]+")
105+
normalised := m.ReplaceAllString(s, "fmt_output_test.go:XXX")
106+
normalised = strings.Replace(normalised, "\r\n", "\n", -1)
107+
normalised = strings.Replace(normalised, "\\r\\n", "\\n", -1)
108+
109+
return normalised
110+
}
111+
101112
func passingStepDef() error { return nil }
102113

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

117128
func failingStepDef() error { return fmt.Errorf("step failed") }
129+
130+
func stepWithAttachment(ctx context.Context) (context.Context, error) {
131+
ctxOut := godog.Attach(ctx,
132+
godog.Attachment{Body: []byte("TheData1"), FileName: "TheFilename1", MediaType: "text/plain"},
133+
godog.Attachment{Body: []byte("TheData2"), FileName: "TheFilename2", MediaType: "text/plain"},
134+
)
135+
136+
return ctxOut, nil
137+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
[
2+
{
3+
"uri": "formatter-tests/features/scenario_with_attachment.feature",
4+
"id": "scenario-with-attachment",
5+
"keyword": "Feature",
6+
"name": "scenario with attachment",
7+
"description": " describes\n an attachment\n feature",
8+
"line": 1,
9+
"elements": [
10+
{
11+
"id": "scenario-with-attachment;step-with-attachment",
12+
"keyword": "Scenario",
13+
"name": "step with attachment",
14+
"description": "",
15+
"line": 6,
16+
"type": "scenario",
17+
"steps": [
18+
{
19+
"keyword": "Given ",
20+
"name": "a step with attachment",
21+
"line": 7,
22+
"match": {
23+
"location": "fmt_output_test.go:119"
24+
},
25+
"result": {
26+
"status": "passed",
27+
"duration": 0
28+
},
29+
"embeddings": [
30+
{
31+
"name": "TheFilename1",
32+
"mime_type": "text/plain",
33+
"data": "VGhlRGF0YTE"
34+
},
35+
{
36+
"name": "TheFilename2",
37+
"mime_type": "text/plain",
38+
"data": "VGhlRGF0YTI"
39+
}
40+
]
41+
}
42+
]
43+
}
44+
]
45+
}
46+
]
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{"event":"TestRunStarted","version":"0.1.0","timestamp":-6795364578871,"suite":"events"}
2+
{"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"}
3+
{"event":"TestCaseStarted","location":"formatter-tests/features/scenario_with_attachment.feature:6","timestamp":-6795364578871}
4+
{"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":[]}
5+
{"event":"TestStepStarted","location":"formatter-tests/features/scenario_with_attachment.feature:7","timestamp":-6795364578871}
6+
{"event":"Attachment","location":"formatter-tests/features/scenario_with_attachment.feature:7","timestamp":-6795364578871,"contentEncoding":"BASE64","fileName":"TheFilename1","mimeType":"text/plain","body":"TheData1"}
7+
{"event":"Attachment","location":"formatter-tests/features/scenario_with_attachment.feature:7","timestamp":-6795364578871,"contentEncoding":"BASE64","fileName":"TheFilename2","mimeType":"text/plain","body":"TheData2"}
8+
{"event":"TestStepFinished","location":"formatter-tests/features/scenario_with_attachment.feature:7","timestamp":-6795364578871,"status":"passed"}
9+
{"event":"TestCaseFinished","location":"formatter-tests/features/scenario_with_attachment.feature:6","timestamp":-6795364578871,"status":"passed"}
10+
{"event":"TestRunFinished","status":"passed","timestamp":-6795364578871,"snippets":"","memory":""}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Feature: scenario with attachment
2+
describes
3+
an attachment
4+
feature
5+
6+
Scenario: step with attachment
7+
Given a step with attachment

internal/models/results.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@ type PickleResult struct {
1818
StartedAt time.Time
1919
}
2020

21+
// PickleAttachment ...
22+
type PickleAttachment struct {
23+
Name string
24+
MimeType string
25+
Data []byte
26+
}
27+
2128
// PickleStepResult ...
2229
type PickleStepResult struct {
2330
Status StepResultStatus
@@ -28,13 +35,16 @@ type PickleStepResult struct {
2835
PickleStepID string
2936

3037
Def *StepDefinition
38+
39+
Attachments []*PickleAttachment
3140
}
3241

3342
// NewStepResult ...
3443
func NewStepResult(
3544
status StepResultStatus,
3645
pickleID, pickleStepID string,
3746
match *StepDefinition,
47+
attachments []*PickleAttachment,
3848
err error,
3949
) PickleStepResult {
4050
return PickleStepResult{
@@ -44,6 +54,7 @@ func NewStepResult(
4454
PickleID: pickleID,
4555
PickleStepID: pickleStepID,
4656
Def: match,
57+
Attachments: attachments,
4758
}
4859
}
4960

0 commit comments

Comments
 (0)