0

I have a User component defined for a Reddit app like so:

// User.js

import React from "react";
import { useSelector } from "react-redux";
import { selectUsername, selectAuthState } from "./userSlice";
import Reddit from "../../api/Reddit";

const User = () => {
    const userID = useSelector(selectUsername);
    const authState = useSelector(selectAuthState);
    const { isLoading } = useSelector(state => state.user)

    const handleAuth = (e) => {
        e.preventDefault();
        Reddit.getAccessToken();
    }

    if (isLoading) {
        return (
            <svg className="spinner" viewBox="0 0 50 50">
              <circle class="path" cx="25" cy="25" r="20" fill="none" stroke-width="5"></circle>
            </svg>
        )
    } else if (authState) {
        return (
            <a id="reddit-username" target="_blank" rel="noreferrer" href={`https://www.reddit.com/user/${userID}`} aria-label="Current user ID">{userID}</a>
        )
    } else {
        return (
            <a id="reddit-username" href="/" onClick={handleAuth} aria-label="Connect the web app with your Reddit account">Link with Reddit</a>
        )
    }
}

export default User;

So far, the test file for the component is laid out like so:

// User.spec.js

import React from 'react';
import { render } from '@testing-library/react';
import { Provider } from 'react-redux';
import { store } from '../../app/store';
import User from "../../features/User/User";

test("renders 'Link with Reddit' link by default", () => {
  const { getByText } = render( 
    <Provider store={store}>
      <User />
    </Provider>
  );

  expect(getByText("Link with Reddit")).toBeInTheDocument();
});

I'm trying to write a test for rendering the SVG element while the component isLoading, but I can't figure out how as I'm still new to testing in React-Redux. How should I go about getting that done?

1
  • I'd just stick a data-testid attribute on it and query for that using getByTestId Commented Dec 6, 2021 at 17:31

1 Answer 1

3

You can provide redux stores for each test case with different states. So that the selector will return state slices with different values. This allows you to test different branches of code that use these variables.

E.g.

User.jsx:

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

const selectUsername = (state) => state.user.name;
const selectAuthState = (state) => state.auth;

export const User = () => {
  const userID = useSelector(selectUsername);
  const authState = useSelector(selectAuthState);
  const { isLoading } = useSelector((state) => state.user);

  const handleAuth = (e) => {
    e.preventDefault();
  };

  if (isLoading) {
    return (
      <svg className="spinner" viewBox="0 0 50 50">
        <circle className="path" cx="25" cy="25" r="20" fill="none" strokeWidth="5"></circle>
      </svg>
    );
  } else if (authState) {
    return (
      <a
        id="reddit-username"
        target="_blank"
        rel="noreferrer"
        href={`https://www.reddit.com/user/${userID}`}
        aria-label="Current user ID"
      >
        {userID}
      </a>
    );
  } else {
    return (
      <a id="reddit-username" href="/" onClick={handleAuth} aria-label="Connect the web app with your Reddit account">
        Link with Reddit
      </a>
    );
  }
};

User.test.jsx:

import { User } from './User';
import React from 'react';
import { render } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { Provider } from 'react-redux';
import { combineReducers, createStore } from 'redux';

function renderUI(reducers) {
  const rootReducer = combineReducers(reducers);
  const store = createStore(rootReducer);
  return render(
    <Provider store={store}>
      <User />
    </Provider>
  );
}

describe('70249367', () => {
  test('should render spinner', () => {
    const { container } = renderUI({
      user: (state = { name: '', isLoading: true }) => {
        return state;
      },
    });
    expect(container.querySelector('.spinner')).toBeInTheDocument();
  });

  test("renders 'Link with Reddit' link by default", () => {
    const { getByText } = renderUI({
      user: (state = { name: '', isLoading: false }) => {
        return state;
      },
    });
    expect(getByText('Link with Reddit')).toBeInTheDocument();
  });

  test('should render username', () => {
    const { getByText } = renderUI({
      user: (state = { name: 'teresa teng', isLoading: false }) => {
        return state;
      },
      auth: (state = {}) => state,
    });
    expect(getByText('teresa teng')).toBeInTheDocument();
  });
});

Test result:

 PASS  examples/70249367/User.test.jsx (8.809 s)
  70249367
    ✓ should render spinner (27 ms)
    ✓ renders 'Link with Reddit' link by default (7 ms)
    ✓ should render username (3 ms)

----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
----------|---------|----------|---------|---------|-------------------
All files |   94.74 |      100 |      80 |   93.33 |                   
 User.jsx |   94.74 |      100 |      80 |   93.33 | 13                
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       3 passed, 3 total
Snapshots:   0 total
Time:        9.405 s

package versions:

"@testing-library/jest-dom": "^5.11.6",
"@testing-library/react": "^11.2.2",
"jest": "^26.6.3",
"react": "^16.14.0",
"react-dom": "^16.14.0",
"react-redux": "^7.2.2",
"redux": "^4.1.0",
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.