Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

shallow().dive() does not work with react-redux Provider #2202

Closed
ThiefMaster opened this issue Jul 25, 2019 · 59 comments
Closed

shallow().dive() does not work with react-redux Provider #2202

ThiefMaster opened this issue Jul 25, 2019 · 59 comments

Comments

@ThiefMaster
Copy link

ThiefMaster commented Jul 25, 2019

it('should work', () => {
  const Component = () => {
    return <div>Hello World</div>;
  };
  const wrapper = shallow(
    <Provider store={mockStore()}>
      <Component />
    </Provider>
  ).dive();
  expect(wrapper).toMatchSnapshot();
});

This is just a simple example - please assume that Component needs to have access to the react-redux context so I can use useSelector() in there, so something like .find(Component).dive() won't work

Current behavior

exports[`<UserMenu /> hopefully works 1`] = `<Component />`;

Expected behavior

exports[`<UserMenu /> hopefully works 1`] = `
<div>
  Hello World
</div>
`;

API

  • shallow

Version

library version
enzyme 3.10.0
react 16.8.6
react-dom 16.8.6
react-test-renderer -
adapter (below) 1.14.0

Adapter

  • enzyme-adapter-react-16
@chan-dra
Copy link

+1 here, doesn't seem to work for me either.

const component = shallow(
      <Provider store={store}>
        <Test {...props} />
      </Provider>
    )
      .find(NavItem)
      .dive();
    expect(component).toMatchSnapshot();

throws this

Invariant Violation: could not find react-redux context value; please ensure the component is wrapped in a <Provider>

@JulianKl
Copy link

Same problem, are there any plans to fix this? Also, does anyone have a workaround that can be used until it is fixed?

@markkra
Copy link

markkra commented Aug 16, 2019

Big corporate app - same issue. If we want to move forward and start using Redux with Hooks we need this fixed. A work around would be great. Right now we only have one component that uses .dive() heavily and if someone figures out a workaround that would be great.

@MikeSpitz
Copy link

Any update on how best to get around this? Or if this will be tackled?

@michielmetcake
Copy link

Same here 💃

@ljharb
Copy link
Member

ljharb commented Sep 10, 2019

Try with the wrappingComponent option?

@idangozlan
Copy link

Same here. seems like enzyme and redux hooks are not getting along.

@KenLui
Copy link

KenLui commented Sep 23, 2019

@idangozlan, shallow() doesn't work with React's useContext hook. If you're using a version of react-redux higher than 5.1.1, you're going to have a bad time dealing with contexts. See this comment for some workarounds.

// Bare component.
const Component = () => {
  return <div>Hello World</div>;
};

// Redux connected component.
const ConnectedComponent = connect()(Component);

it('inject store work', () => {
  // Inject store directly into connected component.
  const wrapper = shallow(
    <ConnectedComponent store={mockStore()} />
  );
  expect(wrapper).toMatchSnapshot();
});

@idangozlan
Copy link

idangozlan commented Sep 23, 2019

@KenLui it's still not helpful, as the component not getting rendered... it's rendering the snapshot as:

<Test store={{...}} dispatch={[Function: dispatch]} />

@ljharb
Copy link
Member

ljharb commented Sep 23, 2019

enzyme doesn't have snapshot support, nor recommend snapshot testing - the closest thing is .debug() (or .debug({ verbose: true })). If you're having trouble with snapshots, you'll need to file an issue on whatever is generating them.

@idangozlan
Copy link

right, but even when running .debug() it's rendering the result above, shouldn't it render the Test component with its markup?

@ljharb
Copy link
Member

ljharb commented Sep 23, 2019

@idangozlan with shallow, you need one .dive per HOC - so since you have connect(), you need wrapper.dive().debug() to see the contents of your inner component.

@idangozlan
Copy link

@ljharb make sense, but then @KenLui is worthless. getting Invariant Violation: could not find react-redux context value; please ensure the component is wrapped in a <Provider>. :)

Hopefully, we will have a solution to this issue soon.

@ljharb
Copy link
Member

ljharb commented Sep 23, 2019

@idangozlan that's why i suggested using the wrappingComponent option:

