Skip to content

Commit

Permalink
support attachments with filenames (#2297)
Browse files Browse the repository at this point in the history
  • Loading branch information
davidjgoss authored Jul 12, 2023
1 parent 0518bb3 commit aea9e8b
Show file tree
Hide file tree
Showing 11 changed files with 336 additions and 91 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
Please see [CONTRIBUTING.md](./CONTRIBUTING.md) on how to contribute to Cucumber.

## [Unreleased]
### Added
- Support attachments with filenames ([#2297](https://github.com/cucumber/cucumber-js/pull/2297))

## [9.2.0] - 2023-06-22
### Added
Expand Down
23 changes: 23 additions & 0 deletions compatibility/features/attachments/attachments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,26 @@ When('the {word} png is attached', async function (filename) {
'image/png'
)
})

When(
'a PDF document is attached with a filename',
async function (this: World) {
await this.attach(
fs.createReadStream(
path.join(
process.cwd(),
'node_modules',
'@cucumber',
'compatibility-kit',
'features',
'attachments',
'document.pdf'
)
),
{
mediaType: 'application/pdf',
fileName: 'document.pdf',
}
)
}
)
73 changes: 38 additions & 35 deletions docs/support_files/attachments.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,39 +14,52 @@ After(function () {
```

By default, text is saved with a MIME type of `text/plain`. You can also specify
a different MIME type:
a different MIME type as part of a second argument:

```javascript
var {After} = require('@cucumber/cucumber');

After(function () {
this.attach('{"name": "some JSON"}', 'application/json');
this.attach('{"name": "some JSON"}', { mediaType: 'application/json' });
});
```

If you'd like, you can also specify a filename to be used if the attachment is made available to download as a file via a formatter:

```javascript
var {After} = require('@cucumber/cucumber');

After(function () {
this.attach('{"name": "some JSON"}', {
mediaType: 'application/json',
fileName: 'results.json'
});
});
```

Images and other binary data can be attached using a [stream.Readable](https://nodejs.org/api/stream.html).
The data will be `base64` encoded in the output.
You should wait for the stream to be read before continuing by providing a callback or waiting for the returned promise to resolve.
You should wait for the stream to be read before continuing by awaiting the returned promise or providing a callback.

```javascript
var {After, Status} = require('@cucumber/cucumber');

// Passing a callback
After(function (testCase, callback) {
// Awaiting the promise
After(async function (testCase) {
if (testCase.result.status === Status.FAILED) {
var stream = getScreenshotOfError();
this.attach(stream, 'image/png', callback);
}
else {
callback();
await this.attach(stream, { mediaType: 'image/png' });
}
});

// Returning the promise
After(function (testCase) {
// Passing a callback
After(function (testCase, callback) {
if (testCase.result.status === Status.FAILED) {
var stream = getScreenshotOfError();
return this.attach(stream, 'image/png');
this.attach(stream, { mediaType: 'image/png' }, callback);
}
else {
callback();
}
});
```
Expand All @@ -60,38 +73,23 @@ var {After, Status} = require('@cucumber/cucumber');
After(function (testCase) {
if (testCase.result.status === Status.FAILED) {
var buffer = getScreenshotOfError();
this.attach(buffer, 'image/png');
}
});
```

If you've already got a base64-encoded string, you can prefix your mime type with `base64:` to indicate this:

```javascript
var {After, Status} = require('@cucumber/cucumber');

After(function (testCase) {
if (testCase.result.status === Status.FAILED) {
var base64String = getScreenshotOfError();
this.attach(base64String, 'base64:image/png');
this.attach(buffer, { mediaType: 'image/png' });
}
});
```

Here is an example of saving a screenshot using [Selenium WebDriver](https://www.npmjs.com/package/selenium-webdriver)
If you've already got a base64-encoded string, you can prefix your mime type with `base64:` to indicate this. Here's an example of saving a screenshot using [Selenium WebDriver](https://www.npmjs.com/package/selenium-webdriver)
when a scenario fails:

```javascript
var {After, Status} = require('@cucumber/cucumber');

After(function (testCase) {
var world = this;
After(async function (testCase) {
if (testCase.result.status === Status.FAILED) {
return webDriver.takeScreenshot().then(function(screenShot) {
world.attach(screenShot, 'base64:image/png');
});
const screenshot = await driver.takeScreenshot()
this.attach(screenshot, { mediaType: 'base64:image/png' })
}
});
})
```

Attachments are also printed by the progress, progress-bar and summary formatters.
Expand All @@ -100,15 +98,20 @@ It can be used to debug scenarios, especially in parallel mode.

```javascript
// Step definition
Given(/^a basic step$/, function() {
Given('a basic step', async function() {
this.attach('Some info.')
this.attach('{"some", "JSON"}}', 'application/json')
this.attach('{"some": "JSON"}}', { mediaType: 'application/json' })
this.attach((await driver.takeScreenshot()), {
mediaType: 'base64:image/png',
fileName: 'screenshot.png'
})
})

// Result format
// ✔ Given a basic step # path:line
// Attachment (text/plain): Some info.
// Attachment (application/json)
// Attachment (image/png): screenshot.png
```

## Logging
Expand Down
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@
"yup": "^0.32.11"
},
"devDependencies": {
"@cucumber/compatibility-kit": "11.3.0",
"@cucumber/compatibility-kit": "^12.0.0",
"@cucumber/query": "12.0.1",
"@microsoft/api-documenter": "7.19.27",
"@microsoft/api-extractor": "7.33.7",
Expand Down
6 changes: 3 additions & 3 deletions src/formatter/helpers/issue_helpers_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -260,8 +260,8 @@ describe('IssueHelpers', () => {
${figures.tick} Given attachment step1 # steps.ts:35
Attachment (text/plain): Some info
Attachment (application/json)
Attachment (image/png)
${figures.cross} When attachment step2 # steps.ts:41
Attachment (image/png): screenshot.png
${figures.cross} When attachment step2 # steps.ts:44
Attachment (text/plain): Other info
error
- Then a passing step # steps.ts:29
Expand Down Expand Up @@ -290,7 +290,7 @@ describe('IssueHelpers', () => {
reindent(`
1) Scenario: my scenario # a.feature:2
${figures.tick} Given attachment step1 # steps.ts:35
${figures.cross} When attachment step2 # steps.ts:41
${figures.cross} When attachment step2 # steps.ts:44
error
- Then a passing step # steps.ts:29
Expand Down
9 changes: 7 additions & 2 deletions src/formatter/helpers/test_case_attempt_formatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,13 @@ function formatStep({
text += indentString(`${colorFn(argumentsText)}\n`, 4)
}
if (valueOrDefault(printAttachments, true)) {
attachments.forEach(({ body, mediaType }) => {
const message = mediaType === 'text/plain' ? `: ${body}` : ''
attachments.forEach(({ body, mediaType, fileName }) => {
let message = ''
if (mediaType === 'text/plain') {
message = `: ${body}`
} else if (fileName) {
message = `: ${fileName}`
}
text += indentString(`Attachment (${mediaType})${message}\n`, 4)
})
}
Expand Down
Loading

0 comments on commit aea9e8b

Please sign in to comment.