2

What would be the way to test a component that relies on the initial state for conditional rendering ?

For example showLessFlag is dependent on state, and testing state in react-testing-library is counter productive.

so how would i test this condition in the CommentList component

{showLessFlag === true ? (
    // will show most recent comments below
    showMoreComments()
) : (
    <Fragment>
        {/* filter based on first comment, this shows by default */}
        {filterComments.map((comment, i) => (
            <div key={i} className="comment">
                <CommentListContainer ref={ref} comment={comment} openModal={openModal} handleCloseModal={handleCloseModal} isBold={isBold} handleClickOpen={handleClickOpen} {...props} />
            </div>
        ))}
    </Fragment>
)}

Should it be test like the following

  it("should check more comments", () => {
        const { getByTestId } = render(<CommentList {...props} />);
        const commentList = getByTestId("comment-show-more");
        expect(commentList).toBeNull();
    });

But im getting this error because of the conditional rendering

TestingLibraryElementError: Unable to find an element by: [data-testid="comment-show-more"]

CommentList.tsx

import React, { Fragment, useState, Ref } from "react";
import Grid from "@material-ui/core/Grid";
import OurSecondaryButton from "../../../common/OurSecondaryButton";
import CommentListContainer from "../commentListContainer/commentListContainer";

function CommentList(props: any, ref: Ref<HTMLDivElement>) {
    const [showMore, setShowMore] = useState<Number>(2);
    const [openModal, setOpenModal] = useState(false);
    const [showLessFlag, setShowLessFlag] = useState<Boolean>(false);
    const the_comments = props.comments.length;
    const inc = showMore as any;
    const min = Math.min(2, the_comments - inc);
    const showComments = (e) => {
        e.preventDefault();
        if (inc + 2 && inc <= the_comments) {
            setShowMore(inc + 2);
            setShowLessFlag(true);
        } else {
            setShowMore(the_comments);
        }
    };
    const handleClickOpen = () => {
        setOpenModal(true);
    };
    const handleCloseModal = () => {
        setOpenModal(false);
    };

    const showLessComments = (e) => {
        e.preventDefault();
        setShowMore(2);
        setShowLessFlag(false);
    };
    const isBold = (comment) => {
        return comment.userId === props.userId ? 800 : 400;
    };
    // show comments by recent, and have the latest comment at the bottom, with the previous one just before it.
    const filterComments = props.comments
        .slice(0)
        .sort((a, b) => {
            const date1 = new Date(a.createdAt) as any;
            const date2 = new Date(b.createdAt) as any;
            return date2 - date1;
        })
        .slice(0, inc)
        .reverse();

    const showMoreComments = () => {
        return filterComments.map((comment, i) => (
            <div data-testid="comment-show-more" key={i} className="comment">
                <CommentListContainer ref={ref} comment={comment} openModal={openModal} handleCloseModal={handleCloseModal} isBold={isBold} handleClickOpen={handleClickOpen} {...props} />
            </div>
        ));
    };

    return (
        <Grid data-testid="comment-list-div">
            <Fragment>
                <div style={{ margin: "30px 0px" }}>
                    {props.comments.length > 2 ? (
                        <Fragment>
                            {min !== -1 && min !== -2 ? (
                                <Fragment>
                                    {min !== 0 ? (
                                        <OurSecondaryButton onClick={(e) => showComments(e)} component="span" color="secondary">
                                            View {min !== -1 && min !== -2 ? min : 0} More Comments
                                        </OurSecondaryButton>
                                    ) : (
                                        <OurSecondaryButton onClick={(e) => showLessComments(e)} component="span" color="secondary">
                                            Show Less Comments
                                        </OurSecondaryButton>
                                    )}
                                </Fragment>
                            ) : (
                                <OurSecondaryButton onClick={(e) => showLessComments(e)} component="span" color="secondary">
                                    Show Less Comments
                                </OurSecondaryButton>
                            )}
                        </Fragment>
                    ) : null}
                </div>
            </Fragment>
            {showLessFlag === true ? (
                // will show most recent comments below
                showMoreComments()
            ) : (
                <Fragment>
                    {/* filter based on first comment  */}
                    {filterComments.map((comment, i) => (
                        <div key={i} className="comment">
                            <CommentListContainer ref={ref} comment={comment} openModal={openModal} handleCloseModal={handleCloseModal} isBold={isBold} handleClickOpen={handleClickOpen} {...props} />
                        </div>
                    ))}
                </Fragment>
            )}
        </Grid>
    );
}