const wrapper = shallow(<ComponentThatNeedsContext>, {
  wrappingComponent: ContextProvider
});
wrapper.debug()

@idangozlan
Copy link

idangozlan commented Sep 24, 2019

@ljharb Still happening (Invariant Violation: could not find react-redux context value; please ensure the component is wrapped in a <Provider>).

The sample code is:

Test.js:

import React from 'react';
import { useSelector } from 'react-redux';

export default function Test() {
  const user = useSelector(state => state.auth.user);

  console.log(user);
  // const user = 'test user';
  // console.log(user);

  return (
    <div className="D">
      <div className="C">
        <div className="B">
          <div className="A">
My Test
            {user || ''}
          </div>
        </div>
      </div>
    </div>
  );
}

The test:

import React from 'react';
import PropTypes from 'prop-types';
import { shallow } from 'enzyme';
import { Provider } from 'react-redux';
import configureStore from 'redux-mock-store';

import Test from './Test';

const mockStore = configureStore([]);
const defaultStore = mockStore({
  auth: { user: 'Idan' },
});

function MyProvider(props) {
  const { children } = props;

  return (
    <Provider store={props.store}>
      {children}
    </Provider>
  );
}

MyProvider.propTypes = {
  children: PropTypes.node.isRequired,
  store: PropTypes.object,
};
MyProvider.defaultProps = {
  store: defaultStore,
};

describe('My Desc', () => {
  it('My Test', () => {
    expect.assertions(1);

    const wrapper = shallow(<Test />, {
      wrappingComponent: MyProvider,
    });
    // const provider = wrapper.getWrappingComponent();
    // provider.setProps({ store: defaultStore });

    console.log(wrapper.debug());
    expect(1).toEqual(1);
  });
});

@ThiefMaster
Copy link
Author

Indeed, I tried it myself again and even without snapshots etc, the problem is that wrappingComponent doesn't seem to work with useContext (which react-redux uses internally)...

@ljharb
Copy link
Member

ljharb commented Sep 25, 2019

Yes, that's very true. React hooks don't provide any hooks for testing purposes, so there's nothing for enzyme to interact with. You'd have to use the non-hook version of react-redux to be able to test it properly.

@idangozlan
Copy link

idangozlan commented Sep 25, 2019

@ljharb do you expect any feature like that will be available in the future?

@ljharb
Copy link
Member

ljharb commented Sep 25, 2019

@idangozlan i certainly hope so - but it would require react to expose API hooks into how hooks work, and/or to implement support for such things in the shallow renderer.

@Albert-Gao
Copy link

Hi, @ljharb

How to invoke the .shallow() when chaining?

			const wrapper = shallow(
				<MockProvider>
					<LottoPyoContainer {...props} />
				</MockProvider>
			);

			console.log(
				wrapper
					.dive()
					.find('Connect(EcommerceBarContainer)')
					.shallow({ wrappingComponent: MockProvider }) // error on this line
					.debug()
			);

What happens here is:

The .find() gives me this:

    <Connect(EcommerceBarContainer) instructionText={{...}} totalCost={1}>
      <EcommerceBarLottoContent lineCountLotto={[undefined]} />
    </Connect(EcommerceBarContainer)>

Now I want to render the <Connect(EcommerceBarContainer)>

But .shallow({ wrappingComponent: MockProvider }) throws an error:

Could not find "store" in the context of "Connect(EcommerceBarContainer)". Either wrap the root component in a <Provider>, or pass a custom React context provider to <Provider> and the corresponding React context consumer to Connect(EcommerceBarContainer) in connect options.

So, my question is: how to invoke the chaining shallow() with the wrapping component, seems the top level <MockProvider> has been thrown when .dive()

@sunilb0575
Copy link

Any solution for this issue, out there?

@sunnyofhell
Copy link

sunnyofhell commented Dec 9, 2019

Same Problem. Found a temporary solution though

  1. While testing use the older version of the library.
  2. There will be 2 versions of react-redux in your project, second older version installed as a dev dependency.
  3. Use jest.mock to point to this file while testing.

https://stackoverflow.com/questions/59191129/enzyme-jest-react-testing-shallow-connected-component-with-react-redux-6

