Skip to content

Commit

Permalink
Rip out sinon & mocha into separate modules
Browse files Browse the repository at this point in the history
This refactor addresses several issues regarding enzyme working in a variety of environments, and
the library just generally not making any assumptions about the environment that tests will be
run in.

For the most part, this amounts to:

- remove direct dependency on jsdom
- remove direct dependency on sinon
- remove assumed dependency on mocha

Additionally, this change moves the code that was removed into separate modules that are
defined in the packages folder. In this case we have added two modules: "enzyme-mocha" and
"enzyme-sinon".

This design will allow us to have one repo where all issues, code, and documentation
regarding enzyme are centralized, but modules are still separated at the NPM level. This
will allow us to make atomic releases across the modules more easily, as well as keep
all of the discussion in one place.

Left to do before this is mergable:

 [ ] Add a "guides" section in the docs explaining how to use enzyme in different environments
 [ ] Build a proper npm script to run an npm publish in the proper order for each module
 [ ] Add more meaningful tests for packages

This PR addresses several issues that havee been brought up:

- Fixes #55
- Fixes #47
- Fixes #44
  • Loading branch information
lelandrichardson committed Dec 8, 2015
1 parent 83e3b35 commit e305009
Show file tree
Hide file tree
Showing 23 changed files with 211 additions and 135 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -30,4 +30,5 @@ node_modules
.idea

