Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support externalising attachments in HTML formatter #2413

Merged
merged 27 commits into from
Aug 12, 2024
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
a7e7ab6
declare options type for formatter
davidjgoss Jun 21, 2024
bcf16a2
make directory available in formatter
davidjgoss Jun 21, 2024
30988ab
upgrade html-formatter
davidjgoss Jun 21, 2024
6eb8838
enable logging in formatter plugin
davidjgoss Jun 21, 2024
bb46134
ignore all files in reports dir
davidjgoss Jun 21, 2024
0e2c22f
use optionsKey to drill down for options
davidjgoss Jun 21, 2024
c5daee4
add scenario
davidjgoss Jun 22, 2024
ff40cee
fix options drilling
davidjgoss Jun 22, 2024
14af080
add mime dependency
davidjgoss Jun 22, 2024
6a2e7bd
implement externalisation
davidjgoss Jun 22, 2024
186762c
more aggressive handling of misconfiguration
davidjgoss Jun 22, 2024
cace7f1
add documentation
davidjgoss Jun 24, 2024
5fdd358
add to interface
davidjgoss Jun 24, 2024
2db209b
fix audit issues
davidjgoss Jul 4, 2024
6c57c29
update html-formatter
davidjgoss Aug 2, 2024
0aaabb4
implement new link function on world
davidjgoss Aug 2, 2024
9579dfc
update api-extractor output
davidjgoss Aug 2, 2024
7b1c71e
exception for uri-list in html externalise
davidjgoss Aug 2, 2024
180fa8b
documentation
davidjgoss Aug 3, 2024
9a76c78
update CHANGELOG.md
davidjgoss Aug 3, 2024
22f3d06
just dont say just
davidjgoss Aug 8, 2024
1cf1232
update html-formatter and use named export
davidjgoss Aug 8, 2024
e51fdb3
Merge remote-tracking branch 'origin/feat/html-externalise' into feat…
davidjgoss Aug 8, 2024
a6d7f6c
fix docs
davidjgoss Aug 8, 2024
ffbdc5b
add docs links to changelog
davidjgoss Aug 8, 2024
9c10817
tweak changelog wording
davidjgoss Aug 8, 2024
9cfdb2b
Merge branch 'main' into feat/html-externalise
davidjgoss Aug 9, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@ coverage/
lib/
node_modules
tmp/
reports/*.html
reports/*.ndjson
reports/*.txt
reports/*.xml
reports/
yarn-error.log
.vscode
.DS_Store
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ 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 externalising attachments in HTML formatter ([#2413](https://github.com/cucumber/cucumber-js/pull/2413))
- Support easily linking to external content via attachments ([#2413](https://github.com/cucumber/cucumber-js/pull/2413))

## [10.8.0] - 2024-05-26
### Added
Expand Down
16 changes: 16 additions & 0 deletions docs/formatters.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,22 @@ You can:
- Filter to specific statuses
- Search by keywords or tag expressions

#### Attachments

By default, the HTML report includes all attachments from your test run as embedded data. This is simple and convenient, with the file being completely standalone and portable. But it can make for a _very_ large file if you have a lot of large attachments like screenshots, videos and other media. You can optionally have attachments saved to external files instead, if that works better for you:

```json
{
"formatOptions": {
"html": {
"externalAttachments": true
}
}
}
```

This will cause attachments to be saved in the same directory as the report file, with filenames that look like `attachment-8e7c5d3d-1ef0-4be6-86e0-16362bad9531.png`. If you want to put the report file somewhere - say, a web server - to be viewed, you'll need to bring those files along with it.

### `message`

Outputs all the [Cucumber Messages](https://github.com/cucumber/messages) for the test run as newline-delimited JSON, which can then be consumed by other tools.
Expand Down
22 changes: 22 additions & 0 deletions docs/support_files/attachments.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,25 @@ After(function () {
```

Anything you log will be attached as a string with a MIME type of `text/x.cucumber.log+plain`

## Links

You can easily attach one or more links from your support code with the `link` function:
davidjgoss marked this conversation as resolved.
Show resolved Hide resolved

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

Before(function () {
this.link('https://cucumber.io');
});

After(function () {
this.link(
'https://github.com/cucumber/cucumber-js',
'https://github.com/cucumber/cucumber-js',
'https://github.com/cucumber/cucumber-js'
);
});
```

Links will be attached as a string with a MIME type of `text/uri-list`
1 change: 1 addition & 0 deletions docs/support_files/world.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ By default, the world is an instance of Cucumber's built-in `World` class. Cucum

* `this.attach`: a method for adding [attachments](./attachments.md) to hooks/steps
* `this.log`: a method for [logging](./attachments.md#logging) information from hooks/steps
* `this.link`: a method for [linking](./attachments.md#links) to URLs from hooks/steps
* `this.parameters`: an object of parameters passed in via configuration (see below)

Your custom world will also receive these arguments, but it's up to you to decide what to do with them and they can be safely ignored.
Expand Down
8 changes: 7 additions & 1 deletion exports/root/report.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,8 @@ export interface IWorld<ParametersType = any> {
// (undocumented)
readonly attach: ICreateAttachment;
// (undocumented)
readonly link: ICreateLink;
// (undocumented)
readonly log: ICreateLog;
// (undocumented)
readonly parameters: ParametersType;
Expand All @@ -349,6 +351,8 @@ export interface IWorldOptions<ParametersType = any> {
// (undocumented)
attach: ICreateAttachment;
// (undocumented)
link: ICreateLink;
// (undocumented)
log: ICreateLog;
// (undocumented)
parameters: ParametersType;
Expand Down Expand Up @@ -532,10 +536,12 @@ export const When: IDefineStep_2;

// @public (undocumented)
export class World<ParametersType = any> implements IWorld<ParametersType> {
constructor({ attach, log, parameters }: IWorldOptions<ParametersType>);
constructor({ attach, log, link, parameters, }: IWorldOptions<ParametersType>);
// (undocumented)
readonly attach: ICreateAttachment;
// (undocumented)
readonly link: ICreateLink;
// (undocumented)
readonly log: ICreateLog;
// (undocumented)
readonly parameters: ParametersType;
Expand Down
36 changes: 36 additions & 0 deletions features/html_formatter.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
Feature: HTML formatter

Rule: Attachments except logs are externalised based on the externalAttachments option

Background:
Given a file named "features/a.feature" with:
"""
Feature: a feature
Scenario: a scenario
Given a step
"""
And a file named "features/steps.js" with:
"""
const {Given, world} = require('@cucumber/cucumber')

Given('a step', () => {
world.log('Logging some info')
world.link('https://cucumber.io')
world.attach(btoa('Base64 text'), 'base64:text/plain')
world.attach('Plain text', 'text/plain')
})
"""

Scenario: Without externalAttachments option
When I run cucumber-js with `--format html:html.out`
Then it passes
And the html formatter output is complete
And the formatter has no externalised attachments

Scenario: With externalAttachments option
When I run cucumber-js with `--format html:html.out --format-options '{"html":{"externalAttachments":true}}'`
Then it passes
And the html formatter output is complete
And the formatter has these external attachments:
| Base64 text |
| Plain text |
26 changes: 25 additions & 1 deletion features/step_definitions/formatter_steps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import path from 'node:path'
import { expect, use } from 'chai'
import chaiExclude from 'chai-exclude'
import fs from 'mz/fs'
import { Then } from '../../'
import { Then, DataTable } from '../../'
import {
ignorableKeys,
normalizeJsonOutput,
Expand Down Expand Up @@ -69,3 +69,27 @@ Then('the html formatter output is complete', async function (this: World) {
expect(actual).to.contain('<html lang="en">')
expect(actual).to.contain('</html>')
})

Then(
'the formatter has no externalised attachments',
async function (this: World) {
const actual = fs
.readdirSync(this.tmpDir)
.filter((filename) => filename.startsWith('attachment-')).length
expect(actual).to.eq(0)
}
)

Then(
'the formatter has these external attachments:',
async function (this: World, table: DataTable) {
const actual = fs
.readdirSync(this.tmpDir)
.filter((filename) => filename.startsWith('attachment-'))
.map((filename) =>
fs.readFileSync(path.join(this.tmpDir, filename), { encoding: 'utf-8' })
)
actual.sort()
expect(actual).to.deep.eq(table.raw().map((row) => row[0]))
}
)
45 changes: 29 additions & 16 deletions package-lock.json

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

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@
"@cucumber/gherkin": "28.0.0",
"@cucumber/gherkin-streams": "5.0.1",
"@cucumber/gherkin-utils": "9.0.0",
"@cucumber/html-formatter": "21.3.1",
"@cucumber/html-formatter": "21.5.0",
"@cucumber/message-streams": "4.0.1",
"@cucumber/messages": "24.1.0",
"@cucumber/tag-expressions": "6.1.0",
Expand All @@ -239,6 +239,7 @@
"lodash.merge": "^4.6.2",
"lodash.mergewith": "^4.6.2",
"luxon": "3.2.1",
"mime": "^3.0.0",
"mkdirp": "^2.1.5",
"mz": "^2.7.0",
"progress": "^2.0.3",
Expand Down
15 changes: 10 additions & 5 deletions src/api/formatters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export async function initializeFormatters({

async function initializeFormatter(
stream: IFormatterStream,
directory: string | undefined,
target: string,
specifier: string
): Promise<void> {
Expand Down Expand Up @@ -73,21 +74,25 @@ export async function initializeFormatters({
await pluginManager.initFormatter(
implementation,
configuration.options,
stream.write.bind(stream)
logger,
stream.write.bind(stream),
directory
)
if (stream !== stdout) {
cleanupFns.push(promisify<any>(stream.end.bind(stream)))
}
}
}

await initializeFormatter(stdout, 'stdout', configuration.stdout)
await initializeFormatter(stdout, undefined, 'stdout', configuration.stdout)
for (const [target, specifier] of Object.entries(configuration.files)) {
await initializeFormatter(
await createStream(target, onStreamError, cwd, logger),
const { stream, directory } = await createStream(
target,
specifier
onStreamError,
cwd,
logger
)
await initializeFormatter(stream, directory, target, specifier)
}

return async function () {
Expand Down
Loading
Loading