@jonluca
Copy link

jonluca commented Feb 12, 2020

Bump on this issue - related to #2174

Currently looking like theres no way to shallow render when using the useContext hook with enzyme. Has there been any progress on this?

@vojtatranta
Copy link

Hey,
just came across same issue. I concluded that for test purposes injecting store directly to the connected component is OK. The only problem is Typescript typing because types do not allow passing store directly as a prop. Otherwise, it is OK.

@christopher-francisco
Copy link

christopher-francisco commented Feb 20, 2020

@vojtatranta how do inject it when the connected component is actually a child?

const wrapper = shallow(<MyComp />);

connst connected = wrapper.find('Connect(Something)');

connected.dive()                                         // doesnt work
connected.dive({ context: { store } })        // doesnt work
connected.shallow()                                   // doesnt work
connected.shallow({ context: { store } })  // doesnt work

@vojtatranta
Copy link

@chris-fran pass the store as prop to connect HOC.

@stella1013
Copy link

I was having issues with TypeScript and React-Redux Provider. I don't know if this helps anyone but I was able to get a DOM snapshot via the below code.

library version
enzyme 3.11.0
react 16.12.0
react-redux 7.1.7
react-dom 16.12.0
react-test-renderer 16.9.2
enzyme-adapter-react-16 1.15.2
redux-mock-store 1.5.4
typescript 3.7.2

testUtils.ts

export const storeFactory = (initialState: any) => {
	return createStore(rootReducer, initialState);
};

test.tsx

const connectedSetup = (initialState={}) => {
    const store = storeFactory(initialState);
    const wrapper = shallow(<ConnectedSplashScreen store={store}/>).dive().dive();
	console.log(wrapper.debug({verbose:true}));
	return wrapper;
};

Though I do receive a Type error on 'store' on this line <ConnectedSplashScreen store={store}/>

(JSX attribute) store: Store<CombinedState<{
    clientState: ClientState;
    myForm: FormState;
    data: DataState;
}>, setVoucherUrlAction | ... 22 more ... | dataSetAction>
Type '{ store: Store<CombinedState<{ clientState: ClientState; myForm: FormState; data: DataState; }>, setVoucherUrlAction | ... 22 more ... | dataSetAction>; }' is not assignable to type 'IntrinsicAttributes & Pick<ReduxType, never>'.
  Property 'store' does not exist on type 'IntrinsicAttributes & Pick<ReduxType, never>'.ts(2322)

@Quirksmode
Copy link

Same issue for me, such a shame as replacing connect with the hooks removes so much boilerplate

@Ba-stian
Copy link

For me using mount return undefined in snapshot too

@ljharb
Copy link
Member

ljharb commented Apr 14, 2020

provider.setProps() won't likely work without a container.update() after it.

@ljharb
Copy link
Member

ljharb commented Apr 14, 2020

Separately, enzyme doesn't support or recommend using snapshot testing; the closest is .debug().

@Ba-stian
Copy link