/build
packages/*/build
_book
2 changes: 1 addition & 1 deletion .travis.yml
Expand Up @@ -12,7 +12,7 @@ node_js:
before_install:
- 'if [ "${TRAVIS_NODE_VERSION}" != "0.9" ]; then case "$(npm --version)" in 1.*) npm install -g npm@1.4.28 ;; 2.*) npm install -g npm@2 ;; esac ; fi'
- 'if [ "${TRAVIS_NODE_VERSION}" != "0.6" ] && [ "${TRAVIS_NODE_VERSION}" != "0.9" ]; then npm install -g npm; fi'
before_script: "sh install-relevant-react.sh"
before_script: "sh scripts/install-relevant-react.sh"
script:
- 'if [ "${TRAVIS_NODE_VERSION}" = "4.2" ]; then npm run lint && npm run travis ; elif [ "${TRAVIS_NODE_VERSION}" = "0.12" ]; then npm run travis ; else npm test ; fi'
after_script:
Expand Down
12 changes: 7 additions & 5 deletions package.json
Expand Up @@ -9,11 +9,13 @@
"postversion": "git push && git push --tags && npm run clean && npm run docs:publish",
"version": "npm run build",
"clean": "rimraf build",
"lint": "eslint src/**",
"test": "mocha --compilers js:babel/register --recursive src/**/__tests__/*.js",
"lint": "eslint src/** packages/**/src/**",
"check": "npm run lint && npm run test:all",
"build": "babel src --out-dir build",
"test:watch": "mocha --compilers js:babel/register --recursive src/**/__tests__/*.js --watch",
"build": "npm run build:core && npm run build:packages",
"build:core": "babel src --out-dir build",
"build:packages": "bash ./scripts/build-packages.sh",
"test": "mocha --compilers js:babel/register test/*.js packages/**/test/*.js",
"test:watch": "mocha --compilers js:babel/register test/*.js packages/**/test/*.js --watch",
"test:all": "npm run react:13 && npm test && npm run react:14 && npm test",
"react:clean": "rimraf node_modules/react node_modules/react-dom node_modules/react-addons-test-utils",
"react:13": "npm run react:clean && npm i react@0.13",
Expand All @@ -23,7 +25,7 @@
"docs:build": "npm run docs:prepare && gitbook build",
"docs:watch": "npm run docs:prepare && gitbook serve",
"docs:publish": "npm run docs:clean && npm run docs:build && cd _book && git init && git commit --allow-empty -m 'update book' && git fetch https://github.com/airbnb/enzyme.git gh-pages && git checkout -b gh-pages && git add . && git commit -am 'update book' && git push https://github.com/airbnb/enzyme.git gh-pages --force",
"travis": "istanbul cover _mocha -- --compilers js:babel/register --recursive src/**/__tests__/*.js"
"travis": "istanbul cover _mocha -- --compilers js:babel/register test/*.js packages/**/test/*.js"
},
"repository": {
"type": "git",
Expand Down
1 change: 1 addition & 0 deletions packages/enzyme-mocha/.npmignore
@@ -0,0 +1 @@
# Left empty in order to include the build directory for npm publish
19 changes: 19 additions & 0 deletions packages/enzyme-mocha/package.json
@@ -0,0 +1,19 @@
{
"name": "enzyme-mocha",
"version": "1.1.0",
"description": "Make Mocha and Enzyme play nice",
"main": "build/index.js",
"repository": "https://github.com/airbnb/enzyme/tree/master/packages/enzyme-mocha",
"keywords": [
"mocha",
"enzyme"
],
"author": "Leland Richardson <leland.richardson@airbnb.com>",
"license": "MIT",
"dependencies": {
"jsdomify": "^1.0.2"
},
"peerDependencies": {
"react": "0.13.x || 0.14.x"
}
}
33 changes: 33 additions & 0 deletions packages/enzyme-mocha/src/index.js
@@ -0,0 +1,33 @@
let jsdomify;

try {
jsdomify = require('jsdomify');
jsdomify.create();
require('react'); // require react explicitly after jsdomify.create() has been called
jsdomify.destroy();
} catch (e) {
// jsdom is not supported
}

export function jsdomSetup() {
if (!jsdomify) return;
jsdomify.create();
}

export function jsdomTeardown() {
if (!jsdomify) return;
jsdomify.destroy();
}

export function describeWithDOM(a, b) {
describe('(uses jsdom)', () => {
if (typeof jsdom === 'function') {
beforeEach(jsdomSetup);
afterEach(jsdomTeardown);
describe(a, b);
} else {
// if jsdom isn't available, skip every test in this describe context
describe.skip(a, b);
}
});
}
3 changes: 3 additions & 0 deletions packages/enzyme-mocha/test/jsdom-spec.js
@@ -0,0 +1,3 @@
describe('jsdom', () => {
it.skip('TODO: set up tests');
});
1 change: 1 addition & 0 deletions packages/enzyme-sinon/.npmignore
@@ -0,0 +1 @@
# Left empty in order to include the build directory for npm publish
16 changes: 16 additions & 0 deletions packages/enzyme-sinon/package.json
@@ -0,0 +1,16 @@
{
"name": "enzyme-sinon",
"version": "1.1.0",
"description": "Make Sinon and Enzyme play nice",
"main": "build/index.js",
"repository": "https://github.com/airbnb/enzyme/tree/master/packages/enzyme-mocha",
"keywords": [
"sinon",
"enzyme"
],
"dependencies": {
"sinon": "^1.17.2"
},
"author": "Leland Richardson <leland.richardson@airbnb.com>",
"license": "MIT"
}
20 changes: 20 additions & 0 deletions packages/enzyme-sinon/src/index.js
@@ -0,0 +1,20 @@
import Sinon from 'sinon';
import onPrototype from './onPrototype';

export let sinon = Sinon.sandbox.create();

export function spySetup() {
sinon = Sinon.sandbox.create();
}

export function spyTearDown() {
sinon.restore();
}

export function spyLifecycle(Component, sinonInstance = sinon) {
onPrototype(Component, (proto, name) => sinonInstance.spy(proto, name));
}

export function spyMethods(Component, sinonInstance = sinon) {
onPrototype(Component, null, (proto, name) => sinonInstance.spy(proto, name));
}
25 changes: 25 additions & 0 deletions packages/enzyme-sinon/src/onPrototype.js
@@ -0,0 +1,25 @@
export default function onPrototype(Component, lifecycle, method) {
const proto = Component.prototype;
Object.getOwnPropertyNames(proto).forEach((name) => {
if (typeof proto[name] !== 'function') return;
switch (name) {
case 'componentDidMount':
case 'componentWillMount':
case 'componentDidUnmount':
case 'componentWillUnmount':
case 'componentWillReceiveProps':
case 'componentDidUpdate':
case 'componentWillUpdate':
case 'shouldComponentUpdate':
case 'render':
if (lifecycle) lifecycle(proto, name);
break;
case 'constructor':
// don't spy on the constructor, even though it shows up in the prototype
break;
default:
if (method) method(proto, name);
break;
}
});
}
31 changes: 31 additions & 0 deletions packages/enzyme-sinon/test/onPrototype-spec.js
@@ -0,0 +1,31 @@
import { expect } from 'chai';
import sinon from 'sinon';
import onPrototype from '../src/onPrototype';

describe('onPrototype', () => {

it('makes the expected calls', () => {

class Foo {
a() {}
b() {}
componentDidUpdate() {}
}

const lifecycleSpy = sinon.spy();
const methodSpy = sinon.spy();

onPrototype(Foo, lifecycleSpy, methodSpy);

expect(lifecycleSpy.callCount).to.equal(1);
expect(lifecycleSpy.args[0][0]).to.equal(Foo.prototype);
expect(lifecycleSpy.args[0][1]).to.equal('componentDidUpdate');

expect(methodSpy.callCount).to.equal(2);
expect(methodSpy.args[0][0]).to.equal(Foo.prototype);
expect(methodSpy.args[0][1]).to.equal('a');
expect(methodSpy.args[1][1]).to.equal('b');

});

});
10 changes: 10 additions & 0 deletions scripts/build-packages.sh
@@ -0,0 +1,10 @@
#!/usr/bin/env zsh
# this cookie was getting too long for a JSON prop
# builds the Phoenix yavascripst from src to lib

# die on error
setopt -e

for pkg in packages/*/ ; do
babel ${pkg}/src --out-dir ${pkg}/build
done
File renamed without changes.
26 changes: 0 additions & 26 deletions src/Utils.js
Expand Up @@ -13,32 +13,6 @@ export function propsOfNode(node) {
return (node && node.props) || {};
}

