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

Incremental rendering of HTML report #121

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]

### Fixed
- Incrementally render the HTML report. ([#120](https://github.com/cucumber/html-formatter/issues/120), [#121](https://github.com/cucumber/html-formatter/pull/121))

## [19.2.0] - 2022-05-27
### Changed
- Upgrade to `@cucumber/react-components` `^20.1.0`
Expand Down
14 changes: 1 addition & 13 deletions javascript/.gitignore
Original file line number Diff line number Diff line change
@@ -1,17 +1,5 @@
dist/
.idea/
.nyc_output/
coverage/
node_modules/
*.log
.deps
.tested*
.linted
.built*
.compared
.codegen
acceptance/
storybook-static
*-go
*.iml
.vscode-test
html/
21 changes: 21 additions & 0 deletions javascript/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,24 @@
# html-formatter

> Takes a stream of Cucumber messages and outputs a standalone HTML report using Cucumber's React components

## Manually testing incremental output

The generated HTML report can be viewed before the entire report has been generated. This can be tested manually.

npm install
npm run build
npm run validate

You should now have some HTML reports under `html/*.html`. Let's render this incrementally:

rm -f incremental.html
touch incremental.html

# open the empty file in a browser
open incremental.html

# incrementally write some contents into that file, simulating cucumber writing the file slowly
cat html/examples-tables.feature.html | ./scripts/slowcat > incremental.html

Return to the browser. Keep refresh it. You should see that the report contents changes.
Empty file added javascript/html/.gitkeep
Empty file.
2 changes: 1 addition & 1 deletion javascript/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"types": "dist/src/CucumberHtmlStream.d.ts",
"repository": {
"type": "git",
"url": "git+https://github.com/cucumber/cucumber.git"
"url": "git+https://github.com/cucumber/cucumber-html.git"
},
"author": "Aslak Hellesøy",
"license": "MIT",
Expand Down
15 changes: 15 additions & 0 deletions javascript/scripts/slowcat
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/usr/bin/env bash
#
# Works like cat, but slows down printing after seeing the first </script> tag.
# By default, the delay in slow mode is 1 second. This can be overridden by passing an argument
#

pause=${1:-1}
slow=false
while IFS='$\n' read -r line; do
if [[ "$slow" = true ]]; then sleep $pause; fi
echo "$line"
if [[ "$line" = "</script>" ]] && [[ "$slow" = false ]]; then
slow=true;
fi
done
22 changes: 7 additions & 15 deletions javascript/src/CucumberHtmlStream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,12 @@ export default class CucumberHtmlStream extends Transform {
if (err) return callback(err)
this.writeFile(this.cssPath, (err) => {
if (err) return callback(err)
this.writeTemplateBetween('{{css}}', '{{messages}}', (err) => {
this.writeTemplateBetween('{{css}}', '{{script}}', (err) => {
if (err) return callback(err)
callback()
this.writeFile(this.jsPath, (err) => {
if (err) return callback(err)
this.writeTemplateBetween('{{script}}', '{{messages}}', callback)
})
})
})
})
Expand All @@ -58,13 +61,7 @@ export default class CucumberHtmlStream extends Transform {
private writePostMessage(callback: TransformCallback) {
this.writePreMessageUnlessAlreadyWritten((err) => {
if (err) return callback(err)
this.writeTemplateBetween('{{messages}}', '{{script}}', (err) => {
if (err) return callback(err)
this.writeFile(this.jsPath, (err) => {
if (err) return callback(err)
this.writeTemplateBetween('{{script}}', null, callback)
})
})
this.writeTemplateBetween('{{messages}}', null, callback)
})
}

Expand Down Expand Up @@ -110,11 +107,6 @@ export default class CucumberHtmlStream extends Transform {
}

private writeMessage(envelope: messages.Envelope) {
if (!this.firstMessageWritten) {
this.firstMessageWritten = true
} else {
this.push(',')
}
this.push(JSON.stringify(envelope))
this.push(`<script>p(${JSON.stringify(envelope)});</script>\n`)
}
}
7 changes: 2 additions & 5 deletions javascript/src/index.mustache.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,10 @@
</style>
</head>
<body>
<div id="content">
</div>
<script>
window.CUCUMBER_MESSAGES = [{{messages}}];
</script>
<div id="content"></div>
<script>
{{script}}
</script>
{{messages}}
</body>
</html>
31 changes: 18 additions & 13 deletions javascript/src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,32 @@
import './styles.scss'

import * as messages from '@cucumber/messages'
import { Envelope } from '@cucumber/messages'
import { components, searchFromURLParams } from '@cucumber/react-components'
import React from 'react'
import React, { useState } from 'react'
import ReactDOM from 'react-dom'

const { CucumberReact } = components
const { FilteredResults, EnvelopesWrapper, SearchWrapper } = components.app

declare global {
interface Window {
CUCUMBER_MESSAGES: messages.Envelope[]
p(envelope: Envelope): void
}
}

const app = (
<CucumberReact theme="auto">
<EnvelopesWrapper envelopes={window.CUCUMBER_MESSAGES}>
<SearchWrapper {...searchFromURLParams()}>
<FilteredResults className="html-formatter" />
</SearchWrapper>
</EnvelopesWrapper>
</CucumberReact>
)
const App: React.FunctionComponent = () => {
const [envelopes, setEnvelopes] = useState<readonly Envelope[]>([])
window.p = (envelope) => setEnvelopes(envelopes.concat(envelope))

ReactDOM.render(app, document.getElementById('content'))
return (
<CucumberReact theme="auto">
<EnvelopesWrapper envelopes={envelopes}>
<SearchWrapper {...searchFromURLParams()}>
<FilteredResults className="html-formatter" />
</SearchWrapper>
</EnvelopesWrapper>
</CucumberReact>
)
}

ReactDOM.render(<App />, document.getElementById('content'))
19 changes: 4 additions & 15 deletions javascript/test/CucumberHtmlStreamTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,14 @@ async function renderAsHtml(
}

describe('CucumberHtmlStream', () => {
it('writes zero messages to html', async () => {
const html = await renderAsHtml()
assert(html.indexOf('window.CUCUMBER_MESSAGES = []') >= 0)
})

it('writes one message to html', async () => {
const e1: messages.Envelope = {
testRunStarted: {
timestamp: { seconds: 0, nanos: 0 },
},
}
const html = await renderAsHtml(e1)
assert(
html.indexOf(`window.CUCUMBER_MESSAGES = [${JSON.stringify(e1)}]`) >= 0
)
assert(html.indexOf(`p(${JSON.stringify(e1)})`) >= 0)
})

it('writes two messages to html', async () => {
Expand All @@ -65,12 +58,8 @@ describe('CucumberHtmlStream', () => {
},
}
const html = await renderAsHtml(e1, e2)
assert(
html.indexOf(
`window.CUCUMBER_MESSAGES = [${JSON.stringify(e1)},${JSON.stringify(
e2
)}]`
) >= 0
)
console.log(html)
assert(html.indexOf(`<script>p(${JSON.stringify(e1)});</script>`) >= 0)
assert(html.indexOf(`<script>p(${JSON.stringify(e2)});</script>`) >= 0)
})
})
9 changes: 6 additions & 3 deletions javascript/test/acceptance.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { NdjsonToMessageStream } from '@cucumber/message-streams'
import assert from 'assert'
import fs from 'fs'
import { writeFile } from 'fs/promises'
import glob from 'glob'
import path from 'path'
import puppeteer from 'puppeteer'
Expand Down Expand Up @@ -33,10 +34,11 @@ describe('html-formatter', () => {
`./node_modules/@cucumber/compatibility-kit/features/**/*.ndjson`
)
for (const ndjson of files) {
it(`can render ${path.basename(ndjson, '.ndjson')}`, async () => {
const basename = path.basename(ndjson, '.ndjson')
it(`can render ${basename}`, async () => {
const ndjsonData = fs.createReadStream(ndjson, { encoding: 'utf-8' })
const toMessageStream = new NdjsonToMessageStream()
const htmlData = await new Promise<string>((resolve, reject) => {
const html = await new Promise<string>((resolve, reject) => {
const chunks: Buffer[] = []
const out = new PassThrough()
.on('data', (chunk) => chunks.push(Buffer.from(chunk)))
Expand All @@ -56,7 +58,8 @@ describe('html-formatter', () => {
}
)
})
assert.ok(await canRenderHtml(htmlData.toString()))
await writeFile(`html/${basename}.html`, html, 'utf-8')
assert.ok(await canRenderHtml(html))
})
}
})
1 change: 1 addition & 0 deletions javascript/test/dummy.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/* dummy css */
1 change: 1 addition & 0 deletions javascript/test/dummy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// dummy js