<commit_before>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(() => ({}));

      @connect()
      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'
      }));

      @connect(({ 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);

      @connect(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);

      @connect(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'
      }));

      @connect(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;

      @connect(() => ({}), () => ({}))
      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;

      @connect(() => ({}))
      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'
      }));

      @connect(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
        };
      }

      @connect(
        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'
      }));

      @connect(
        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;

      @connect(() => {
        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;

      @connect((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;

      @connect(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;

      @connect(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) {
        @connect(...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();
        };
      };

      @connect(
        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;

      @connect(
        () => ({ 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}/>;
      }

      @connect(
        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} />;
      }

      @connect(
        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(
          @connect(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(() => {});

      @connect(
        null,
        () => ({ scooby: 'doo' })
      )
      class ContainerBefore extends Component {
        render() {
          return (
            <Passthrough {...this.props} />
          );
        }
      }

      @connect(
        () => ({ foo: 'baz' }),
        () => ({ scooby: 'foo' })
      )
      class ContainerAfter extends Component {
        render() {
          return (
            <Passthrough {...this.props} />
          );
        }
      }

      @connect(
        () => ({ 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);

      expect(() =>
        TestUtils.renderIntoDocument(<Decorated />)
      ).toThrow(
        '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)".'
      );
    });

    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);
    });

    it('should pass state consistently to mapState', () => {
      const store = createStore(stringBuilder);

      store.dispatch({ type: 'APPEND', body: 'a'});
      let childMapStateInvokes = 0;

      @connect(state => ({ state }))
      class Container extends Component {

        emitChange() {<commit_msg>Add failing test for #118<commit_after>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(() => ({}));

      @connect()
      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'
      }));

      @connect(({ 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);

      @connect(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);

      @connect(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'
      }));

      @connect(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;

      @connect(() => ({}), () => ({}))
      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;

      @connect(() => ({}))
      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'
      }));

      @connect(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
        };
      }

      @connect(
        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'
      }));

      @connect(
        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;

      @connect(() => {
        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;

      @connect((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;

      @connect(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;

      @connect(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) {
        @connect(...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();
        };
      };

      @connect(
        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;

      @connect(
        () => ({ 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}/>;
      }

      @connect(
        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} />;
      }

      @connect(
        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(
          @connect(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(() => {});

      @connect(
        null,
        () => ({ scooby: 'doo' })
      )
      class ContainerBefore extends Component {
        render() {
          return (
            <Passthrough {...this.props} />
          );
        }
      }

      @connect(
        () => ({ foo: 'baz' }),
        () => ({ scooby: 'foo' })
      )
      class ContainerAfter extends Component {
        render() {
          return (
            <Passthrough {...this.props} />
          );
        }
      }

      @connect(
        () => ({ 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);

      expect(() =>
        TestUtils.renderIntoDocument(<Decorated />)
      ).toThrow(
        '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)".'
      );
    });

    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);
    });

    it('calls mapState and mapDispatch for impure components', () => {
      const store = createStore(() => ({
        foo: 'foo',
        bar: 'bar'
      }));

      const mapStateSpy = expect.createSpy();
      const mapDispatchSpy = expect.createSpy().andReturn({});

      class ImpureComponent extends Component {
        render() {
          return <Passthrough statefulValue={this.props.value} />;
        }
      }

      const decorator = connect(
        (state, {storeGetter}) => {
          mapStateSpy();
          return {value: state[storeGetter.storeKey]};
        },
        mapDispatchSpy,
        null,
        { pure: false }
      );
      const Decorated = decorator(ImpureComponent);

      class StatefulWrapper extends Component {
        state = {
          storeGetter: {storeKey: 'foo'}
        };

        render() {
          return <Decorated storeGetter={this.state.storeGetter} />;
        };
      }

      const tree = TestUtils.renderIntoDocument(
        <ProviderMock store={store}>
          <StatefulWrapper />
        </ProviderMock>
      );

      const target = TestUtils.findRenderedComponentWithType(tree, Passthrough);
      const wrapper = TestUtils.findRenderedComponentWithType(tree, StatefulWrapper);

      expect(mapStateSpy.calls.length).toBe(2);
      expect(mapDispatchSpy.calls.length).toBe(2);
      expect(target.props.statefulValue).toEqual('foo');

      // Impure update
      const storeGetter = wrapper.state.storeGetter;
      storeGetter.storeKey = 'bar';
      wrapper.setState({ storeGetter });

      expect(mapStateSpy.calls.length).toBe(3);
      expect(mapDispatchSpy.calls.length).toBe(3);
      expect(target.props.statefulValue).toEqual('bar');
    });

    it('should pass state consistently to mapState', () => {
      const store = createStore(stringBuilder);

      store.dispatch({ type: 'APPEND', body: 'a'});
      let childMapStateInvokes = 0;

      @connect(state => ({ state }))
      class Container extends Component {

        emitChange() {
{'fraction_non_alphanumeric': 0.14464060337879964, 'fraction_numerical': 0.0015054057752839742, 'mean_word_length': 1.9622089095085806, 'pattern_counts': {'":': 0, '<': 284, '<?xml version=': 0, '>': 571, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6623315', 'n_tokens_mistral': 18127, 'n_tokens_neox': 17814, 'n_words': 3552}