export function onPrototype(Component, lifecycle, method) {
const proto = Component.prototype;
Object.getOwnPropertyNames(proto).forEach((name) => {
if (typeof proto[name] !== 'function') return;
switch (name) {
case 'componentDidMount':
case 'componentWillMount':
case 'componentDidUnmount':
case 'componentWillUnmount':
case 'componentWillReceiveProps':
case 'componentDidUpdate':
case 'componentWillUpdate':
case 'shouldComponentUpdate':
case 'render':
if (lifecycle) lifecycle(proto, name);
break;
case 'constructor':
// don't spy on the constructor, even though it shows up in the prototype
break;
default:
if (method) method(proto, name);
break;
}
});
}

export function getNode(node) {
return isDOMComponent(node) ? findDOMNode(node) : node;
}
Expand Down
71 changes: 0 additions & 71 deletions src/__tests__/_helpers.js

This file was deleted.

File renamed without changes.
2 changes: 1 addition & 1 deletion src/__tests__/Debug-spec.js → test/Debug-spec.js
Expand Up @@ -4,7 +4,7 @@ import {
spaces,
indent,
debugNode,
} from '../Debug';
} from '../src/Debug';

describe('debug', () => {

Expand Down
Expand Up @@ -4,9 +4,10 @@ import {
mount,
render,
ReactWrapper,
} from '../';
import { describeIf, describeWithDOM, sinon } from './_helpers';
import { REACT013 } from '../version';
} from '../src/';
import sinon from 'sinon';
import { describeIf, describeWithDOM } from './_helpers';
import { REACT013 } from '../src/version';

describeWithDOM('mount', () => {

Expand Down
Expand Up @@ -3,12 +3,12 @@ import sinon from 'sinon';
import { expect } from 'chai';
import {
splitSelector,
} from '../Utils';
} from '../src/Utils';
import {
hasClassName,
treeForEach,
treeFilter,
} from '../ShallowTraversal';
} from '../src/ShallowTraversal';

describe('ShallowTraversal', () => {

Expand Down
@@ -1,9 +1,9 @@
import React from 'react';
import { expect } from 'chai';
import { shallow, render, ShallowWrapper } from '../';
import { shallow, render, ShallowWrapper } from '../src/';
import sinon from 'sinon';
import { describeIf } from './_helpers';
import { REACT013 } from '../version';
import { REACT013 } from '../src/version';

describe('shallow', () => {

Expand Down

0 comments on commit e305009

Please sign in to comment.