twreporter-react
Version:
React-Redux site for The Reporter Foundation in Taiwan
1,153 lines (976 loc) • 31.8 kB
JavaScript
import expect from 'expect';
import jsdom from 'mocha-jsdom';
import React, { createClass, Children, PropTypes, Component } from 'react';
import ReactDOM from 'react-dom';
import TestUtils from 'react-addons-test-utils';
import { createStore } from 'redux';
import { connect } from '../../src/index';
describe('React', () => {
describe('connect', () => {
jsdom();
class Passthrough extends Component {
render() {
return <div {...this.props} />;
}
}
class ProviderMock extends Component {
static childContextTypes = {
store: PropTypes.object.isRequired
}
getChildContext() {
return { store: this.props.store };
}
render() {
return Children.only(this.props.children);
}
}
function stringBuilder(prev = '', action) {
return action.type === 'APPEND'
? prev + action.body
: prev;
}
it('should receive the store in the context', () => {
const store = createStore(() => ({}));
()
class Container extends Component {
render() {
return <div {...this.props} />;
}
}
const tree = TestUtils.renderIntoDocument(
<ProviderMock store={store}>
<Container pass="through" />
</ProviderMock>
);
const container = TestUtils.findRenderedComponentWithType(tree, Container);
expect(container.context.store).toBe(store);
});
it('should pass state and props to the given component', () => {
const store = createStore(() => ({
foo: 'bar',
baz: 42,
hello: 'world'
}));
(({ foo, baz }) => ({ foo, baz }))
class Container extends Component {
render() {
return <Passthrough {...this.props} />;
}
}
const container = TestUtils.renderIntoDocument(
<ProviderMock store={store}>
<Container pass='through' baz={50} />
</ProviderMock>
);
const stub = TestUtils.findRenderedComponentWithType(container, Passthrough);
expect(stub.props.pass).toEqual('through');
expect(stub.props.foo).toEqual('bar');
expect(stub.props.baz).toEqual(42);
expect(stub.props.hello).toEqual(undefined);
expect(() =>
TestUtils.findRenderedComponentWithType(container, Container)
).toNotThrow();
});
it('should subscribe to the store changes', () => {
const store = createStore(stringBuilder);
(state => ({ string: state }) )
class Container extends Component {
render() {
return <Passthrough {...this.props}/>;
}
}
const tree = TestUtils.renderIntoDocument(
<ProviderMock store={store}>
<Container />
</ProviderMock>
);
const stub = TestUtils.findRenderedComponentWithType(tree, Passthrough);
expect(stub.props.string).toBe('');
store.dispatch({ type: 'APPEND', body: 'a'});
expect(stub.props.string).toBe('a');
store.dispatch({ type: 'APPEND', body: 'b'});
expect(stub.props.string).toBe('ab');
});
it('should handle dispatches before componentDidMount', () => {
const store = createStore(stringBuilder);
(state => ({ string: state }) )
class Container extends Component {
componentWillMount() {
store.dispatch({ type: 'APPEND', body: 'a'});
}
render() {
return <Passthrough {...this.props}/>;
}
}
const tree = TestUtils.renderIntoDocument(
<ProviderMock store={store}>
<Container />
</ProviderMock>
);
const stub = TestUtils.findRenderedComponentWithType(tree, Passthrough);
expect(stub.props.string).toBe('a');
});
it('should handle additional prop changes in addition to slice', () => {
const store = createStore(() => ({
foo: 'bar'
}));
(state => state)
class ConnectContainer extends Component {
render() {
return (
<Passthrough {...this.props} pass={this.props.bar.baz} />
);
}
}
class Container extends Component {
constructor() {
super();
this.state = {
bar: {
baz: ''
}
};
}
componentDidMount() {
this.setState({
bar: Object.assign({}, this.state.bar, { baz: 'through' })
});
}
render() {
return (
<ProviderMock store={store}>
<ConnectContainer bar={this.state.bar} />
</ProviderMock>
);
}
}
const container = TestUtils.renderIntoDocument(<Container />);
const stub = TestUtils.findRenderedComponentWithType(container, Passthrough);
expect(stub.props.foo).toEqual('bar');
expect(stub.props.pass).toEqual('through');
});
it('should remove undefined props', () => {
const store = createStore(() => ({}));
let props = { x: true };
let container;
(() => ({}), () => ({}))
class ConnectContainer extends Component {
render() {
return (
<Passthrough {...this.props} />
);
}
}
class HolderContainer extends Component {
render() {
return (
<ConnectContainer {...props} />
);
}
}
TestUtils.renderIntoDocument(
<ProviderMock store={store}>
<HolderContainer ref={instance => container = instance} />
</ProviderMock>
);
const propsBefore = {
...TestUtils.findRenderedComponentWithType(container, Passthrough).props
};
props = {};
container.forceUpdate();
const propsAfter = {
...TestUtils.findRenderedComponentWithType(container, Passthrough).props
};
expect(propsBefore.x).toEqual(true);
expect('x' in propsAfter).toEqual(false, 'x prop must be removed');
});
it('should remove undefined props without mapDispatch', () => {
const store = createStore(() => ({}));
let props = { x: true };
let container;
(() => ({}))
class ConnectContainer extends Component {
render() {
return (
<Passthrough {...this.props} />
);
}
}
class HolderContainer extends Component {
render() {
return (
<ConnectContainer {...props} />
);
}
}
TestUtils.renderIntoDocument(
<ProviderMock store={store}>
<HolderContainer ref={instance => container = instance} />
</ProviderMock>
);
const propsBefore = {
...TestUtils.findRenderedComponentWithType(container, Passthrough).props
};
props = {};
container.forceUpdate();
const propsAfter = {
...TestUtils.findRenderedComponentWithType(container, Passthrough).props
};
expect(propsBefore.x).toEqual(true);
expect('x' in propsAfter).toEqual(false, 'x prop must be removed');
});
it('should ignore deep mutations in props', () => {
const store = createStore(() => ({
foo: 'bar'
}));
(state => state)
class ConnectContainer extends Component {
render() {
return (
<Passthrough {...this.props} pass={this.props.bar.baz} />
);
}
}
class Container extends Component {
constructor() {
super();
this.state = {
bar: {
baz: ''
}
};
}
componentDidMount() {
// Simulate deep object mutation
this.state.bar.baz = 'through';
this.setState({
bar: this.state.bar
});
}
render() {
return (
<ProviderMock store={store}>
<ConnectContainer bar={this.state.bar} />
</ProviderMock>
);
}
}
const container = TestUtils.renderIntoDocument(<Container />);
const stub = TestUtils.findRenderedComponentWithType(container, Passthrough);
expect(stub.props.foo).toEqual('bar');
expect(stub.props.pass).toEqual('');
});
it('should allow for merge to incorporate state and prop changes', () => {
const store = createStore(stringBuilder);
function doSomething(thing) {
return {
type: 'APPEND',
body: thing
};
}
(
state => ({stateThing: state}),
dispatch => ({
doSomething: (whatever) => dispatch(doSomething(whatever))
}),
(stateProps, actionProps, parentProps) => ({
...stateProps,
...actionProps,
mergedDoSomething(thing) {
const seed = stateProps.stateThing === '' ? 'HELLO ' : '';
actionProps.doSomething(seed + thing + parentProps.extra);
}
})
)
class Container extends Component {
render() {
return <Passthrough {...this.props}/>;
};
}
class OuterContainer extends Component {
constructor() {
super();
this.state = { extra: 'z' };
}
render() {
return (
<ProviderMock store={store}>
<Container extra={this.state.extra} />
</ProviderMock>
);
}
}
const tree = TestUtils.renderIntoDocument(<OuterContainer />);
const stub = TestUtils.findRenderedComponentWithType(tree, Passthrough);
expect(stub.props.stateThing).toBe('');
stub.props.mergedDoSomething('a');
expect(stub.props.stateThing).toBe('HELLO az');
stub.props.mergedDoSomething('b');
expect(stub.props.stateThing).toBe('HELLO azbz');
tree.setState({extra: 'Z'});
stub.props.mergedDoSomething('c');
expect(stub.props.stateThing).toBe('HELLO azbzcZ');
});
it('should merge actionProps into WrappedComponent', () => {
const store = createStore(() => ({
foo: 'bar'
}));
(
state => state,
dispatch => ({ dispatch })
)
class Container extends Component {
render() {
return <Passthrough {...this.props} />;
}
}
const container = TestUtils.renderIntoDocument(
<ProviderMock store={store}>
<Container pass='through' />
</ProviderMock>
);
const stub = TestUtils.findRenderedComponentWithType(container, Passthrough);
expect(stub.props.dispatch).toEqual(store.dispatch);
expect(stub.props.foo).toEqual('bar');
expect(() =>
TestUtils.findRenderedComponentWithType(container, Container)
).toNotThrow();
const decorated = TestUtils.findRenderedComponentWithType(container, Container);
expect(decorated.isSubscribed()).toBe(true);
});
it('should not invoke mapState when props change if it only has one argument', () => {
const store = createStore(stringBuilder);
let invocationCount = 0;
(() => {
invocationCount++;
return {};
})
class WithoutProps extends Component {
render() {
return <Passthrough {...this.props}/>;
}
}
class OuterComponent extends Component {
constructor() {
super();
this.state = { foo: 'FOO' };
}
setFoo(foo) {
this.setState({ foo });
}
render() {
return (
<div>
<WithoutProps {...this.state} />
</div>
);
}
}
let outerComponent;
TestUtils.renderIntoDocument(
<ProviderMock store={store}>
<OuterComponent ref={c => outerComponent = c} />
</ProviderMock>
);
outerComponent.setFoo('BAR');
outerComponent.setFoo('DID');
expect(invocationCount).toEqual(2);
});
it('should invoke mapState every time props are changed if it has a second argument', () => {
const store = createStore(stringBuilder);
let propsPassedIn;
let invocationCount = 0;
((state, props) => {
invocationCount++;
propsPassedIn = props;
return {};
})
class WithProps extends Component {
render() {
return <Passthrough {...this.props}/>;
}
}
class OuterComponent extends Component {
constructor() {
super();
this.state = { foo: 'FOO' };
}
setFoo(foo) {
this.setState({ foo });
}
render() {
return (
<div>
<WithProps {...this.state} />
</div>
);
}
}
let outerComponent;
TestUtils.renderIntoDocument(
<ProviderMock store={store}>
<OuterComponent ref={c => outerComponent = c} />
</ProviderMock>
);
outerComponent.setFoo('BAR');
outerComponent.setFoo('BAZ');
expect(invocationCount).toEqual(4);
expect(propsPassedIn).toEqual({
foo: 'BAZ'
});
});
it('should not invoke mapDispatch when props change if it only has one argument', () => {
const store = createStore(stringBuilder);
let invocationCount = 0;
(null, () => {
invocationCount++;
return {};
})
class WithoutProps extends Component {
render() {
return <Passthrough {...this.props}/>;
}
}
class OuterComponent extends Component {
constructor() {
super();
this.state = { foo: 'FOO' };
}
setFoo(foo) {
this.setState({ foo });
}
render() {
return (
<div>
<WithoutProps {...this.state} />
</div>
);
}
}
let outerComponent;
TestUtils.renderIntoDocument(
<ProviderMock store={store}>
<OuterComponent ref={c => outerComponent = c} />
</ProviderMock>
);
outerComponent.setFoo('BAR');
outerComponent.setFoo('DID');
expect(invocationCount).toEqual(1);
});
it('should invoke mapDispatch every time props are changed if it has a second argument', () => {
const store = createStore(stringBuilder);
let propsPassedIn;
let invocationCount = 0;
(null, (dispatch, props) => {
invocationCount++;
propsPassedIn = props;
return {};
})
class WithProps extends Component {
render() {
return <Passthrough {...this.props}/>;
}
}
class OuterComponent extends Component {
constructor() {
super();
this.state = { foo: 'FOO' };
}
setFoo(foo) {
this.setState({ foo });
}
render() {
return (
<div>
<WithProps {...this.state} />
</div>
);
}
}
let outerComponent;
TestUtils.renderIntoDocument(
<ProviderMock store={store}>
<OuterComponent ref={c => outerComponent = c} />
</ProviderMock>
);
outerComponent.setFoo('BAR');
outerComponent.setFoo('BAZ');
expect(invocationCount).toEqual(3);
expect(propsPassedIn).toEqual({
foo: 'BAZ'
});
});
it('should pass dispatch and avoid subscription if arguments are falsy', () => {
const store = createStore(() => ({
foo: 'bar'
}));
function runCheck(...connectArgs) {
(...connectArgs)
class Container extends Component {
render() {
return <Passthrough {...this.props} />;
}
}
const container = TestUtils.renderIntoDocument(
<ProviderMock store={store}>
<Container pass='through' />
</ProviderMock>
);
const stub = TestUtils.findRenderedComponentWithType(container, Passthrough);
expect(stub.props.dispatch).toEqual(store.dispatch);
expect(stub.props.foo).toBe(undefined);
expect(stub.props.pass).toEqual('through');
expect(() =>
TestUtils.findRenderedComponentWithType(container, Container)
).toNotThrow();
const decorated = TestUtils.findRenderedComponentWithType(container, Container);
expect(decorated.isSubscribed()).toBe(false);
}
runCheck();
runCheck(null, null, null);
runCheck(false, false, false);
});
it('should unsubscribe before unmounting', () => {
const store = createStore(stringBuilder);
const subscribe = store.subscribe;
// Keep track of unsubscribe by wrapping subscribe()
const spy = expect.createSpy(() => ({}));
store.subscribe = (listener) => {
const unsubscribe = subscribe(listener);
return () => {
spy();
return unsubscribe();
};
};
(
state => ({ string: state }),
dispatch => ({ dispatch })
)
class Container extends Component {
render() {
return <Passthrough {...this.props} />;
}
}
const div = document.createElement('div');
ReactDOM.render(
<ProviderMock store={store}>
<Container />
</ProviderMock>,
div
);
expect(spy.calls.length).toBe(0);
ReactDOM.unmountComponentAtNode(div);
expect(spy.calls.length).toBe(1);
});
it('should not attempt to set state after unmounting', () => {
const store = createStore(stringBuilder);
let mapStateToPropsCalls = 0;
(
() => ({ calls: ++mapStateToPropsCalls }),
dispatch => ({ dispatch })
)
class Container extends Component {
render() {
return <Passthrough {...this.props} />;
}
}
const div = document.createElement('div');
store.subscribe(() =>
ReactDOM.unmountComponentAtNode(div)
);
ReactDOM.render(
<ProviderMock store={store}>
<Container />
</ProviderMock>,
div
);
expect(mapStateToPropsCalls).toBe(2);
const spy = expect.spyOn(console, 'error');
store.dispatch({ type: 'APPEND', body: 'a'});
spy.destroy();
expect(spy.calls.length).toBe(0);
expect(mapStateToPropsCalls).toBe(2);
});
it('should shallowly compare the selected state to prevent unnecessary updates', () => {
const store = createStore(stringBuilder);
const spy = expect.createSpy(() => ({}));
function render({ string }) {
spy();
return <Passthrough string={string}/>;
}
(
state => ({ string: state }),
dispatch => ({ dispatch })
)
class Container extends Component {
render() {
return render(this.props);
}
}
const tree = TestUtils.renderIntoDocument(
<ProviderMock store={store}>
<Container />
</ProviderMock>
);
const stub = TestUtils.findRenderedComponentWithType(tree, Passthrough);
expect(spy.calls.length).toBe(1);
expect(stub.props.string).toBe('');
store.dispatch({ type: 'APPEND', body: 'a'});
expect(spy.calls.length).toBe(2);
store.dispatch({ type: 'APPEND', body: 'b'});
expect(spy.calls.length).toBe(3);
store.dispatch({ type: 'APPEND', body: ''});
expect(spy.calls.length).toBe(3);
});
it('should shallowly compare the merged state to prevent unnecessary updates', () => {
const store = createStore(stringBuilder);
const spy = expect.createSpy(() => ({}));
function render({ string, pass }) {
spy();
return <Passthrough string={string} pass={pass} passVal={pass.val} />;
}
(
state => ({ string: state }),
dispatch => ({ dispatch }),
(stateProps, dispatchProps, parentProps) => ({
...dispatchProps,
...stateProps,
...parentProps
})
)
class Container extends Component {
render() {
return render(this.props);
}
}
class Root extends Component {
constructor(props) {
super(props);
this.state = { pass: '' };
}
render() {
return (
<ProviderMock store={store}>
<Container pass={this.state.pass} />
</ProviderMock>
);
}
}
const tree = TestUtils.renderIntoDocument(<Root />);
const stub = TestUtils.findRenderedComponentWithType(tree, Passthrough);
expect(spy.calls.length).toBe(1);
expect(stub.props.string).toBe('');
expect(stub.props.pass).toBe('');
store.dispatch({ type: 'APPEND', body: 'a'});
expect(spy.calls.length).toBe(2);
expect(stub.props.string).toBe('a');
expect(stub.props.pass).toBe('');
tree.setState({ pass: '' });
expect(spy.calls.length).toBe(2);
expect(stub.props.string).toBe('a');
expect(stub.props.pass).toBe('');
tree.setState({ pass: 'through' });
expect(spy.calls.length).toBe(3);
expect(stub.props.string).toBe('a');
expect(stub.props.pass).toBe('through');
tree.setState({ pass: 'through' });
expect(spy.calls.length).toBe(3);
expect(stub.props.string).toBe('a');
expect(stub.props.pass).toBe('through');
const obj = { prop: 'val' };
tree.setState({ pass: obj });
expect(spy.calls.length).toBe(4);
expect(stub.props.string).toBe('a');
expect(stub.props.pass).toBe(obj);
tree.setState({ pass: obj });
expect(spy.calls.length).toBe(4);
expect(stub.props.string).toBe('a');
expect(stub.props.pass).toBe(obj);
const obj2 = Object.assign({}, obj, { val: 'otherval' });
tree.setState({ pass: obj2 });
expect(spy.calls.length).toBe(5);
expect(stub.props.string).toBe('a');
expect(stub.props.pass).toBe(obj2);
obj2.val = 'mutation';
tree.setState({ pass: obj2 });
expect(spy.calls.length).toBe(5);
expect(stub.props.string).toBe('a');
expect(stub.props.passVal).toBe('otherval');
});
it('should throw an error if mapState, mapDispatch, or mergeProps returns anything but a plain object', () => {
const store = createStore(() => ({}));
function makeContainer(mapState, mapDispatch, mergeProps) {
return React.createElement(
(mapState, mapDispatch, mergeProps)
class Container extends Component {
render() {
return <Passthrough />;
}
}
);
}
function AwesomeMap() { }
expect(() => {
TestUtils.renderIntoDocument(
<ProviderMock store={store}>
{makeContainer(() => 1, () => ({}), () => ({}))}
</ProviderMock>
);
}).toThrow(/mapState/);
expect(() => {
TestUtils.renderIntoDocument(
<ProviderMock store={store}>
{makeContainer(() => 'hey', () => ({}), () => ({}))}
</ProviderMock>
);
}).toThrow(/mapState/);
expect(() => {
TestUtils.renderIntoDocument(
<ProviderMock store={store}>
{makeContainer(() => new AwesomeMap(), () => ({}), () => ({}))}
</ProviderMock>
);
}).toThrow(/mapState/);
expect(() => {
TestUtils.renderIntoDocument(
<ProviderMock store={store}>
{makeContainer(() => ({}), () => 1, () => ({}))}
</ProviderMock>
);
}).toThrow(/mapDispatch/);
expect(() => {
TestUtils.renderIntoDocument(
<ProviderMock store={store}>
{makeContainer(() => ({}), () => 'hey', () => ({}))}
</ProviderMock>
);
}).toThrow(/mapDispatch/);
expect(() => {
TestUtils.renderIntoDocument(
<ProviderMock store={store}>
{makeContainer(() => ({}), () => new AwesomeMap(), () => ({}))}
</ProviderMock>
);
}).toThrow(/mapDispatch/);
expect(() => {
TestUtils.renderIntoDocument(
<ProviderMock store={store}>
{makeContainer(() => ({}), () => ({}), () => 1)}
</ProviderMock>
);
}).toThrow(/mergeProps/);
expect(() => {
TestUtils.renderIntoDocument(
<ProviderMock store={store}>
{makeContainer(() => ({}), () => ({}), () => 'hey')}
</ProviderMock>
);
}).toThrow(/mergeProps/);
expect(() => {
TestUtils.renderIntoDocument(
<ProviderMock store={store}>
{makeContainer(() => ({}), () => ({}), () => new AwesomeMap())}
</ProviderMock>
);
}).toThrow(/mergeProps/);
});
it('should recalculate the state and rebind the actions on hot update', () => {
const store = createStore(() => {});
(
null,
() => ({ scooby: 'doo' })
)
class ContainerBefore extends Component {
render() {
return (
<Passthrough {...this.props} />
);
}
}
(
() => ({ foo: 'baz' }),
() => ({ scooby: 'foo' })
)
class ContainerAfter extends Component {
render() {
return (
<Passthrough {...this.props} />
);
}
}
(
() => ({ foo: 'bar' }),
() => ({ scooby: 'boo' })
)
class ContainerNext extends Component {
render() {
return (
<Passthrough {...this.props} />
);
}
}
let container;
TestUtils.renderIntoDocument(
<ProviderMock store={store}>
<ContainerBefore ref={instance => container = instance} />
</ProviderMock>
);
const stub = TestUtils.findRenderedComponentWithType(container, Passthrough);
expect(stub.props.foo).toEqual(undefined);
expect(stub.props.scooby).toEqual('doo');
function imitateHotReloading(TargetClass, SourceClass) {
// Crude imitation of hot reloading that does the job
Object.keys(SourceClass.prototype).filter(key =>
typeof SourceClass.prototype[key] === 'function'
).forEach(key => {
if (key !== 'render') {
TargetClass.prototype[key] = SourceClass.prototype[key];
}
});
container.forceUpdate();
}
imitateHotReloading(ContainerBefore, ContainerAfter);
expect(stub.props.foo).toEqual('baz');
expect(stub.props.scooby).toEqual('foo');
imitateHotReloading(ContainerBefore, ContainerNext);
expect(stub.props.foo).toEqual('bar');
expect(stub.props.scooby).toEqual('boo');
});
it('should set the displayName correctly', () => {
expect(connect(state => state)(
class Foo extends Component {
render() {
return <div />;
}
}
).displayName).toBe('Connect(Foo)');
expect(connect(state => state)(
createClass({
displayName: 'Bar',
render() {
return <div />;
}
})
).displayName).toBe('Connect(Bar)');
expect(connect(state => state)(
createClass({
render() {
return <div />;
}
})
).displayName).toBe('Connect(Component)');
});
it('should expose the wrapped component as WrappedComponent', () => {
class Container extends Component {
render() {
return <Passthrough />;
}
}
const decorator = connect(state => state);
const decorated = decorator(Container);
expect(decorated.WrappedComponent).toBe(Container);
});
it('should use the store from the props instead of from the context if present', () => {
class Container extends Component {
render() {
return <Passthrough />;
}
}
let actualState;
const expectedState = { foos: {} };
const decorator = connect(state => {
actualState = state;
return {};
});
const Decorated = decorator(Container);
const mockStore = {
dispatch: () => {},
subscribe: () => {},
getState: () => expectedState
};
TestUtils.renderIntoDocument(<Decorated store={mockStore} />);
expect(actualState).toEqual(expectedState);
});
it('should throw an error if the store is not in the props or context', () => {
class Container extends Component {
render() {
return <Passthrough />;
}
}
const decorator = connect(() => {});
const Decorated = decorator(Container);
const expectedError =
`Invariant Violation: Could not find "store" in either the context ` +
`or props of "Connect(Container)". Either wrap the root component in a ` +
`<Provider>, or explicitly pass "store" as a prop to "Connect(Container)".`;
expect(() => TestUtils.renderIntoDocument(<Decorated />)).toThrow(e => {
expect(e.message).toEqual(expectedError);
return true;
});
});
it('should return the instance of the wrapped component for use in calling child methods', () => {
const store = createStore(() => ({}));
const someData = {
some: 'data'
};
class Container extends Component {
someInstanceMethod() {
return someData;
}
render() {
return <Passthrough />;
}
}
const decorator = connect(state => state);
const Decorated = decorator(Container);
const tree = TestUtils.renderIntoDocument(
<ProviderMock store={store}>
<Decorated />
</ProviderMock>
);
const decorated = TestUtils.findRenderedComponentWithType(tree, Decorated);
expect(() => decorated.someInstanceMethod()).toThrow();
expect(decorated.getWrappedInstance().someInstanceMethod()).toBe(someData);
expect(decorated.refs.wrappedInstance.someInstanceMethod()).toBe(someData);
});
it('should wrap impure components without supressing updates', () => {
const store = createStore(() => ({}));
class ImpureComponent extends Component {
static contextTypes = {
statefulValue: React.PropTypes.number
};
render() {
return <Passthrough statefulValue={this.context.statefulValue} />;
}
}
const decorator = connect(state => state, null, null, { pure: false });
const Decorated = decorator(ImpureComponent);
class StatefulWrapper extends Component {
state = {
value: 0
};
static childContextTypes = {
statefulValue: React.PropTypes.number
};
getChildContext() {
return {
statefulValue: this.state.value
};
}
render() {
return <Decorated />;
};
}
const tree = TestUtils.renderIntoDocument(
<ProviderMock store={store}>
<StatefulWrapper />
</ProviderMock>
);
const target = TestUtils.findRenderedComponentWithType(tree, Passthrough);
const wrapper = TestUtils.findRenderedComponentWithType(tree, StatefulWrapper);
expect(target.props.statefulValue).toEqual(0);
wrapper.setState({ value: 1 });
expect(target.props.statefulValue).toEqual(1);
});
});
});