diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..0a5c2f3 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +dist +tests/coverage diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..329eec0 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,21 @@ +{ + "ecmaFeatures": { + "jsx": true, + "modules": true + }, + "env": { + "browser": true, + "node": true + }, + "parser": "babel-eslint", + "rules": { + "quotes": [2, "single"], + "strict": [2, "never"], + "react/jsx-uses-react": 2, + "react/jsx-uses-vars": 2, + "react/react-in-jsx-scope": 2 + }, + "plugins": [ + "react" + ] +} diff --git a/.jscsrc b/.jscsrc deleted file mode 100644 index aad5667..0000000 --- a/.jscsrc +++ /dev/null @@ -1,12 +0,0 @@ -{ - "excludeFiles": [ - ".git/**", - "node_modules/**", - "tests/coverage/**", - "dist/**" - ], - "fileExtensions": [".js"], - "preset": "google", - - "disallowMultipleVarDecl": null -} diff --git a/README.md b/README.md index 458b1c3..c422b0b 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,10 @@ A few examples where this can be useful: that exact state later on when debugging - "Pausing" the app state and resuming it later (nice for games) +React compatibility: +- `react-component-tree@0.2` with `react@0.13` and below +- `react-component-tree@0.3` with `react@0.14` and above + ## ComponentTree.serialize Generate a snapshot with the props and state of a component combined, including diff --git a/karma.conf.js b/karma.conf.js index 28648ff..3f2be5d 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -7,7 +7,9 @@ module.exports = function(config) { dir: 'coverage/' }, files: [ - '**/*.js' + 'bind-polyfill.js', + 'unit/*.js', + 'integration/*.js' ], frameworks: ['mocha', 'chai', 'sinon-chai'], preprocessors: { diff --git a/package.json b/package.json index d2b1e4d..e07eeb7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-component-tree", - "version": "0.2.4", + "version": "0.3.0", "description": "Serialize and reproduce the state of an entire tree of React components", "main": "dist/entry.js", "repository": { @@ -14,15 +14,17 @@ "devDependencies": { "babel": "^5.8.23", "babel-core": "^5.0.12", + "babel-eslint": "^4.1.5", "babel-loader": "^5.0.0", "chai": "^2.2.0", "coveralls": "^2.11.2", + "eslint": "^1.9.0", + "eslint-plugin-react": "^3.8.0", "istanbul": "^0.3.13", "istanbul-instrumenter-loader": "^0.1.2", - "jscs": "^1.12.0", "karma": "^0.13.9", "karma-chai": "^0.1.0", - "karma-cli": "0.0.4", + "karma-cli": "0.1.1", "karma-coverage": "^0.2.7", "karma-mocha": "^0.1.10", "karma-mocha-reporter": "^1.0.2", @@ -30,16 +32,20 @@ "karma-sinon-chai": "^0.3.0", "karma-webpack": "^1.7.0", "mocha": "^2.2.4", - "react": "^0.13.1", + "react": "^0.14.0", + "react-addons-test-utils": "^0.14.0", + "react-dom": "^0.14.0", "sinon": "^1.14.1", "sinon-chai": "^2.7.0", "webpack": "^1.12.0" }, "peerDependencies": { - "react": "^0.13.1" + "react": "^0.14.0", + "react-dom": "^0.14.0" }, "scripts": { - "pretest": "jscs --esnext ./", + "pretest": "npm run lint", + "lint": "eslint .", "test": "karma start --single-run", "coveralls": "cat tests/coverage/*/lcov.info | node_modules/coveralls/bin/coveralls.js", "compile": "babel -d dist/ src/", diff --git a/src/render.js b/src/render.js index 2d1c0c8..4da0eba 100644 --- a/src/render.js +++ b/src/render.js @@ -1,5 +1,6 @@ var _ = require('lodash'), - React = require('react'); + React = require('react'), + ReactDOM = require('react-dom'); exports.render = function(options) { /** @@ -18,7 +19,7 @@ exports.render = function(options) { children = options.snapshot.children; var element = React.createElement(options.component, props, children), - component = React.render(element, options.container); + component = ReactDOM.render(element, options.container); if (!_.isEmpty(state)) { exports.injectState(component, state); diff --git a/tests/integration/render-inject-state.js b/tests/integration/render-inject-state.js new file mode 100644 index 0000000..a9ca763 --- /dev/null +++ b/tests/integration/render-inject-state.js @@ -0,0 +1,82 @@ +var React = require('react'), + ReactDOM = require('react-dom'), + render = require('../../src/render.js').render; + +describe('INTEGRATION Render and inject state', function() { + var domContainer, + component; + + class ChildComponent extends React.Component { + render() { + return React.DOM.span(); + } + } + + class ParentComponent extends React.Component { + render() { + return React.createElement(ChildComponent, {ref: 'child'}); + } + } + + class GrandparentComponent extends React.Component { + render() { + return React.createElement(ParentComponent, {ref: 'child'}); + } + } + + beforeEach(function() { + domContainer = document.createElement('div'); + }); + + afterEach(function() { + ReactDOM.unmountComponentAtNode(domContainer); + }); + + it('should set state on root component', function() { + component = render({ + component: GrandparentComponent, + snapshot: { + state: {foo: 'bar'} + }, + container: domContainer + }); + + expect(component.state.foo).to.equal('bar'); + }); + + it('should set state on child component', function() { + component = render({ + component: GrandparentComponent, + snapshot: { + state: { + children: { + child: {foo: 'bar'} + } + } + }, + container: domContainer + }); + + expect(component.refs.child.state.foo).to.equal('bar'); + }); + + it('should set state on grandchild component', function() { + component = render({ + component: GrandparentComponent, + snapshot: { + state: { + children: { + child: { + children: { + child: {foo: 'bar'} + } + } + } + } + }, + container: domContainer + }); + + expect(component.refs.child.refs.child.state.foo).to.equal('bar'); + }); +}); diff --git a/tests/integration/render.js b/tests/integration/render.js new file mode 100644 index 0000000..7d83709 --- /dev/null +++ b/tests/integration/render.js @@ -0,0 +1,47 @@ +var React = require('react'), + ReactDOM = require('react-dom'), + render = require('../../src/render.js').render; + +describe('INTEGRATION Render', function() { + var domContainer, + children = [React.createElement('span', { + key: '1', + children: 'test child' + })], + component; + + class Component extends React.Component { + render() { + return React.DOM.span(); + } + } + + beforeEach(function() { + domContainer = document.createElement('div'); + + component = render({ + component: Component, + snapshot: { + foo: 'bar', + children: children + }, + container: domContainer + }); + }); + + afterEach(function() { + ReactDOM.unmountComponentAtNode(domContainer); + }); + + it('should create component with correct props', function() { + expect(component.props.foo).to.equal('bar'); + }); + + it('should receive children through props', function() { + expect(component.props.children).to.be.equal(children); + }); + + it('should render in given container', function() { + expect(ReactDOM.findDOMNode(component).parentNode).to.equal(domContainer); + }); +}); diff --git a/tests/load-child-component.js b/tests/unit/load-child-component.js similarity index 76% rename from tests/load-child-component.js rename to tests/unit/load-child-component.js index f363cca..1292c69 100644 --- a/tests/load-child-component.js +++ b/tests/unit/load-child-component.js @@ -1,10 +1,11 @@ var _ = require('lodash'), - React = require('react/addons'), - renderIntoDocument = React.addons.TestUtils.renderIntoDocument, - loadChild = require('../src/load-child.js'), - LoadChildComponent = require('../src/load-child-component.js'); + React = require('react'), + TestUtils = require('react-addons-test-utils'), + renderIntoDocument = TestUtils.renderIntoDocument, + loadChild = require('../../src/load-child.js'), + LoadChildComponent = require('../../src/load-child-component.js'); -describe('Load child component', function() { +describe('UNIT Load child component', function() { var fakeReactElement = {}, myComponent; diff --git a/tests/load-child-mixin.js b/tests/unit/load-child-mixin.js similarity index 76% rename from tests/load-child-mixin.js rename to tests/unit/load-child-mixin.js index 36cd53f..22c9d87 100644 --- a/tests/load-child-mixin.js +++ b/tests/unit/load-child-mixin.js @@ -1,10 +1,11 @@ var _ = require('lodash'), - React = require('react/addons'), - renderIntoDocument = React.addons.TestUtils.renderIntoDocument, - loadChild = require('../src/load-child.js'), - LoadChildMixin = require('../src/load-child-mixin.js'); + React = require('react'), + TestUtils = require('react-addons-test-utils'), + renderIntoDocument = TestUtils.renderIntoDocument, + loadChild = require('../../src/load-child.js'), + LoadChildMixin = require('../../src/load-child-mixin.js'); -describe('Load child mixin', function() { +describe('UNIT Load child mixin', function() { var fakeReactElement = {}, myComponent; diff --git a/tests/load-child.js b/tests/unit/load-child.js similarity index 96% rename from tests/load-child.js rename to tests/unit/load-child.js index fabdcb1..14b2234 100644 --- a/tests/load-child.js +++ b/tests/unit/load-child.js @@ -1,7 +1,7 @@ var React = require('react'), - loadChild = require('../src/load-child.js').loadChild; + loadChild = require('../../src/load-child.js').loadChild; -describe('Load child', function() { +describe('UNIT Load child', function() { var FirstComponent = {}, SecondComponent = {}, component, diff --git a/tests/load-missing-child.js b/tests/unit/load-missing-child.js similarity index 87% rename from tests/load-missing-child.js rename to tests/unit/load-missing-child.js index 4811525..c1efecc 100644 --- a/tests/load-missing-child.js +++ b/tests/unit/load-missing-child.js @@ -1,7 +1,7 @@ var React = require('react'), - loadChild = require('../src/load-child.js').loadChild; + loadChild = require('../../src/load-child.js').loadChild; -describe('Load missing child', function() { +describe('UNIT Load missing child', function() { var component; beforeEach(function() { diff --git a/tests/render-inject-state.js b/tests/unit/render-inject-state.js similarity index 83% rename from tests/render-inject-state.js rename to tests/unit/render-inject-state.js index 1f624e3..0ee42f8 100644 --- a/tests/render-inject-state.js +++ b/tests/unit/render-inject-state.js @@ -1,8 +1,10 @@ -var React = require('react/addons'), - renderIntoDocument = React.addons.TestUtils.renderIntoDocument, - render = require('../src/render.js').render; +var React = require('react'), + ReactDOM = require('react-dom'), + TestUtils = require('react-addons-test-utils'), + renderIntoDocument = TestUtils.renderIntoDocument, + render = require('../../src/render.js').render; -describe('Render and inject state', function() { +describe('UNIT Render and inject state', function() { var component; class ChildComponent extends React.Component { @@ -30,11 +32,11 @@ describe('Render and inject state', function() { sinon.spy(component.refs.child, 'setState'); sinon.spy(component.refs.child.refs.child, 'setState'); - sinon.stub(React, 'render').returns(component); + sinon.stub(ReactDOM, 'render').returns(component); }); afterEach(function() { - React.render.restore(); + ReactDOM.render.restore(); }); it('should set state on root component', function() { diff --git a/tests/render.js b/tests/unit/render.js similarity index 83% rename from tests/render.js rename to tests/unit/render.js index 835bed7..3dbb3a5 100644 --- a/tests/render.js +++ b/tests/unit/render.js @@ -1,7 +1,8 @@ var React = require('react'), - render = require('../src/render.js').render; + ReactDOM = require('react-dom'), + render = require('../../src/render.js').render; -describe('Render', function() { +describe('UNIT Render', function() { var domContainer, children = [React.createElement('span', { key: '1', @@ -16,7 +17,7 @@ describe('Render', function() { beforeEach(function() { sinon.spy(React, 'createElement'); - sinon.stub(React, 'render'); + sinon.stub(ReactDOM, 'render'); domContainer = document.createElement('div'); @@ -32,7 +33,7 @@ describe('Render', function() { afterEach(function() { React.createElement.restore(); - React.render.restore(); + ReactDOM.render.restore(); }); it('should create element for component', function() { @@ -56,12 +57,12 @@ describe('Render', function() { }); it('should render created element', function() { - var args = React.render.lastCall.args; + var args = ReactDOM.render.lastCall.args; expect(args[0]).to.equal(React.createElement.returnValues[0]); }); it('should render in given container', function() { - var args = React.render.lastCall.args; + var args = ReactDOM.render.lastCall.args; expect(args[1]).to.equal(domContainer); }); }); diff --git a/tests/serialize.js b/tests/unit/serialize.js similarity index 88% rename from tests/serialize.js rename to tests/unit/serialize.js index fbe782b..72086f0 100644 --- a/tests/serialize.js +++ b/tests/unit/serialize.js @@ -1,8 +1,9 @@ -var React = require('react/addons'), - renderIntoDocument = React.addons.TestUtils.renderIntoDocument, - serialize = require('../src/serialize.js').serialize; +var React = require('react'), + TestUtils = require('react-addons-test-utils'), + renderIntoDocument = TestUtils.renderIntoDocument, + serialize = require('../../src/serialize.js').serialize; -describe('Serialize', function() { +describe('UNIT Serialize', function() { class ChildComponent extends React.Component { render() { return React.DOM.span();