-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: visual regression testing and dev server app (#94)
<img height=20 src=https://readme.com/static/favicon.ico align=center> [Demo][demo] | 🎫 [Ticket][ticket] :---:|:---: ### 🧰 Changes Adds an example app to develop against. To start the dev server: ``` npm run start ``` And you can also run visual regression tests against it: ``` npm run test.browser ``` This runs `jest-image-snapshot` against the dev server. It uses saved screenshots in `__tests__/browser/__image_snapshots__` to compare. If it finds differences, it'll save diffs to `__tests__/browser/__image_snapshots__/__diff_output__`. Currently, there's little guarantee that your environment will match the Github Actions environment. So, if you need to update snapshots, the steps are: * `sed -i -e 's/test.browser/test.browser -- -u/' .github/workflows/ci.yml` * commit and push to github * download 'Artifacts' from last build * commit updated snapshots from 'Artifacts' --- ### TODO - [x] update snapshots from CI - [x] trim CI adornments --- ### Next steps - [x] Add PR apps via heroku pipeline. Adding PR apps will make it nice to snapshot changes in PRs. - [ ] dockerize puppeteer. Dockerizing the puppeteer browser should reduce broswer rendering fragility with the tests, and make it easier to create snapshots that match CI. --- ### Things that are broken - [ ] testing snapshots currently fails locally. Presumably until we use docker locally, we'll be out of sync with CI. - [ ] `npm run test.browser` seems to have broken jest's cache. You need to run `npx jest --clearCache` between runs - [ ] `npm run test.browser.watch` does not kill the dev server. We typically won't be running it in watch mode. [demo]: https://markdown-pr-94.herokuapp.com/# [ticket]: https://app.asana.com/0/1199089258199423/1199546355080189/f
- Loading branch information
1 parent
f4bd718
commit 0774166
Showing
49 changed files
with
2,262 additions
and
88 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,4 +7,8 @@ node_modules | |
*.code-* | ||
*.sublime-* | ||
|
||
.DS_Store | ||
.DS_Store | ||
|
||
__image_snapshots__ | ||
|
||
example/img/emojis |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
.DEFAULT_GOAL := help | ||
.PHONY: help | ||
|
||
emojis: example/img/emojis ## Install our emojis | ||
|
||
example/img/emojis: node_modules/@readme/emojis | ||
rm -rf example/img/emojis | ||
mkdir -p example/img/emojis | ||
cp node_modules/@readme/emojis/src/img/*.png example/img/emojis/ | ||
|
||
help: ## Show this help. | ||
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
web: npx http-server example --port $PORT |
Binary file added
BIN
+1.1 MB
...sual-regression-tests-rdmd-syntax-renders-callouts-without-surprises-1-diff.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+317 KB
...sual-regression-tests-rdmd-syntax-renders-callouts-without-surprises-1-snap.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+84.8 KB
...ression-tests-rdmd-syntax-renders-code-block-tests-without-surprises-1-snap.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+11.8 KB
...ion-tests-rdmd-syntax-renders-code-block-vars-test-without-surprises-1-snap.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+186 KB
...l-regression-tests-rdmd-syntax-renders-code-blocks-without-surprises-1-snap.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+80.5 KB
...visual-regression-tests-rdmd-syntax-renders-embeds-without-surprises-1-snap.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+121 KB
...sual-regression-tests-rdmd-syntax-renders-features-without-surprises-1-snap.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+121 KB
...sual-regression-tests-rdmd-syntax-renders-headings-without-surprises-1-snap.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+144 KB
...visual-regression-tests-rdmd-syntax-renders-images-without-surprises-1-snap.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+88.5 KB
...-visual-regression-tests-rdmd-syntax-renders-lists-without-surprises-1-snap.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+79.8 KB
...visual-regression-tests-rdmd-syntax-renders-tables-without-surprises-1-snap.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import 'babel-polyfill'; | ||
import { setup as setupPuppeteer } from 'jest-environment-puppeteer'; | ||
|
||
module.exports = async function globalSetup(globalConfig) { | ||
await setupPuppeteer(globalConfig); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
/* global page */ | ||
|
||
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); | ||
|
||
describe('visual regression tests', () => { | ||
describe('rdmd syntax', () => { | ||
beforeEach(async () => { | ||
await page.setViewport({ width: 800, height: 800 }); | ||
}); | ||
|
||
const docs = [ | ||
'callouts', | ||
'codeBlockTests', | ||
'codeBlockVarsTest', | ||
'codeBlocks', | ||
'embeds', | ||
'features', | ||
'headings', | ||
'images', | ||
'lists', | ||
'tables', | ||
]; | ||
|
||
it.each(docs)( | ||
'renders "%s" without surprises', | ||
async doc => { | ||
const uri = `http://localhost:9966/?ci=true#${doc}`; | ||
await page.goto(uri, { waitUntil: 'networkidle0' }); | ||
await sleep(500); | ||
|
||
const image = await page.screenshot({ fullPage: true }); | ||
|
||
expect(image).toMatchImageSnapshot(); | ||
}, | ||
10000 | ||
); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { configureToMatchImageSnapshot } from 'jest-image-snapshot'; | ||
import path from 'path'; | ||
|
||
const opts = {}; | ||
|
||
if (process.env.CI) { | ||
opts.customSnapshotsDir = path.resolve('__tests__/browser/ci/'); | ||
} | ||
|
||
const toMatchImageSnapshot = configureToMatchImageSnapshot(opts); | ||
|
||
expect.extend({ toMatchImageSnapshot }); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
|
||
import markdown from '../index'; | ||
import Fixtures from './Fixtures'; | ||
import Header from './Header'; | ||
import Router from './Router'; | ||
|
||
require('./demo.scss'); | ||
|
||
function DemoContent({ ci, children, fixture, name, onChange, opts }) { | ||
if (ci) { | ||
return ( | ||
<div className="rdmd-demo--display"> | ||
<div className="markdown-body">{markdown(fixture, opts)}</div> | ||
</div> | ||
); | ||
} | ||
|
||
return ( | ||
<React.Fragment> | ||
<div className="rdmd-demo--editor"> | ||
<div className="rdmd-demo--editor-container"> | ||
{children} | ||
<textarea name="demo-editor" onChange={onChange} value={fixture} /> | ||
</div> | ||
</div> | ||
<div className="rdmd-demo--display"> | ||
<h2 className="rdmd-demo--markdown-header">{name}</h2> | ||
<div className="markdown-body">{markdown(fixture, opts)}</div> | ||
</div> | ||
</React.Fragment> | ||
); | ||
} | ||
|
||
DemoContent.propTypes = { | ||
children: PropTypes.node.isRequired, | ||
ci: PropTypes.string, | ||
fixture: PropTypes.string, | ||
name: PropTypes.string.isRequired, | ||
onChange: PropTypes.func.isRequired, | ||
opts: PropTypes.obj, | ||
}; | ||
|
||
function Demo({ opts }) { | ||
// eslint-disable-next-line no-restricted-globals | ||
const ci = new URLSearchParams(location.search).get('ci'); | ||
|
||
return ( | ||
<React.Fragment> | ||
{!ci && <Header />} | ||
<div className="rdmd-demo--container"> | ||
<div className="rdmd-demo--content"> | ||
<Router | ||
render={({ route, getRoute }) => { | ||
return ( | ||
<Fixtures | ||
ci={ci} | ||
getRoute={getRoute} | ||
render={props => <DemoContent {...props} ci={ci} opts={opts} />} | ||
selected={route} | ||
/> | ||
); | ||
}} | ||
/> | ||
</div> | ||
</div> | ||
</React.Fragment> | ||
); | ||
} | ||
|
||
Demo.propTypes = { | ||
opts: PropTypes.obj, | ||
}; | ||
|
||
export default Demo; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,179 @@ | ||
[block:api-header] | ||
{ | ||
"title": "Syntax" | ||
} | ||
[/block] | ||
Callouts are very nearly equivalent to standard Markdown block quotes in their syntax, other than some specific requirements on their content: To be considered a “callout”, a block quote must start with an initial emoji. This is used to determine the callout's theme. Here's an example of how you might write a warning callout. | ||
|
||
> 👍 Success | ||
> | ||
> Vitae reprehenderit at aliquid error voluptates eum dignissimos. | ||
|
||
### Emoji Themes | ||
|
||
Default themes are specified using one of the following emojis. (If you don't like the one we've chosen, you can always switch to the alternate!) | ||
|
||
| Emoji | Class | Alternate | | ||
|:-----:|:--------|-------------:| | ||
| 📘 | `.callout_info` | ℹ️ | | ||
| 👍 | `.callout_okay` | ✅ | | ||
| 🚧 | `.callout_warn` | ⚠️ | | ||
| ❗️ | `.callout_error` | 🛑 | | ||
|
||
<hr> | ||
<details><summary><em>Tips & Tricks </em></summary><br> | ||
|
||
If you have a block quote that starts with an initial emoji which *should not* be rendered as a ReadMe callout, just bold the emoji. It's a bit of a hack for sure, but it's easy enough, and hey: it works! So this: | ||
|
||
> **👋** Lorem ipsum dolor sit amet consectetur adipisicing elit. | ||
|
||
Renders to a plain ol' block quote: | ||
|
||
> **👋** Lorem ipsum dolor sit amet consectetur adipisicing elit. | ||
</details><hr> | ||
[block:api-header] | ||
{ | ||
"title": "Examples" | ||
} | ||
[/block] | ||
|
||
[block:callout] | ||
{ | ||
"type": "success", | ||
"body": "Vitae reprehenderit at aliquid error voluptates eum dignissimos.", | ||
"title": "[Success](#edge-cases)" | ||
} | ||
[/block] | ||
> 📘 Info | ||
> | ||
> Lorem ipsum dolor sit amet consectetur adipisicing elit. | ||
> 🚧 Warning | ||
> | ||
> Hic, neque a nisi adipisci non repudiandae ratione id natus. | ||
> ❗️ Error | ||
> Sunt eius porro assumenda sequi, explicabo dolorem unde. | ||
If a callout starts with an emoji that's not dedicated to one of the themes (above), the component will fall back to a default block quote-style color scheme. | ||
|
||
> 🥇 Themeless | ||
> | ||
> Lorem ipsum dolor sit amet consectetur adipisicing elit. | ||
[block:api-header] | ||
{ | ||
"title": "Custom CSS" | ||
} | ||
[/block] | ||
Callouts come in [various themes](#section--examples-). These can be customized using the following CSS selectors and variables: | ||
|
||
|
||
```scss CSS Variables | ||
.markdown-body .callout.callout_warn { | ||
--text: #6a737d; // theme text color default | ||
--title: inherit; // theme title color (falls back to text color by default) | ||
--background: #f8f8f9; | ||
--border: #8b939c; | ||
} | ||
``` | ||
```scss Theme Selectors | ||
.markdown-body .callout.callout_default {} /* gray */ | ||
.markdown-body .callout.callout_info {} /* blue */ | ||
.markdown-body .callout.callout_okay {} /* green */ | ||
.markdown-body .callout.callout_warn {} /* orange */ | ||
.markdown-body .callout.callout_error {} /* red */ | ||
``` | ||
|
||
### Extended Themes | ||
|
||
Each callout will also have a `theme` attribute that's set to it's emoji prefix. Combined with a basic attribute selector, we should be able to create entirely new styles per-emoji, in addition to the built in themes above! | ||
|
||
```css Custom CSS | ||
.markdown-body .callout[theme="🎅"] { | ||
--background: #c54245; | ||
--border: #ffffff6b; | ||
--text: #f5fffa; | ||
} | ||
``` | ||
```markdown Markdown Syntax | ||
> 🎅 Old Saint Nick | ||
> | ||
> 'Twas the night before Christmas, when all through the house not a creature was stirring, not even a mouse. The stockings were hung by the chimney with care, in hopes that St. Nicholas soon would be there. The children were nestled all snug in their beds, while visions of sugar plums danced in their heads. | ||
``` | ||
```html Generated HTML | ||
<!-- condensed for clarity! --> | ||
<blockquote class="callout callout_default" theme="🎅"> | ||
<h3>🎅 Old Saint Nick</h3> | ||
<p>'Twas the night before Christmas, when all through the house not a creature was stirring, not even a mouse. The stockings were hung by the chimney with care, in hopes that St. Nicholas soon would be there. The children were nestled all snug in their beds, while visions of sugar plums danced in their heads.</p> | ||
</blockquote> | ||
``` | ||
|
||
And voilà... | ||
|
||
> 🎅 Old Saint Nick | ||
> | ||
> 'Twas the night before Christmas, when all through the house not a creature was stirring, not even a mouse. The stockings were hung by the chimney with care, in hopes that St. Nicholas soon would be there. The children were nestled all snug in their beds, while visions of sugar plums danced in their heads. | ||
### Custom Icons | ||
|
||
Emojis are already a pretty good starting point as far as default icon options go! There are a *lot* of 'em, and they're supported across nearly all platforms. But what if we're going for a different look, or need to match our docs to a branding kit? Icons are a big part of setting the "tone" for your site. | ||
|
||
With a touch of Custom CSS, we should be able to get a callout using the 📷 emoji to display an icon font glyph! | ||
|
||
```css Custom CSS | ||
.callout[theme=📷] { | ||
--emoji: unset; | ||
--icon: "\f083"; /* copied front FontAwesome */ | ||
--icon-color: #c50a50; | ||
} | ||
``` | ||
``` Markdown Syntax | ||
> 📷 Cool pix! | ||
> | ||
> Vitae reprehenderit at aliquid error voluptates eum dignissimos. | ||
``` | ||
|
||
This works like a charm: | ||
|
||
<div id="my-theme"> | ||
|
||
> 📸 Cool pix! | ||
> Vitae reprehenderit at aliquid error voluptates eum dignissimos. | ||
[block:html] | ||
{ | ||
"html": "<style>\n #my-theme .callout[theme=📸] {\n --emoji: unset;\n --icon: \"\";\n }\n #my-theme .callout[theme=📷],\n #my-theme .callout[theme=📸] {\n --icon-color: #c50a50;\n --border: var(--icon-color);\n --title: var(--icon-color);\n }\n summary {\n outline: none;\n user-select: none;\n }\n</style>" | ||
} | ||
[/block] | ||
</div> | ||
|
||
<hr><details><summary><em>Setting the Custom Icon Font</em></summary><br> | ||
|
||
The custom icon font defaults to `FontAwesome`, but you can use any font family available on the page by setting the `--icon-font` variable! | ||
|
||
```css | ||
.callout[theme=📷] { | ||
--icon-font-family: FontAwesome; /* copied from https://fontawesome.com/v4.7.0/icon/camera-retro */ | ||
} | ||
``` | ||
|
||
</details><hr> | ||
[block:api-header] | ||
{ | ||
"title": "Edge Cases" | ||
} | ||
[/block] | ||
Callouts don't need to have any body text: | ||
|
||
> 🥇 No body text. | ||
You can also skip the title, if you're so inclined! | ||
|
||
> 🥈 | ||
> | ||
> Lorem ipsum dolor sit amet consectetur adipisicing elit. Error eos animi obcaecati quod repudiandae aliquid nemo veritatis ex, quos delectus minus sit omnis vel dolores libero, recusandae ea dignissimos iure? | ||
[block:html] | ||
{ | ||
"html": "<style>\n.markdown-body .callout[theme=\"🎅\"] {\n --background: #c50a4f;\n --border: #ffffff6b;\n --text: #f5fffa;\n}\n</style>" | ||
} | ||
[/block] |
Oops, something went wrong.