I add container.update() below provider.setProps()
and I still get undefined

    TypeError: Cannot read property 'debug' of undefined

      46 | 
      47 |     it('should render component without crashing', () => {
    > 48 |         console.log(container.debug())
         |                               ^
      49 |     });
      50 |     it('should find button in component', () => {
      51 |         expect(container.find('.button').length).toEqual(1);

in second test

it('should find button in component', () => {
        expect(container.find('.button').length).toEqual(1);
    })

I get this

    could not find react-redux context value; please ensure the component is wrapped in a <Provider>

       9 | const ReportButton = ({fullReport, dataId, count, type}) => {
      10 | 
    > 11 |     const period_tariff_is_active = useSelector(state => state.auth.user && state.auth.user.period_tariff_is_active);
         |                                     ^
      12 |     const lang = useSelector(state => state.auth.user && state.auth.user.lang);
      13 |     const filter = useSelector(state => state.selectFilter);
      14 |     const loading = useSelector(state => state.loading.loading);

@renjithspace
Copy link

@idangozlan that's why i suggested using the wrappingComponent option:

const wrapper = shallow(<ComponentThatNeedsContext>, {
  wrappingComponent: ContextProvider
});
wrapper.debug()

Using wrappingComponent is got worked 👍

@anosov-otg
Copy link

@renjithspace could you please explain that code snippet. What is ContextProvider in this case?

@renjithspace
Copy link

renjithspace commented Sep 15, 2020

@anosov-otg for example: An HOC of React redux provider

import { Provider } from 'react-redux'

@ljharb
Copy link
Member

ljharb commented Sep 18, 2020

Closing; if wrappingComponent doesn't work for anyone, please file a new issue.

@ljharb ljharb closed this as completed Sep 18, 2020
@anosov-otg
Copy link

wrappingComponent doesn't work for me, but these options do: #1002 (comment)

@ljharb
Copy link
Member

ljharb commented Sep 18, 2020

@anosov-otg the first and third are fine, I'd expect the second to work with wrappingComponent as the Provider instead.

@dschinkel
Copy link

dschinkel commented Sep 19, 2020

const wrapper = shallow(<ComponentThatNeedsContext>, {
  wrappingComponent: ContextProvider
});

what about provider with store..where do you put send in a store above? Unless I'm missing some context :) how is this allowing us to send in a store?

If this is not what you were thinking please elaborate:

import { Provider } from "react-redux";

const wrapper = shallow(<ComponentThatNeedsContext>, {
  wrappingComponent: Provider
});

@ljharb
Copy link
Member

ljharb commented Sep 19, 2020

@dschinkel you'd use the wrappingComponentProps option to supply props to the Provider.

@dschinkel
Copy link

dschinkel commented Sep 19, 2020

tried it, I get Cannot read property 'getState' of undefined

import { Provider } from "react-redux";
const store = createStore(rootReducer, initialState, middleware);
it('contains expected actions', () => {
  const homePage = shallow(<HomePageContainer />, {
    wrappingComponent: Provider,
    wrappingComponentProps: store
  });

  expect(homePage.props().fetchFeaturedCompanies).to.exist;
  expect(homePage.props().fetchCompanies).to.exist;
});

I won't derail this and can open another issue if need be.

Is this something new? I wasn't one to use Provider a lot in the past because I sent in my store via props.

@ljharb
Copy link
Member

ljharb commented Sep 19, 2020

wrappingComponentProps needs to be a props object. try { store }.

@dschinkel
Copy link

dschinkel commented Sep 19, 2020

referring to this for further discussion 2449

@renjithspace
Copy link

renjithspace commented Sep 23, 2020

@dschinkel @anosov-otg We can tackle this in different ways.

Solution 1:

Create an HOC for your provider, here is an example with redux:

// StoreProvider.js

import { Provider } from 'react-redux'
import store from 'path/to/store'

function StoreProvider() {
  return (
    <Provider store={store}>
      {this.props.children}
    </Provider>
  )
}

And use that in tests with wrappingComponent:

// ComponentThatNeedsProvider.test.js

import { shallow } from 'enzyme'
import StoreProvider from 'path/to/StoreProvider'
import ComponentThatNeedsProvider from 'path/to/ComponentThatNeedsProvider'

const wrapper = shallow(<ComponentThatNeedsProvider>, {
  wrappingComponent: StoreProvider
});

Solution 2:

You can directly pass provider props with wrappingComponentProps like below without creating an HOC for provider as @ljharb mentioned.

// ComponentThatNeedsProvider.test.js

import { shallow } from 'enzyme'
import { Provider } from 'react-redux'
import store from 'path/to/store'
import ComponentThatNeedsProvider from 'path/to/ComponentThatNeedsProvider'

const wrapper = shallow(<ComponentThatNeedsProvider>, {
  wrappingComponent: Provider,
  wrappingComponentProps: { store }
});

If you prefer DRY or if you have multiple providers with a bunch of props to pass, go with Solution 1 👍

@dschinkel
Copy link

dschinkel commented Sep 23, 2020

@stella1013
here's an answer to that error you got about TS bitching about the store prop from tests and how to resolve it. I mention it toward the end. And this is exactly why I hate TS, it's a pain in the ass overall to have to deal with this stuff.

https://medium.com/@DaveSchinkel/setting-up-mocha-js-to-run-typescript-42f3099450eb

@dschinkel
Copy link

dschinkel commented Sep 23, 2020

@renjithspace

yea I have tried that but no luck with wrappingComponentProps: { store } and in fact I've given up entirely on the wrappingComponent route, it just doesn't work for any of my tests for different use cases period including using Router from react-router to wrap components under test. I don't trust it...it just doesn't work for me period in any cases I've tried so far.

I ended up with this working for me (had to add a childAt() oddly enough) due to how react-redux is structured these says in v7:

it('fetches featured companies', async () => {
  const companies: Array<Company> = [companyStub]
  mockGet('/api/v1/companies/featured', companies);
  const homePage = shallow(<HomePageContainer store={store} />);

  await homePage.childAt(0).props().fetchFeaturedCompanies();
  const newState = store.getState();

  expect(newState.companies.companies).to.not.exist;
  expect(newState.companies.featuredCompanies).to.exist;
});

redux v7 re-introduced the ability to pass store as a prop directly onto React components (as I like it).

@ajGingrich
Copy link

The wrapping component only works for me when I use a full mount.

const wrapper = mount(<MyComponent />, {
    wrappingComponent: Provider,
    wrappingComponentProps: { store },
})

works but when using shallow it doesn't. That defeats the whole purpose, I could just do a full mount on the provider in the first place which is want I want to avoid.

@ljharb
Copy link
Member

ljharb commented Jan 25, 2021

@ajGingrich i agree; the goal is that it works with shallow. If you're still having that trouble, it'd be great if you filed a new issue about it so we could try to work through it.

@marquezmanu
Copy link

I will leave my experience here it might help any folk with a similar project setup.

In the project I work, we are using enzyme v3.11.0, react-redux v7.2.4 and typescript v4.2.4. The problem we had was with the tests for a connected component, when wrapping the component with the react-redux Provider, the shallow output was not the expected one, and when trying to dive into it, again the results was not the expected ones, similar of what it is described for several folks above.
The solution for us was to pass directly to the Connected component the fake store, and not wrap it with the react-redux Provider or any custom provider, with that typescript was yelling at us since store was not a valid prop, so to fix that we included in the typescript config this little snippet of code.

  • In the file tsconfig.json include the following snippet.
declare namespace JSX {
  interface IntrinsicAttributes {
    store?: unknown;
  }
}

With that now typescript is happy and we as well.

And below a small example of how we do the shallow test for the connected components.

import configureMockStore from 'redux-mock-store';
const mockStore = configureMockStore();
const storeFake = mockStore({ foo: 'bar' });

it('should render the wrapped component with the connected properties', () => {
  const wrapper = shallow(<Component store={storeFake} />);
  expect(toJson(wrapper)).toMatchInlineSnapshot(`
  <ContextProvider
    value={null}
  >
    <Component
      dispatch={[Function]}
      foo="bar"
      store={
        Object {
          "clearActions": [Function],
          "dispatch": [Function],
          "getActions": [Function],
          "getState": [Function],
          "replaceReducer": [Function],
          "subscribe": [Function],
        }
      }
    />
  </ContextProvider>
  `);
}

Note: Not a native english speaker sorry for any typo or for a bad expressed idea. Please quote me if it is needed a bit of more explanation.

Please check this post to have a more detailed explanaition on how to include the ts type extension in your project.

@coskunuyar
Copy link

I had the same problem using dive twice solved my issues.Thats interesting.

const setup = (initialState = {}) => { const store = storeFactory(initialState); return shallow(<Input store={store} />) .dive() .dive(); };

@ljharb
Copy link
Member

ljharb commented Aug 6, 2021

@coskunuyar one dive per HOC, yes.

@joan-saum
Copy link

How come this issue has still no solution? React hooks has been introduced in 2019, and it is still not possible to access the redux store with react hooks during a shallow render..

@ljharb
Copy link
Member

ljharb commented Dec 17, 2021

@joan-saum and it will still have no solution in 2119, since it’s blocked on the react team providing hooks for testing/reaching into hooks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests