├── .babelrc ├── .editorconfig ├── .eslintrc ├── .gitignore ├── .npmrc ├── .travis.yml ├── .yarnrc ├── CHANGELOG.md ├── LICENSE ├── README.md ├── demo.gif ├── example ├── .babelrc ├── .eslintrc ├── package.json ├── src │ └── components │ │ └── button │ │ ├── Button.js │ │ └── README.md ├── styleguide.config.dev.js ├── styleguide.config.js └── yarn.lock ├── logo.png ├── package.json ├── src ├── api.js ├── components │ ├── Code.js │ ├── Playground.js │ ├── Preview.js │ └── Test.js ├── core │ ├── configureServer.js │ ├── snapshot.js │ └── updateWebpackConfig.js ├── index.js └── styles.css ├── test ├── api.spec.js ├── components │ ├── Code.spec.js │ ├── Playground.spec.js │ ├── Preview.spec.js │ ├── Test.spec.js │ └── __snapshots__ │ │ ├── Code.spec.js.snap │ │ └── Test.spec.js.snap ├── core │ ├── __snapshots__ │ │ └── snapshot.spec.js.snap │ ├── configureServer.spec.js │ ├── snapshot.spec.js │ └── updateWebpackConfig.spec.js └── testSetup.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "react"] 3 | } 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain 2 | # consistent coding styles between different editors and IDEs. 3 | 4 | root = true 5 | 6 | [*] 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = false 11 | indent_style = space 12 | indent_size = 2 13 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | 4 | "env": { 5 | "browser": true, 6 | "jest": true 7 | }, 8 | 9 | "rules": { 10 | "semi": ["error", "never"], 11 | "comma-dangle": ["error", { 12 | "arrays": "always-multiline", 13 | "objects": "always-multiline", 14 | "imports": "always-multiline", 15 | "exports": "always-multiline", 16 | "functions": "never" 17 | }], 18 | "no-param-reassign": ["error", { "props": false }], 19 | "react/destructuring-assignment": ["error", "never"], 20 | "react/forbid-prop-types": ["error", { checkContextTypes: false }], 21 | "react/jsx-filename-extension": ["error", { "extensions": [".js"] }], 22 | "react/require-default-props": "off", 23 | "import/no-extraneous-dependencies": ["error", { "devDependencies": true }], 24 | "import/no-unresolved": "off", 25 | "no-plusplus": "off" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .opt-in 2 | .opt-out 3 | .snapguidist 4 | lib 5 | node_modules 6 | npm-debug.log 7 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | save-exact=true 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "6" 4 | -------------------------------------------------------------------------------- /.yarnrc: -------------------------------------------------------------------------------- 1 | save-prefix false 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) 5 | and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). 6 | 7 | ## [4.0.0] - 2018-08-17 8 | ### Changed 9 | - Change snapshot files extension to `.sg` (before: `.snap`) [#37](https://github.com/styleguidist/snapguidist/pull/37). 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Michele Bertoli 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/styleguidist/snapguidist.svg?branch=master)](https://travis-ci.org/styleguidist/snapguidist) 2 | [![NPM version](https://img.shields.io/npm/v/snapguidist.svg)](https://www.npmjs.com/package/snapguidist) 3 | [![tested with jest](https://img.shields.io/badge/tested_with-jest-99424f.svg)](https://github.com/facebook/jest) 4 | 5 | Preview 6 | 7 | [Jest](https://github.com/facebook/jest) Snapshot Testing for [React Styleguidist](https://github.com/styleguidist/react-styleguidist). 8 | 9 | ## Demo 10 | 11 | ![Demo](demo.gif) 12 | 13 | ## Getting Started 14 | 15 | To add `snapguidist` to your `react-styleguidist` configuration, follow these steps: 16 | 17 | 1. install the package using yarn or npm: 18 | 19 | ```bash 20 | yarn add --dev snapguidist 21 | ``` 22 | 23 | 2. enhance the webpack configuration in `styleguide.config.js`: 24 | 25 | ```diff 26 | +const snapguidist = require('snapguidist'); 27 | -module.exports = { 28 | +module.exports = snapguidist({ 29 | components: 'src/components/**/[A-Z]*.js', 30 | defaultExample: true, 31 | webpackConfig: { 32 | module: { 33 | rules: [ 34 | { 35 | test: /\.jsx?$/, 36 | exclude: /node_modules/, 37 | loader: 'babel-loader', 38 | }, 39 | { 40 | test: /\.css$/, 41 | loader: 'style-loader!css-loader', 42 | }, 43 | ], 44 | }, 45 | }, 46 | -}; 47 | +}); 48 | ``` 49 | 50 | ## Migrate to v4 51 | 52 | In v4, snapshots have been renamed to `.sg` (as opposed to `.snap`) to avoid conflicts with Jest, and improve compatibility with [create-react-app](https://github.com/facebook/create-react-app). 53 | 54 | Once upgraded to v4, please run `yarn test -u` to remove the old snapshots (new ones will be automatically generated on the next run) 55 | or the following commands to rename them: 56 | ```sh 57 | cd .snapguidist/__snapshots/ 58 | for old in *.snap; do git mv $old `basename $old .snap`.sg; done 59 | ``` 60 | 61 | ## Example 62 | 63 | To run the example, install the dependencies and start it: 64 | 65 | ```bash 66 | cd example 67 | yarn install 68 | yarn start 69 | ``` 70 | 71 | ## Development 72 | 73 | > Any contribution to `snapguidist` is highly appreciated. 74 | 75 | Run the following command from the root folder to enable the *hot-reload*: 76 | 77 | ```bash 78 | yarn build:watch & yarn start 79 | ``` 80 | 81 | ### Credits 82 | 83 | Logo by [@SaraVieira](https://github.com/SaraVieira) 84 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/styleguidist/snapguidist/7deea67fa9176501a8ca6cf6bc6dc9cab70afdae/demo.gif -------------------------------------------------------------------------------- /example/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "react"] 3 | } 4 | -------------------------------------------------------------------------------- /example/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-param-reassign": [2, { "props": false }], 4 | "comma-dangle": [2, { 5 | "arrays": "always-multiline", 6 | "objects": "always-multiline", 7 | "imports": "always-multiline", 8 | "exports": "always-multiline", 9 | "functions": "never" 10 | }], 11 | "import/no-dynamic-require": 0, 12 | "import/newline-after-import": 0 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "0.2.0", 4 | "license": "MIT", 5 | "scripts": { 6 | "start": "styleguidist server" 7 | }, 8 | "devDependencies": { 9 | "babel-core": "6.26.0", 10 | "babel-loader": "7.1.2", 11 | "prop-types": "15.6.0", 12 | "react": "16.2.0", 13 | "react-dom": "16.2.0", 14 | "react-styleguidist": "6.2.2", 15 | "snapguidist": "./..", 16 | "webpack": "3.10.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /example/src/components/button/Button.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | 4 | const Button = props => 5 | 6 | Button.propTypes = { 7 | text: PropTypes.string, 8 | } 9 | 10 | export default Button 11 | -------------------------------------------------------------------------------- /example/src/components/button/README.md: -------------------------------------------------------------------------------- 1 | React component example 1: 2 | 3 | 65 | 73 | 74 |
75 | {this.props.response.diff 76 | ? 77 | : ( 78 |
79 | 80 | 81 |
82 | ) 83 | } 84 |
85 | 86 | ) 87 | } 88 | 89 | ) 90 | } 91 | } 92 | 93 | Test.propTypes = { 94 | isFetching: PropTypes.bool, 95 | onClick: PropTypes.func, 96 | response: PropTypes.shape({ 97 | actual: PropTypes.string, 98 | count: PropTypes.number, 99 | diff: PropTypes.string, 100 | expected: PropTypes.string, 101 | pass: PropTypes.bool, 102 | }), 103 | } 104 | 105 | export default Test 106 | -------------------------------------------------------------------------------- /src/core/configureServer.js: -------------------------------------------------------------------------------- 1 | const bodyParser = require('body-parser') 2 | const cors = require('cors') 3 | const snapshot = require('./snapshot') 4 | 5 | const configureServer = (app) => { 6 | app.use(cors()) 7 | app.use(bodyParser.json()) 8 | 9 | app.post('/snapguidist', (req, res) => { 10 | const result = snapshot(req.body.name, req.body.tree) 11 | 12 | res.send(result) 13 | }) 14 | 15 | app.put('/snapguidist', (req, res) => { 16 | const update = true 17 | const result = snapshot(req.body.name, req.body.tree, update) 18 | 19 | res.send(result) 20 | }) 21 | } 22 | 23 | module.exports = configureServer 24 | -------------------------------------------------------------------------------- /src/core/snapshot.js: -------------------------------------------------------------------------------- 1 | const { SnapshotState } = require('jest-snapshot') 2 | const stripAnsi = require('strip-ansi') 3 | const diff = require('jest-diff') 4 | const path = require('path') 5 | 6 | const base = './.snapguidist/__snapshots__/' 7 | const typeOf = { value: Symbol.for('react.test.json') } 8 | 9 | // Clean up result for better looking on CodeMirror 10 | const cleanUp = (result) => { 11 | result.actual = result.actual.trim() 12 | result.expected = (result.expected || '').trim() 13 | result.diff = (result.diff || '').replace(/\n[ ]+\n/g, '\n\n').trim() 14 | } 15 | 16 | const setTreeTypes = (obj) => { 17 | if (!obj) return 18 | 19 | if (Array.isArray(obj)) { 20 | obj.forEach(setTreeTypes) 21 | } else if (obj.type) { 22 | setTreeTypes(obj.children) 23 | Object.defineProperty(obj, '$$typeof', typeOf) 24 | } 25 | } 26 | 27 | const snapshot = (name, tree, update) => { 28 | const snapshotPath = path.resolve(base, `${name}.sg`) 29 | const state = new SnapshotState(null, { 30 | snapshotPath, 31 | updateSnapshot: update ? 'all' : 'new', 32 | }) 33 | 34 | setTreeTypes(tree) 35 | 36 | const result = state.match({ 37 | testName: name, 38 | received: tree, 39 | }) 40 | 41 | state.save() 42 | 43 | if (!result.pass) { 44 | result.diff = stripAnsi(diff(result.expected, result.actual)) 45 | cleanUp(result) 46 | } 47 | 48 | return result 49 | } 50 | 51 | module.exports = snapshot 52 | -------------------------------------------------------------------------------- /src/core/updateWebpackConfig.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const webpack = require('webpack') 3 | 4 | const libFolder = path.join(__dirname, '..') 5 | const componentPath = component => path.join(libFolder, 'components', component) 6 | const stylesPath = style => path.join(libFolder, style) 7 | const styleLoaders = '!!style-loader!css-loader!' 8 | 9 | const PLAYGROUND_RENDERER = 'rsg-components/Playground/PlaygroundRenderer' 10 | const PLAYGROUND = 'rsg-components/Playground' 11 | const PREVIEW = 'rsg-components/Preview' 12 | 13 | const updateWebpackConfig = (webpackConfig, env, serverInfo) => { 14 | webpackConfig.entry.push( 15 | `${styleLoaders}codemirror/lib/codemirror.css`, 16 | `${styleLoaders}rsg-codemirror-theme.css`, 17 | `${styleLoaders}${stylesPath('styles.css')}` 18 | ) 19 | 20 | webpackConfig.resolve.alias = Object.assign({ 21 | [PLAYGROUND_RENDERER]: 'react-styleguidist/lib/rsg-components/Playground/PlaygroundRenderer', 22 | [PLAYGROUND]: componentPath('Playground'), 23 | [PREVIEW]: componentPath('Preview'), 24 | }, webpackConfig.resolve.alias) 25 | 26 | webpackConfig.plugins.push(new webpack.DefinePlugin({ 27 | 'process.env.SNAPGUIDIST': JSON.stringify(serverInfo), 28 | })) 29 | 30 | return webpackConfig 31 | } 32 | 33 | module.exports = updateWebpackConfig 34 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const updateWebpackConfig = require('./core/updateWebpackConfig') 2 | const configureServer = require('./core/configureServer') 3 | 4 | function snapguidist(config = {}) { 5 | const serverInfo = { 6 | host: config.serverHost || 'localhost', 7 | port: config.serverPort || 6060, 8 | } 9 | 10 | const { 11 | dangerouslyUpdateWebpackConfig: _updateWebpackConfig, 12 | configureServer: _configureServer, 13 | } = config 14 | 15 | return Object.assign(config, 16 | { 17 | 18 | dangerouslyUpdateWebpackConfig(webpackConfig, env) { 19 | let final = updateWebpackConfig(webpackConfig, env, serverInfo) 20 | if (_updateWebpackConfig) { 21 | final = _updateWebpackConfig(final, env) 22 | } 23 | return final 24 | }, 25 | 26 | configureServer(app, env) { 27 | configureServer(app, env) 28 | if (_configureServer) { 29 | _configureServer(app, env) 30 | } 31 | }, 32 | 33 | }) 34 | } 35 | 36 | module.exports = snapguidist 37 | -------------------------------------------------------------------------------- /src/styles.css: -------------------------------------------------------------------------------- 1 | .ReactStyleguidist-Playground__preview { 2 | margin-bottom: 0; 3 | } 4 | 5 | .snapguidist__test { 6 | background: linear-gradient(to right, #888888, #c3c3c3); 7 | box-sizing: border-box; 8 | height: 0px; 9 | margin: 15px -15px -15px; 10 | overflow: hidden; 11 | padding: 2px 4px 4px; 12 | transition: height 0.1s ease-out; 13 | } 14 | 15 | .snapguidist__test--pass { 16 | background: linear-gradient(to right, #009f00, #00e300); 17 | } 18 | 19 | .snapguidist__test--fail { 20 | background: linear-gradient(to right, #ff3d3d, #ffaeae); 21 | height: 25px; 22 | } 23 | 24 | .snapguidist__test--expanded { 25 | height: auto; 26 | } 27 | 28 | .snapguidist__button { 29 | background-color: #333; 30 | border: 0; 31 | color: #fff; 32 | cursor: pointer; 33 | font-size: 11px; 34 | margin-right: 4px; 35 | outline: none; 36 | text-transform: uppercase; 37 | transition: opacity 0.1s; 38 | } 39 | 40 | .snapguidist__button:disabled { 41 | opacity: 0.5; 42 | } 43 | 44 | .snapguidist__arrow { 45 | display: inline-block; 46 | transition: transform 0.1s; 47 | } 48 | 49 | .snapguidist__arrow--expanded { 50 | transform: rotate(-180deg); 51 | } 52 | 53 | .snapguidist__label { 54 | color: #333; 55 | display: inline-block; 56 | font-family: sans-serif; 57 | font-size: 11px; 58 | margin: 4px 0 0 2px; 59 | text-transform: uppercase; 60 | } 61 | 62 | .snapguidist__code .CodeMirror { 63 | border-radius: 3px; 64 | border: 1px solid #ff3d3d; 65 | font-size: 12px; 66 | height: auto; 67 | padding: 4px 12px; 68 | } 69 | -------------------------------------------------------------------------------- /test/api.spec.js: -------------------------------------------------------------------------------- 1 | import fetch from 'unfetch' 2 | import api from '../src/api' 3 | 4 | jest.mock( 5 | 'react-test-renderer', 6 | () => ({ create: example => ({ toJSON: () => example }) }) 7 | ) 8 | 9 | jest.mock( 10 | 'unfetch', 11 | () => jest.fn(() => Promise.resolve(({ json: () => {} }))) 12 | ) 13 | 14 | process.env.SNAPGUIDIST = {} 15 | 16 | const name = 'name' 17 | const example = 'example' 18 | 19 | test('fires a POST when update is false', () => { 20 | const update = false 21 | api.runTest(name, example, update) 22 | 23 | expect(fetch).toHaveBeenCalledWith( 24 | 'http://localhost:3000/snapguidist', 25 | { 26 | body: '{"name":"name","tree":"example"}', 27 | headers: { 28 | 'Content-Type': 'application/json', 29 | }, 30 | method: 'POST', 31 | } 32 | ) 33 | }) 34 | 35 | test('fires a PUT when update is true', () => { 36 | const update = true 37 | api.runTest(name, example, update) 38 | 39 | expect(fetch).toHaveBeenCalledWith( 40 | 'http://localhost:3000/snapguidist', 41 | { 42 | body: '{"name":"name","tree":"example"}', 43 | headers: { 44 | 'Content-Type': 'application/json', 45 | }, 46 | method: 'PUT', 47 | } 48 | ) 49 | }) 50 | -------------------------------------------------------------------------------- /test/components/Code.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { shallow } from 'enzyme' 3 | import toJSON from 'enzyme-to-json' 4 | import Code from '../../src/components/Code' 5 | 6 | const props = { 7 | diff: false, 8 | label: 'label', 9 | value: 'value', 10 | } 11 | const context = { config: { highlightTheme: 'theme' } } 12 | 13 | test('works with jsx', () => { 14 | const wrapper = shallow(, { context }) 15 | 16 | expect(toJSON(wrapper)).toMatchSnapshot() 17 | }) 18 | 19 | test('works with diff', () => { 20 | const wrapper = shallow(, { context }) 21 | 22 | expect(toJSON(wrapper)).toMatchSnapshot() 23 | }) 24 | -------------------------------------------------------------------------------- /test/components/Playground.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { shallow } from 'enzyme' 3 | import SnapguidistPlayground from '../../src/components/Playground' 4 | 5 | jest.mock( 6 | 'react-styleguidist/lib/rsg-components/Playground/Playground', 7 | () => { 8 | const Playground = () => null 9 | return Playground 10 | } 11 | ) 12 | 13 | const props = { 14 | code: 'code', 15 | evalInContext: () => {}, 16 | index: 1, 17 | name: 'name', 18 | } 19 | 20 | test('passes the props to Playground', () => { 21 | const wrapper = shallow() 22 | 23 | expect(wrapper.find('Playground').props()).toEqual(props) 24 | }) 25 | 26 | test('generates the context', () => { 27 | const wrapper = shallow() 28 | const expected = { name: `${props.name}-${props.index}` } 29 | 30 | expect(wrapper.instance().getChildContext()).toEqual(expected) 31 | }) 32 | -------------------------------------------------------------------------------- /test/components/Preview.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { shallow, mount } from 'enzyme' 3 | import SnapguidistPreview from '../../src/components/Preview' 4 | import api from '../../src/api' 5 | 6 | window.requestAnimationFrame = fn => fn() 7 | 8 | jest.mock( 9 | 'react-styleguidist/lib/rsg-components/Preview/Preview', 10 | () => { 11 | const Preview = () => null 12 | return Preview 13 | } 14 | ) 15 | 16 | jest.mock( 17 | '../../src/api', 18 | () => ({ runTest: jest.fn(() => ({ then: callback => callback({ pass: true }) })) }) 19 | ) 20 | 21 | const props = { code: 'code', evalInContext: () => {} } 22 | const options = { context: { name: 'name' } } 23 | 24 | beforeEach(() => api.runTest.mockClear()) 25 | 26 | test('passes the code to Preview', () => { 27 | const wrapper = shallow(, options) 28 | 29 | expect(wrapper.find('Preview').prop('code')).toEqual(props.code) 30 | }) 31 | 32 | test('wraps evalInContext and stores the example', () => { 33 | const example = 'example' 34 | const evalInContext = () => () => example 35 | const wrapper = shallow(, options) 36 | const exampleComponent = wrapper.instance().evalInContext() 37 | exampleComponent() 38 | 39 | expect(wrapper.instance().example).toBe(example) 40 | }) 41 | 42 | test('passes isFetching to Test', () => { 43 | const wrapper = shallow(, options) 44 | const isFetching = true 45 | wrapper.setState({ isFetching }) 46 | 47 | expect(wrapper.find('Test').prop('isFetching')).toEqual(isFetching) 48 | }) 49 | 50 | test('fires the api call with the update flag', () => { 51 | const wrapper = shallow(, options) 52 | wrapper.find('Test').simulate('click') 53 | 54 | expect(api.runTest).toHaveBeenCalledWith(options.context.name, undefined, true) 55 | }) 56 | 57 | test('fires the api call on didMount', () => { 58 | mount(, options) 59 | 60 | expect(api.runTest).toHaveBeenCalledWith(options.context.name, undefined, undefined) 61 | }) 62 | 63 | test('fires the api call on didUpdate, when code changes', () => { 64 | const wrapper = mount(, options) 65 | api.runTest.mockClear() 66 | wrapper.setProps({ code: 'c0d3' }) 67 | 68 | expect(api.runTest).toHaveBeenCalledWith(options.context.name, undefined, undefined) 69 | }) 70 | 71 | test('does not fire the api call on didUpdate, when code is the same', () => { 72 | const wrapper = mount(, options) 73 | api.runTest.mockClear() 74 | wrapper.setProps(props) 75 | 76 | expect(api.runTest).not.toHaveBeenCalledWith() 77 | }) 78 | 79 | test('passes the response to Test', () => { 80 | const wrapper = mount(, options) 81 | 82 | expect(wrapper.find('Test').prop('response')).toEqual({ pass: true }) 83 | }) 84 | -------------------------------------------------------------------------------- /test/components/Test.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import renderer from 'react-test-renderer' 3 | import { shallow } from 'enzyme' 4 | import toJSON from 'enzyme-to-json' 5 | import Test from '../../src/components/Test' 6 | 7 | const props = { 8 | isFetching: false, 9 | onClick: () => {}, 10 | response: { 11 | actual: 'actual', 12 | count: 0, 13 | expected: 'expected', 14 | pass: false, 15 | }, 16 | } 17 | 18 | test('works when pass', () => { 19 | const component = renderer.create() 20 | const tree = component.toJSON() 21 | 22 | expect(tree).toMatchSnapshot() 23 | }) 24 | 25 | test('works when fail, with diff', () => { 26 | const wrapper = shallow() 27 | 28 | expect(toJSON(wrapper)).toMatchSnapshot() 29 | }) 30 | 31 | test('works when fail, without diff)', () => { 32 | const wrapper = shallow() 33 | 34 | expect(toJSON(wrapper)).toMatchSnapshot() 35 | }) 36 | 37 | test('works when is fetching', () => { 38 | const wrapper = shallow() 39 | 40 | expect(toJSON(wrapper)).toMatchSnapshot() 41 | }) 42 | 43 | test('works on update', () => { 44 | const onClick = jest.fn() 45 | const wrapper = shallow() 46 | wrapper.find('button').at(0).simulate('click') 47 | 48 | expect(onClick).toHaveBeenCalled() 49 | expect(toJSON(wrapper)).toMatchSnapshot() 50 | }) 51 | 52 | test('works on toggle', () => { 53 | const wrapper = shallow() 54 | wrapper.find('button').at(1).simulate('click') 55 | 56 | expect(toJSON(wrapper)).toMatchSnapshot() 57 | }) 58 | -------------------------------------------------------------------------------- /test/components/__snapshots__/Code.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`works with diff 1`] = ` 4 |
5 |
8 | label 9 |
10 |
13 | 22 |
23 |
24 | `; 25 | 26 | exports[`works with jsx 1`] = ` 27 |
28 |
31 | label 32 |
33 |
36 | 45 |
46 |
47 | `; 48 | -------------------------------------------------------------------------------- /test/components/__snapshots__/Test.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`works on toggle 1`] = ` 4 |
7 |
8 |
9 | 17 | 29 |
30 |
31 |
32 | 36 | 40 |
41 |
42 |
43 |
44 | `; 45 | 46 | exports[`works on update 1`] = ` 47 |
50 |
51 |
52 | 60 | 72 |
73 |
74 |
75 | 79 | 83 |
84 |
85 |
86 |
87 | `; 88 | 89 | exports[`works when fail, with diff 1`] = ` 90 |
93 |
94 |
95 | 103 | 115 |
116 |
117 |
118 | 122 | 126 |
127 |
128 |
129 |
130 | `; 131 | 132 | exports[`works when fail, without diff) 1`] = ` 133 |
136 |
137 |
138 | 146 | 158 |
159 |
160 | 165 |
166 |
167 |
168 | `; 169 | 170 | exports[`works when is fetching 1`] = ` 171 |
174 |
175 |
176 | 184 | 196 |
197 |
198 |
199 | 203 | 207 |
208 |
209 |
210 |
211 | `; 212 | 213 | exports[`works when pass 1`] = ` 214 |
217 | `; 218 | -------------------------------------------------------------------------------- /test/core/__snapshots__/snapshot.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`fails if new is null but old is not 1`] = ` 4 | Object { 5 | "actual": "null", 6 | "count": 1, 7 | "diff": "- Expected 8 | + Received 9 | 10 | -
11 | + null", 12 | "expected": "
", 13 | "key": "new-is-null 1", 14 | "pass": false, 15 | } 16 | `; 17 | 18 | exports[`fails if old was null but new is not 1`] = ` 19 | Object { 20 | "actual": "
", 21 | "count": 1, 22 | "diff": "- Expected 23 | + Received 24 | 25 | - null 26 | +
", 27 | "expected": "null", 28 | "key": "old-was-null 1", 29 | "pass": false, 30 | } 31 | `; 32 | 33 | exports[`fails if the type changes 1`] = ` 34 | Object { 35 | "actual": " 36 | 37 | ", 38 | "count": 1, 39 | "diff": "- Expected 40 | + Received 41 | 42 | 43 | -
44 | + 45 | 46 | -
47 | +
", 48 | "expected": "
49 | 50 |
", 51 | "key": "name 1", 52 | "pass": false, 53 | } 54 | `; 55 | 56 | exports[`passes if new and old are null 1`] = ` 57 | Object { 58 | "actual": "", 59 | "count": 1, 60 | "expected": "", 61 | "key": "both-null 1", 62 | "pass": true, 63 | } 64 | `; 65 | -------------------------------------------------------------------------------- /test/core/configureServer.spec.js: -------------------------------------------------------------------------------- 1 | import express from 'express' 2 | import request from 'supertest' 3 | import snapshot from '../../src/core/snapshot' 4 | import configureServer from '../../src/core/configureServer' 5 | 6 | jest.mock('../../src/core/snapshot') 7 | 8 | const app = express() 9 | configureServer(app) 10 | 11 | test('calls snapshot with name and tree', () => { 12 | const name = 'name' 13 | const tree = { type: 'div' } 14 | 15 | return request(app) 16 | .post('/snapguidist') 17 | .send({ name, tree }) 18 | .then(() => expect(snapshot).toBeCalledWith(name, tree)) 19 | }) 20 | 21 | test('calls snapshot with name, tree and update', () => { 22 | const name = 'name' 23 | const tree = { type: 'div' } 24 | const update = true 25 | 26 | return request(app) 27 | .put('/snapguidist') 28 | .send({ name, tree }) 29 | .then(() => expect(snapshot).toBeCalledWith(name, tree, update)) 30 | }) 31 | -------------------------------------------------------------------------------- /test/core/snapshot.spec.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs-extra' 2 | import snapshot from '../../src/core/snapshot' 3 | 4 | beforeEach(() => { 5 | fs.emptyDirSync('./.snapguidist/__snapshots__') 6 | }) 7 | 8 | afterAll(() => { 9 | fs.removeSync('./.snapguidist') 10 | }) 11 | 12 | test('passes if new', () => { 13 | const tree = { type: 'div' } 14 | const result = snapshot('name', tree) 15 | 16 | expect(result).toEqual(expect.objectContaining({ pass: true })) 17 | }) 18 | 19 | test('fails if the type changes', () => { 20 | const tree = { type: 'div', children: [{ type: 'span' }] } 21 | snapshot('name', tree) 22 | 23 | tree.type = 'span' 24 | const result = snapshot('name', tree) 25 | 26 | expect(result).toMatchSnapshot() 27 | }) 28 | 29 | test('does not fail if update is true', () => { 30 | const tree = { type: 'div' } 31 | snapshot('name', tree) 32 | 33 | tree.type = 'span' 34 | const update = true 35 | const result = snapshot('name', tree, update) 36 | 37 | expect(result).toEqual(expect.objectContaining({ pass: true })) 38 | }) 39 | 40 | test('passes if null, first time', () => { 41 | const tree = null 42 | const result = snapshot('name', tree) 43 | 44 | expect(result).toEqual(expect.objectContaining({ pass: true })) 45 | }) 46 | 47 | test('passes if new and old are null', () => { 48 | const name = 'both-null' 49 | snapshot(name, null) 50 | const result = snapshot(name, null) 51 | 52 | expect(result).toMatchSnapshot() 53 | }) 54 | 55 | test('fails if old was null but new is not', () => { 56 | const name = 'old-was-null' 57 | snapshot(name, null) 58 | 59 | const tree = { type: 'div' } 60 | const result = snapshot(name, tree) 61 | 62 | expect(result).toMatchSnapshot() 63 | }) 64 | 65 | test('fails if new is null but old is not', () => { 66 | const name = 'new-is-null' 67 | const tree = { type: 'div' } 68 | snapshot(name, tree) 69 | 70 | const result = snapshot(name, null) 71 | 72 | expect(result).toMatchSnapshot() 73 | }) 74 | 75 | test('null tree does not fail if update is true', () => { 76 | const name = 'null-with-update' 77 | const tree = { type: 'div' } 78 | snapshot(name, tree) 79 | 80 | const update = true 81 | const result = snapshot(name, null, update) 82 | 83 | expect(result).toEqual(expect.objectContaining({ pass: true })) 84 | }) 85 | -------------------------------------------------------------------------------- /test/core/updateWebpackConfig.spec.js: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import updateWebpackConfig from '../../src/core/updateWebpackConfig' 3 | 4 | jest.mock( 5 | 'webpack', 6 | () => ({ 7 | DefinePlugin(data) { return { definitions: data } }, 8 | }) 9 | ) 10 | 11 | const libFolder = path.join(__dirname, '..', '..') 12 | const styleLoaders = '!!style-loader!css-loader!' 13 | 14 | test('enhances the webpack configuration', () => { 15 | const webpackConfig = { 16 | entry: [], 17 | plugins: [], 18 | resolve: { alias: {} }, 19 | } 20 | const serverInfo = 'serverInfo' 21 | const expected = { 22 | entry: [ 23 | `${styleLoaders}codemirror/lib/codemirror.css`, 24 | `${styleLoaders}rsg-codemirror-theme.css`, 25 | `${styleLoaders}${libFolder}/src/styles.css`, 26 | ], 27 | 28 | plugins: [{ 29 | definitions: { 30 | 'process.env.SNAPGUIDIST': '"serverInfo"', 31 | }, 32 | }], 33 | 34 | resolve: { 35 | alias: { 36 | 'rsg-components/Playground/PlaygroundRenderer': 'react-styleguidist/lib/rsg-components/Playground/PlaygroundRenderer', 37 | 'rsg-components/Playground': `${libFolder}/src/components/Playground`, 38 | 'rsg-components/Preview': `${libFolder}/src/components/Preview`, 39 | }, 40 | }, 41 | } 42 | 43 | expect(updateWebpackConfig(webpackConfig, null, serverInfo)).toEqual(expected) 44 | }) 45 | -------------------------------------------------------------------------------- /test/testSetup.js: -------------------------------------------------------------------------------- 1 | import { configure } from 'enzyme' 2 | import Adapter from 'enzyme-adapter-react-16' 3 | 4 | configure({ adapter: new Adapter() }) 5 | --------------------------------------------------------------------------------