-
Notifications
You must be signed in to change notification settings - Fork 715
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
test(happo): add Happo for screenshot testing #1030
Changes from all commits
78fe380
39d3f5c
06dcaa4
151b62c
6f4b5dc
f873381
e0d9282
6f39705
0288009
e27faf7
6756c8d
2367913
e506453
eca1275
df71c51
e0a5e61
856df30
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
module.exports = { | ||
asyncTimeout: 500, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
const { RemoteBrowserTarget } = require('happo.io'); | ||
const { findPagesDir } = require('next/dist/lib/find-pages-dir'); | ||
const getWebpackConfig = require('next/dist/build/webpack-config').default; | ||
const nextConfig = require('./next.config'); | ||
const path = require('path'); | ||
const { asyncTimeout } = require('./.happo-variables'); | ||
|
||
const happoTmpDir = './.happo'; // should match .gitignore | ||
|
||
module.exports = { | ||
// these are provided via github actions in CI, locally you | ||
// need to provide them yourself | ||
apiKey: process.env.HAPPO_API_KEY, | ||
apiSecret: process.env.HAPPO_API_SECRET, | ||
|
||
// ms timeout for async examples (default=200) | ||
asyncTimeout, | ||
|
||
// don't use JSDOM for rendering. saves having to mock out | ||
// things like getBoundingClientRect + canvas element methods | ||
prerender: false, | ||
|
||
stylesheets: [path.join(__dirname, '/public/static/doc_styles.css')], | ||
|
||
// happo snapshots to include | ||
include: 'happo/*.@(ts|tsx)', | ||
|
||
targets: { | ||
'chrome-desktop': new RemoteBrowserTarget('chrome', { | ||
viewport: '800x552', | ||
}), | ||
}, | ||
|
||
// extend next's webpack config so examples can be used directly | ||
// this is largely taken from the happo storybook plugin | ||
customizeWebpackConfig: async config => { | ||
const base = await getWebpackConfig(__dirname, { | ||
config: { | ||
devIndicators: {}, | ||
distDir: happoTmpDir, | ||
experimental: { plugins: [] }, | ||
future: {}, | ||
env: {}, | ||
pageExtensions: ['pages.js'], | ||
sassOptions: {}, // we don't have this loader | ||
...nextConfig, | ||
}, | ||
rewrites: [], | ||
entrypoints: {}, | ||
pagesDir: findPagesDir(process.cwd()), | ||
}); | ||
config.plugins = base.plugins; | ||
config.resolve = base.resolve; | ||
config.resolveLoader = base.resolveLoader; | ||
Object.keys(config.resolve.alias).forEach(key => { | ||
if (!config.resolve.alias[key]) { | ||
delete config.resolve.alias[key]; | ||
} | ||
}); | ||
config.module = base.module; | ||
return config; | ||
}, | ||
|
||
// happo is unable to resolve some imports if the tmpdir isn't located inside | ||
// the project structure. The default is an OS provided folder, `os.tmpdir()`. | ||
tmpdir: path.join(__dirname, happoTmpDir), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch, I've seen this happen myself. I'll make a note to change the default tmp folder to something like |
||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import React from 'react'; | ||
import { tiles as examples } from '../src/components/Gallery'; | ||
import AxisTile from '../src/components/Gallery/AxisTile'; | ||
import XYChartTile from '../src/components/Gallery/XYChartTile'; | ||
import { asyncTimeout } from '../.happo-variables.js'; | ||
|
||
type HappoSnapshot = { | ||
component: string; | ||
variants: { | ||
[key: string]: ( | ||
renderInDom: (component: React.ReactElement) => void, | ||
) => React.ReactNode | Promise<unknown>; | ||
}; | ||
}; | ||
|
||
const specialCases = new Set(['@visx/demo-axis', '@visx/demo-xychart']); | ||
|
||
// renders an example with a timeout | ||
const renderWithTimeout: ( | ||
Example: React.ReactElement, | ||
) => HappoSnapshot['variants'][string] = Example => renderInDom => { | ||
return new Promise(resolve => { | ||
renderInDom(Example); | ||
setTimeout(resolve, asyncTimeout); | ||
}); | ||
}; | ||
|
||
const getComponentName = (Example: typeof examples[0]) => | ||
Example.packageJson.name || 'missing-name'; | ||
|
||
const snapshots: HappoSnapshot[] = examples | ||
.filter(Example => !specialCases.has(getComponentName(Example))) | ||
.map(Example => ({ | ||
// note: this (reasonably) asserts Examples have unique names | ||
component: getComponentName(Example), | ||
variants: { default: () => <Example.default /> }, | ||
})); | ||
|
||
export default snapshots.concat([ | ||
// needs timeout for animated axes | ||
{ | ||
component: '@visx/demo-axis', | ||
variants: { | ||
default: renderWithTimeout(<AxisTile />), | ||
}, | ||
}, | ||
// needs timeout for animated axes | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One option that you might want to consider is to not use an animation if prefers-reduced-motion is set to reduce, and then add 'chrome-desktop': new RemoteBrowserTarget('chrome', {
viewport: '800x552',
prefersReducedMotion: true,
}), There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is a cool idea. it'll be a bit of work to re-write those examples because the animations aren't css-based. looks like you can do the media query in JS to detect it, so I can swap out animated components for their static analogs. I may do this in a follow up PR depending on how big the change is. |
||
{ | ||
component: '@visx/demo-xychart', | ||
variants: { | ||
default: renderWithTimeout(<XYChartTile />), | ||
}, | ||
}, | ||
]); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,11 +4,13 @@ | |
"description": "visx demo", | ||
"repository": "https://github.com/airbnb/visx", | ||
"scripts": { | ||
"dev": "next", | ||
"build": "next build && next export", | ||
"start": "next start", | ||
"deploy": "rm -rf out && yarn build && cd out && touch .nojekyll && git init && git add . && git commit -m \"Deploy commit\" && git remote add origin git@github.com:airbnb/visx.git && git push -f origin master:gh-pages", | ||
"dev": "next", | ||
"happo": "happo", | ||
"happo-ci-github-actions": "happo-ci-github-actions", | ||
"preview": "yarn build && cd ./out && npx serve", | ||
"deploy": "rm -rf out && yarn build && cd out && touch .nojekyll && git init && git add . && git commit -m \"Deploy commit\" && git remote add origin git@github.com:airbnb/visx.git && git push -f origin master:gh-pages" | ||
"start": "next start" | ||
}, | ||
"keywords": [ | ||
"visx", | ||
|
@@ -66,6 +68,7 @@ | |
"@visx/xychart": "1.4.0", | ||
"@visx/zoom": "1.3.0", | ||
"@zeit/next-css": "^1.0.1", | ||
"babel-loader": "8.2.2", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. needed by |
||
"classnames": "^2.2.5", | ||
"d3-array": "^1.1.1", | ||
"d3-collection": "^1.0.4", | ||
|
@@ -77,6 +80,7 @@ | |
"d3-scale-chromatic": "^1.3.3", | ||
"d3-shape": "^1.0.6", | ||
"d3-time-format": "^2.0.5", | ||
"happo.io": "^6.4.0", | ||
"markdown-loader": "^5.1.0", | ||
"next": "9.5.4", | ||
"nprogress": "^0.2.0", | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,26 @@ | ||
import { getSeededRandom } from '@visx/mock-data'; | ||
|
||
export interface Circle { | ||
id: string; | ||
radius: number; | ||
x: number; | ||
y: number; | ||
} | ||
|
||
const generateCircles = ({ width, height }: { width: number; height: number }) => | ||
new Array(width < 360 ? 40 : 185).fill(1).map((d, i) => { | ||
const radius = 25 - Math.random() * 20; | ||
const generateCircles = ({ width, height }: { width: number; height: number }) => { | ||
const radiusRandom = getSeededRandom(0.2); | ||
const xRandom = getSeededRandom(0.3); | ||
const yRandom = getSeededRandom(0.4); | ||
|
||
return new Array(width < 360 ? 40 : 185).fill(1).map((d, i) => { | ||
const radius = 25 - radiusRandom() * 20; | ||
return { | ||
id: `${i}`, | ||
radius, | ||
x: Math.round(Math.random() * (width - radius * 2) + radius), | ||
y: Math.round(Math.random() * (height - radius * 2) + radius), | ||
x: Math.round(xRandom() * (width - radius * 2) + radius), | ||
y: Math.round(yRandom() * (height - radius * 2) + radius), | ||
}; | ||
}); | ||
}; | ||
|
||
export default generateCircles; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
noting that the
happo-ci
binary expects this path to be in the same directory, but because this is run in./packages/visx-demo/
it needs to point to the monorepo rootnode_modules
whereyarn
installs it.