Skip to content

Commit

Permalink
Merge pull request #67 from storybooks/inside-jest
Browse files Browse the repository at this point in the history
Added support for require.context similar to storyshots 2.x versions.
  • Loading branch information
arunoda authored Dec 23, 2016
2 parents b03e6da + 6d606f9 commit 84dc260
Show file tree
Hide file tree
Showing 14 changed files with 3,543 additions and 73 deletions.
2 changes: 1 addition & 1 deletion .babelrc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"presets": [
"es2015"
"es2015", "react"
],
"plugins": [
"transform-runtime"
Expand Down
10 changes: 10 additions & 0 deletions .storybook/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { configure } from '@kadira/storybook';

const req = require.context('../stories/required_with_context', true, /.stories.js$/)

function loadStories() {
req.keys().forEach((filename) => req(filename))
require('../stories/directly_required')
}

configure(loadStories, module);
11 changes: 10 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,25 @@
"scripts": {
"prepublish": "babel ./src --out-dir ./dist",
"lint": "standard",
"test": "npm run lint"
"jest": "jest",
"test": "npm run lint && npm run jest",
"storybook": "start-storybook -p 6006",
"build-storybook": "build-storybook"
},
"devDependencies": {
"@kadira/storybook": "^2.21.0",
"babel-cli": "^6.14.0",
"babel-jest": "^18.0.0",
"babel-plugin-transform-runtime": "^6.15.0",
"babel-preset-es2015": "^6.18.0",
"babel-preset-react": "^6.16.0",
"jest": "^18.0.0",
"react-dom": "^15.4.1",
"standard": "^8.6.0"
},
"dependencies": {
"babel-runtime": "^6.20.0",
"react": "^15.4.1",
"react-test-renderer": "^15.3.1",
"read-pkg-up": "^2.0.0"
}
Expand Down
21 changes: 14 additions & 7 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import renderer from 'react-test-renderer'
import path from 'path'
import readPkgUp from 'read-pkg-up'

import runWithRequireContext from './require_context'
const { describe, it, expect } = global

let storybook
let configPath

const babel = require('babel-core')

const pkg = readPkgUp.sync().pkg
const isStorybook =
(pkg.devDependencies && pkg.devDependencies['@kadira/storybook']) ||
Expand All @@ -18,21 +20,26 @@ const isRNStorybook =
export default function testStorySnapshots (options = {}) {
if (isStorybook) {
storybook = require.requireActual('@kadira/storybook')
const loadBabelConfig = require('@kadira/storybook/dist/server/babel_config').default
const configDirPath = path.resolve(options.configPath || '.storybook')
configPath = path.join(configDirPath, 'config.js')

const content = babel.transformFileSync(configPath, babelConfig).code
const contextOpts = {
filename: configPath,
dirname: configDirPath
}
const babelConfig = loadBabelConfig(configDirPath)

runWithRequireContext(content, contextOpts)
} else if (isRNStorybook) {
storybook = require.requireActual('@kadira/react-native-storybook')
configPath = path.resolve(options.configPath || 'storybook')
require.requireActual(configPath)
} else {
throw new Error('\'storyshots\' is intended only to be used with react storybook or react native storybook')
}

try {
require.requireActual(configPath)
} catch (e) {
throw new Error(`Could not load stories from ${configPath}. Check 'configPath' option`)
}

if (typeof describe !== 'function') {
throw new Error('\'testStorySnapshots\' is intended only to be used inside jest')
}
Expand Down
72 changes: 72 additions & 0 deletions src/require_context.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import vm from 'vm'
import fs from 'fs'
import path from 'path'
import moduleSystem from 'module'

function requireModules (keys, root, directory, regExp, recursive) {
const files = fs.readdirSync(path.join(root, directory))

files.forEach((filename) => {
// webpack adds a './' to the begining of the key
// TODO: Check this in windows
const entryKey = `./${path.join(directory, filename)}`
if (regExp.test(entryKey)) {
// eslint-disable-next-line no-param-reassign, global-require, import/no-dynamic-require
keys[entryKey] = require(path.join(root, directory, filename))
return
}

if (!recursive) {
return
}

if (fs.statSync(path.join(root, directory, filename)).isDirectory()) {
requireModules(keys, root, path.join(directory, filename), regExp, recursive)
}
})
}

function isRelativeRequest (request) {
if (request.charCodeAt(0) !== 46/* . */) {
return false
}

if (request === '.' || '..') {
return true
}

return request.charCodeAt(1) === 47/* / */ || (
request.charCodeAt(1) === 46/* . */ && request.charCodeAt(2) === 47/* / */)
}

export default function runWithRequireContext (content, options) {
const { filename, dirname } = options

const newRequire = (request) => {
if (isRelativeRequest(request)) {
// eslint-disable-next-line global-require, import/no-dynamic-require
return require(path.resolve(dirname, request))
}

// eslint-disable-next-line global-require, import/no-dynamic-require
return require(request)
}

newRequire.resolve = require.resolve
newRequire.extensions = require.extensions
newRequire.main = require.main
newRequire.cache = require.cache

newRequire.context = (directory, useSubdirectories = false, regExp = /^\.\//) => {
const fullPath = path.resolve(dirname, directory)
const keys = {}
requireModules(keys, fullPath, '.', regExp, useSubdirectories)

const req = f => (keys[f])
req.keys = () => (Object.keys(keys))
return req
}

const compiledModule = vm.runInThisContext(moduleSystem.wrap(content))
compiledModule(module.exports, newRequire, module, filename, dirname)
}
200 changes: 200 additions & 0 deletions stories/__test__/__snapshots__/storyshots.test.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
exports[`Storyshots Another Button with some emoji 1`] = `
<button
onClick={[Function]}
style={
Object {
"backgroundColor": "#FFFFFF",
"border": "1px solid #eee",
"borderRadius": 3,
"cursor": "pointer",
"fontSize": 15,
"margin": 10,
"padding": "3px 10px",
}
}>
😀 😎 👍 💯
</button>
`;

exports[`Storyshots Another Button with text 1`] = `
<button
onClick={[Function]}
style={
Object {
"backgroundColor": "#FFFFFF",
"border": "1px solid #eee",
"borderRadius": 3,
"cursor": "pointer",
"fontSize": 15,
"margin": 10,
"padding": "3px 10px",
}
}>
Hello Button
</button>
`;

exports[`Storyshots Button with some emoji 1`] = `
<button
onClick={[Function]}
style={
Object {
"backgroundColor": "#FFFFFF",
"border": "1px solid #eee",
"borderRadius": 3,
"cursor": "pointer",
"fontSize": 15,
"margin": 10,
"padding": "3px 10px",
}
}>
😀 😎 👍 💯
</button>
`;

exports[`Storyshots Button with text 1`] = `
<button
onClick={[Function]}
style={
Object {
"backgroundColor": "#FFFFFF",
"border": "1px solid #eee",
"borderRadius": 3,
"cursor": "pointer",
"fontSize": 15,
"margin": 10,
"padding": "3px 10px",
}
}>
Hello Button
</button>
`;

exports[`Storyshots Welcome to Storybook 1`] = `
<div
style={
Object {
"fontFamily": "\"Helvetica Neue\", Helvetica, \"Segoe UI\", Arial, freesans, sans-serif",
"lineHeight": 1.4,
"margin": 15,
"maxWidth": 600,
}
}>
<h1>
Welcome to STORYBOOK
</h1>
<p>
This is a UI component dev environment for your app.
</p>
<p>
We\'ve added some basic stories inside the
<code
style={
Object {
"backgroundColor": "#f3f2f2",
"border": "1px solid #eae9e9",
"borderRadius": 4,
"color": "#3a3a3a",
"fontSize": 15,
"fontWeight": 600,
"padding": "2px 5px",
}
}>
src/stories
</code>
directory.
<br />
A story is a single state of one or more UI components. You can have as many stories as you want.
<br />
(Basically a story is like a visual test case.)
</p>
<p>
See these sample
<a
href="#"
onClick={[Function]}
style={
Object {
"borderBottom": "1px solid #1474f3",
"color": "#1474f3",
"paddingBottom": 2,
"textDecoration": "none",
}
}>
stories
</a>
for a component called
<code
style={
Object {
"backgroundColor": "#f3f2f2",
"border": "1px solid #eae9e9",
"borderRadius": 4,
"color": "#3a3a3a",
"fontSize": 15,
"fontWeight": 600,
"padding": "2px 5px",
}
}>
Button
</code>
.
</p>
<p>
Just like that, you can add your own components as stories.
<br />
You can also edit those components and see changes right away.
<br />
(Try editing the
<code
style={
Object {
"backgroundColor": "#f3f2f2",
"border": "1px solid #eae9e9",
"borderRadius": 4,
"color": "#3a3a3a",
"fontSize": 15,
"fontWeight": 600,
"padding": "2px 5px",
}
}>
Button
</code>
component located at
<code
style={
Object {
"backgroundColor": "#f3f2f2",
"border": "1px solid #eae9e9",
"borderRadius": 4,
"color": "#3a3a3a",
"fontSize": 15,
"fontWeight": 600,
"padding": "2px 5px",
}
}>
src/stories/Button.js
</code>
.)
</p>
<p>
This is just one thing you can do with Storybook.
<br />
Have a look at the
<a
href="https://github.com/kadirahq/react-storybook"
style={
Object {
"borderBottom": "1px solid #1474f3",
"color": "#1474f3",
"paddingBottom": 2,
"textDecoration": "none",
}
}
target="_blank">
React Storybook
</a>
repo for more information.
</p>
</div>
`;
2 changes: 2 additions & 0 deletions stories/__test__/storyshots.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import initStoryshots from '../../src'
initStoryshots()
27 changes: 27 additions & 0 deletions stories/directly_required/Button.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React from 'react'

const buttonStyles = {
border: '1px solid #eee',
borderRadius: 3,
backgroundColor: '#FFFFFF',
cursor: 'pointer',
fontSize: 15,
padding: '3px 10px',
margin: 10
}

const Button = ({ children, onClick }) => (
<button
style={buttonStyles}
onClick={onClick}
>
{children}
</button>
)

Button.propTypes = {
children: React.PropTypes.string.isRequired,
onClick: React.PropTypes.func
}

export default Button
Loading

0 comments on commit 84dc260

Please sign in to comment.