Skip to content

Commit 942f912

Browse files
authored
Text attachments are collapsible (#108)
* Places text attachments in a collapsible section using the html "details" tag as per discussion #78 * Updated CHANGELOG.md and bumped version to 19.1.2 * attached images now include the specified file name if it's been provided added missing assertions for image attachment summary added additional tests for image filename rendering changed layout of the previously committed text collapsible to match that of the existing Image collapsible * added missing tests for video attachment rendering video attachment summary is now the attachment name if provided otherwise it's the constant "Attached Video" * added missing tests for video attachment rendering video attachment summary is now the attachment name if provided otherwise it's the constant "Attached Video" * linter fixes * Linter fix * linter fixes add media type to attachment default title * linter fixes * undid change to autocrlf
1 parent 3abf991 commit 942f912

File tree

3 files changed

+114
-16
lines changed

3 files changed

+114
-16
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
66
and this project adheres to [Semantic Versioning](http://semver.org/).
77

88
## [Unreleased]
9+
### Changed
10+
- Text attachments in the html report are now displayed as collapsible sections
11+
### Added
12+
- Attachment names are now displayed as the title of attachment sections in the report
13+
- Where an attachment name is not provided then the media type is included in the default name of the attachment
914

1015
## [19.1.1] - 2022-03-22
1116
### Fixed

src/components/gherkin/Attachment.tsx

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
import * as messages from '@cucumber/messages'
2-
import { faPaperclip } from '@fortawesome/free-solid-svg-icons'
3-
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
42
// @ts-ignore
53
import Convert from 'ansi-to-html'
64
import React from 'react'
@@ -54,9 +52,12 @@ function image(attachment: messages.Attachment, classes: AttachmentClasses) {
5452
/>
5553
)
5654
}
55+
56+
const attachmentTitle = attachment.fileName ?? 'Attached Image (' + attachment.mediaType + ')'
57+
5758
return (
5859
<details>
59-
<summary>Attached Image</summary>
60+
<summary>{attachmentTitle}</summary>
6061
<img
6162
alt="Embedded Image"
6263
src={`data:${attachment.mediaType};base64,${attachment.body}`}
@@ -67,6 +68,8 @@ function image(attachment: messages.Attachment, classes: AttachmentClasses) {
6768
}
6869

6970
function video(attachment: messages.Attachment) {
71+
const attachmentTitle = attachment.fileName ?? 'Attached Video (' + attachment.mediaType + ')'
72+
7073
if (attachment.contentEncoding !== 'BASE64') {
7174
return (
7275
<ErrorMessage
@@ -76,7 +79,7 @@ function video(attachment: messages.Attachment) {
7679
}
7780
return (
7881
<details>
79-
<summary>Attached Video</summary>
82+
<summary>{attachmentTitle}</summary>
8083
<video controls>
8184
<source src={`data:${attachment.mediaType};base64,${attachment.body}`} />
8285
Your browser is unable to display video
@@ -106,19 +109,25 @@ function text(
106109
const body =
107110
attachment.contentEncoding === 'IDENTITY' ? attachment.body : base64Decode(attachment.body)
108111

112+
const attachmentTitle = attachment.fileName ?? 'Attached Text (' + attachment.mediaType + ')'
113+
109114
if (dangerouslySetInnerHTML) {
110115
return (
111-
<pre className={classes.text}>
112-
<FontAwesomeIcon icon={faPaperclip} className={classes.icon} />
113-
<span dangerouslySetInnerHTML={{ __html: prettify(body) }} />
114-
</pre>
116+
<details>
117+
<summary>{attachmentTitle}</summary>
118+
<pre className={classes.text}>
119+
<span dangerouslySetInnerHTML={{ __html: prettify(body) }} />
120+
</pre>
121+
</details>
115122
)
116123
}
117124
return (
118-
<pre className={classes.text}>
119-
<FontAwesomeIcon icon={faPaperclip} className={classes.icon} />
120-
{prettify(body)}
121-
</pre>
125+
<details>
126+
<summary>{attachmentTitle}</summary>
127+
<pre className={classes.text}>
128+
<span>{prettify(body)}</span>
129+
</pre>
130+
</details>
122131
)
123132
}
124133

test/components/gherkin/AttachmentTest.tsx

Lines changed: 88 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,37 @@ import { Attachment } from '../../../src/components/gherkin'
66
import { render } from '../utils'
77

88
describe('<Attachment>', () => {
9+
it('renders a video', () => {
10+
const binary = new Uint8Array(10)
11+
binary.fill(255, 0, binary.length)
12+
const attachment: messages.Attachment = {
13+
mediaType: 'video/mp4',
14+
body: 'fake-base64',
15+
contentEncoding: messages.AttachmentContentEncoding.BASE64,
16+
}
17+
const { container } = render(<Attachment attachment={attachment} />)
18+
const summary = container.querySelector('details summary')
19+
const video = container.querySelector('video source')
20+
assert.strictEqual(summary!.textContent, 'Attached Video (video/mp4)')
21+
assert.strictEqual(video!.getAttribute('src'), 'data:video/mp4;base64,fake-base64')
22+
})
23+
24+
it('renders a video with a name', () => {
25+
const binary = new Uint8Array(10)
26+
binary.fill(255, 0, binary.length)
27+
const attachment: messages.Attachment = {
28+
mediaType: 'video/mp4',
29+
fileName: 'the attachment name',
30+
body: 'fake-base64',
31+
contentEncoding: messages.AttachmentContentEncoding.BASE64,
32+
}
33+
const { container } = render(<Attachment attachment={attachment} />)
34+
const summary = container.querySelector('details summary')
35+
const video = container.querySelector('video source')
36+
assert.strictEqual(summary!.textContent, 'the attachment name')
37+
assert.strictEqual(video!.getAttribute('src'), 'data:video/mp4;base64,fake-base64')
38+
})
39+
940
it('renders an image', () => {
1041
const binary = new Uint8Array(10)
1142
binary.fill(255, 0, binary.length)
@@ -15,7 +46,25 @@ describe('<Attachment>', () => {
1546
contentEncoding: messages.AttachmentContentEncoding.BASE64,
1647
}
1748
const { container } = render(<Attachment attachment={attachment} />)
49+
const summary = container.querySelector('details summary')
1850
const img = container.querySelector('img')
51+
assert.strictEqual(summary!.textContent, 'Attached Image (image/png)')
52+
assert.strictEqual(img!.getAttribute('src'), 'data:image/png;base64,fake-base64')
53+
})
54+
55+
it('renders an image with a name', () => {
56+
const binary = new Uint8Array(10)
57+
binary.fill(255, 0, binary.length)
58+
const attachment: messages.Attachment = {
59+
mediaType: 'image/png',
60+
fileName: 'the attachment name',
61+
body: 'fake-base64',
62+
contentEncoding: messages.AttachmentContentEncoding.BASE64,
63+
}
64+
const { container } = render(<Attachment attachment={attachment} />)
65+
const summary = container.querySelector('details summary')
66+
const img = container.querySelector('img')
67+
assert.strictEqual(summary!.textContent, 'the attachment name')
1968
assert.strictEqual(img!.getAttribute('src'), 'data:image/png;base64,fake-base64')
2069
})
2170

@@ -26,8 +75,24 @@ describe('<Attachment>', () => {
2675
contentEncoding: messages.AttachmentContentEncoding.BASE64,
2776
}
2877
const { container } = render(<Attachment attachment={attachment} />)
29-
const pre = container.querySelector('pre')
30-
assert.strictEqual(pre!.textContent, 'hello')
78+
const summary = container.querySelector('details summary')
79+
const data = container.querySelector('details pre span')
80+
assert.strictEqual(summary!.textContent, 'Attached Text (text/plain)')
81+
assert.strictEqual(data!.textContent, 'hello')
82+
})
83+
84+
it('renders base64 encoded plaintext with a name', () => {
85+
const attachment: messages.Attachment = {
86+
mediaType: 'text/plain',
87+
fileName: 'the attachment name',
88+
body: Buffer.from('hello').toString('base64'),
89+
contentEncoding: messages.AttachmentContentEncoding.BASE64,
90+
}
91+
const { container } = render(<Attachment attachment={attachment} />)
92+
const summary = container.querySelector('details summary')
93+
const data = container.querySelector('details pre span')
94+
assert.strictEqual(summary!.textContent, 'the attachment name')
95+
assert.strictEqual(data!.textContent, 'hello')
3196
})
3297

3398
it('correctly renders ANSI characters', () => {
@@ -37,9 +102,28 @@ describe('<Attachment>', () => {
37102
contentEncoding: messages.AttachmentContentEncoding.IDENTITY,
38103
}
39104
const { container } = render(<Attachment attachment={attachment} />)
40-
const span = container.querySelector('pre > span')
105+
const summary = container.querySelector('details summary')
106+
const data = container.querySelector('details > pre > span')
107+
assert.strictEqual(summary!.textContent, 'Attached Text (text/x.cucumber.log+plain)')
108+
assert.strictEqual(
109+
data!.innerHTML,
110+
'<span style="color:#000">black<span style="color:#AAA">white</span></span>'
111+
)
112+
})
113+
114+
it('correctly renders ANSI characters with a name', () => {
115+
const attachment: messages.Attachment = {
116+
mediaType: 'text/x.cucumber.log+plain',
117+
fileName: 'the attachment name',
118+
body: '\x1b[30mblack\x1b[37mwhite',
119+
contentEncoding: messages.AttachmentContentEncoding.IDENTITY,
120+
}
121+
const { container } = render(<Attachment attachment={attachment} />)
122+
const summary = container.querySelector('details summary')
123+
const data = container.querySelector('details > pre > span')
124+
assert.strictEqual(summary!.textContent, 'the attachment name')
41125
assert.strictEqual(
42-
span!.innerHTML,
126+
data!.innerHTML,
43127
'<span style="color:#000">black<span style="color:#AAA">white</span></span>'
44128
)
45129
})

0 commit comments

Comments
 (0)