export default React.forwardRef(CommentList) as React.RefForwardingComponent<HTMLDivElement, any>;

CommentList.test.tsx

import "@testing-library/jest-dom";
import React, { Ref } from "react";
import CommentList from "./CommentList";
import { render, getByText, queryByText, getAllByTestId } from "@testing-library/react";

const props = {
    user: {},
    postId: null,
    userId: null,
    currentUser: {},
    ref: {
        current: undefined,
    },
    comments: [
        {
            author: { username: "barnowl", gravatar: "https://api.adorable.io/avatars/400/bf1eed82fbe37add91cb4192e4d14de6.png", bio: null },
            comment_body: "fsfsfsfsfs",
            createdAt: "2020-05-27T14:32:01.682Z",
            gifUrl: "",
            id: 520,
            postId: 28,
            updatedAt: "2020-05-27T14:32:01.682Z",
            userId: 9,
        },
        {
            author: { username: "barnowl", gravatar: "https://api.adorable.io/avatars/400/bf1eed82fbe37add91cb4192e4d14de6.png", bio: null },
            comment_body: "fsfsfsfsfs",
            createdAt: "2020-05-27T14:32:01.682Z",
            gifUrl: "",
            id: 519,
            postId: 27,
            updatedAt: "2020-05-27T14:32:01.682Z",
            userId: 10,
        },
    ],
    deleteComment: jest.fn(),
};
describe("Should render <CommentList/>", () => {
    it("should render <CommentList/>", () => {
        const commentList = render(<CommentList {...props} />);
        expect(commentList).toBeTruthy();
    });

    it("should render first comment", () => {
        const { getByTestId } = render(<CommentList {...props} />);
        const commentList = getByTestId("comment-list-div");
        expect(commentList.firstChild).toBeTruthy();
    });

    it("should render second child", () => {
        const { getByTestId } = render(<CommentList {...props} />);
        const commentList = getByTestId("comment-list-div");
        expect(commentList.lastChild).toBeTruthy();
    });

    it("should check comments", () => {
        const rtl = render(<CommentList {...props} />);

        expect(rtl.getByTestId("comment-list-div")).toBeTruthy();
        expect(rtl.getByTestId("comment-list-div")).toBeTruthy();

        expect(rtl.getByTestId("comment-list-div").querySelectorAll(".comment").length).toEqual(2);
    });
    it("should match snapshot", () => {
        const rtl = render(<CommentList {...props} />);
        expect(rtl).toMatchSnapshot();
    });

    it("should check more comments", () => {
      const { getByTestId } = render(<CommentList {...props} />);
      const commentList = getByTestId("comment-show-more");
      expect(commentList).toBeNull();
    });
});
1
  • Can you provide the codesandbox reproduction example? That's will be helpful. Commented May 29, 2020 at 4:19

2 Answers 2

8

Any getBy* query in react-testing-library will throw an error if no match is found. If you want to test/assert the absence of an element then you want to use any of the queryBy* queries, they return null if no match is found.

Queries

enter image description here

it("should check more comments", () => {
  const { queryByTestId } = render(<CommentList {...props} />);
  const commentList = queryByTestId("comment-show-more");
  expect(commentList).toBeNull();
});
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you, this got the test passing. Appreciate it, will read more on react-testing-library use cases for situations like this.
0

To better answer this question, being that i have more experience with using react testing library now.

When we go about testing for conditions, we need to trigger the action that makes the change to the state.

For example in this situation

We have a condition like showLessFlag

{showLessFlag === true ? (
    // will show most recent comments below
    showMoreComments()
) : (
    <Fragment>
        {/* filter based on first comment, this shows by default */}
        {filterComments.map((comment, i) => (
            <div key={i} className="comment">
                <CommentListContainer ref={ref} comment={comment} openModal={openModal} handleCloseModal={handleCloseModal} isBold={isBold} handleClickOpen={handleClickOpen} {...props} />
            </div>
        ))}
    </Fragment>
)}

In order to properly test this, we need to trigger the event that will change showLessFlag to false.

So we can do something like

 <OurSecondaryButton
      onClick={(e) => showLessComments(e)}
      data-testid="_test-show-less"
      component="span"
      color="secondary"
    >
      Show Less Comments
 </OurSecondaryButton>

test

it("should trigger showLessComments ", () => {
   const { getByTestId } = render(<CommentList {...props} />);
   const showLessButton = getByTestId("__test-show-less");
   fireEvent.click(showLessButton);
   expect(...) // whatever to be called, check for the existence of a div tag, or whatever you want
});

Testing for conditions improves code coverage :)

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.