vendredi 12 février 2021

Unit testing a React component that uses both redux and a redirect

I'm having trouble with unit testing my component with React Testing Library. It uses a Redux store and also has a conditional redirect within.

I have found documentation online that helps with both Redux testing and Routing testing, but never have I found anything that combines the two.

Component.jsx

const mapStateToProps = ({toggles}) => ({
    fallback: (toggles.fallback && typeof toggles.fallback === 'object') ? toggles.fallback : null
    redirect: (toggles.redirect && typeof toggles.redirect === 'object') ? toggles.redirect : null
});

const MainComponent = ({fallback, redirect}) => {

    // Fallback if toggle active
    if (fallback) {
        return <FallbackComponent />;
    }

    // Redirect to /login if toggle active
    if (redirect) {
        return <Redirect to={`${process.env.PUBLIC_URL}/login`}/>;
    }

    // If redirecting, show the good component
    return <MyComponent />
}

// Defaults set here for both toggles being false

export default connect(mapStateToProps)(MainComponent);

The redirect URL will take us to load the <MyLogin/> component, and for arguments sake let's say this component is a simple one (pseudocode incoming...):

export default const MyLogin = () => (
    <div>
        <h1 data-testid="loginHello">Log in</h1>
    </div>
);

So for testing purposes, when we redirect, we should be looking for the data-testid, 'loginHello'.

Tests

From previous components I had tested before that include Redux but have no routing involved, I know that we must create a <Provider /> with a store to fake the conditions seen within the component.

I then saw that in order to use routing within the test, we should wrap the test rendered component in a <Router/> with a history in order to make it work...

However it seems nothing I do will work...

const configState = (fallback = false, redirect = false) => ({
    toggles: {
        fallback: fallback,
        redirect: redirect
    }
});

describe('my tests', () => {
    it('should render my redirected component', () => {
        // Create initial state of no fallback, but should redirect
        const state = configState(false, true);
        const store = configureStore();

        const history = createMemoryHistory();

        const rendered = render(
            <Provider store={store(state)}>
                <Router history={history}>
                    <MainComponent />
                </Router>
            </Provider>
        );

        // I would use debug to check visually if I was seeing the correct component
        // rendered.debug();

        expect(rendered.queryByTestId('loginHello')).toBeInTheDocument();
        expect(rendered.queryByTestId('loginHello').textContent).toBe('Set your new password');
    });
});

Update:

I replaced the return <Redirect... part with a simpler to test return of return <div data-testid="hello">REDIRECTING...</div> and adjusted my test such that I was searching for the new testid, and I found that I could see this!

I then added this line to the Login component that we will redirect to and ran the test again, only to receive the error:

TypeError: Cannot read property 'textContent' of null

So it seems that the issue is with the redirect not occurring when I am testing for some reason?

Aucun commentaire:

Enregistrer un commentaire