How do I sync tests in React?

Viewed 42

I have CommentsList component which displays a list with all the comments. Each comment has a 'Reply to' button that opens the AddComment component (this component allows me to add a reply to a comment). To display the AddComment component for each comment, I used an array of states.

The AddComment component contains a text area for the input, a cancel button and a submit button. When I click on the submit button and the reply is added successfully, the AddComment component closes. If the input is empty and I click on the submit button, the component doesn't close because the input can't be empty in order to be submitted successfully. I want to test this functionality so that I can verify if the AddComment component disappears after I post a reply.

The problem is that in tests my AddComment component doesn't disappear when I click on the submit button. What I noticed is that the comment is added succesfully, but the state of the AddComment component for the comment isn't changed. When I click on submit button the input is submitted, but the function that changes the state is never called. I think the problem might be the fact that the actions don't synchronize.

I tried to use await act to render CommentsList component to make sure that the test run closer to how React works in the browser, but my AddComment component still doesn't disappear.

Here's my CommentsList component

function CommentsList(props) {
  const { t } = useTranslation();
  const [hasReplyCommentBox, setHasReplyCommentBox] = useState([]);

  function toggleHasReplyComment(commentIndex) {
    var auxState = { ...hasReplyCommentBox };
    auxState[commentIndex] = auxState[commentIndex] ? 0 : 1;
    setHasReplyCommentBox(auxState);
  }

  function replyToCommentButton(commentIndex) {
    return [
      <span
        id={"replyButton-" + commentIndex}
        onClick={() => toggleHasReplyComment(commentIndex)}>
        {t('Reply to')}
      </span>
    ];
  }

  function commentReplyBox(commentIndex, parentCommentId) {
    return hasReplyCommentBox[commentIndex]
      ?
      <AddComment
        id={props.codeId}
        parentCommentId={parentCommentId}
        commentIndex={commentIndex}
        toggleHasReplyComment={toggleHasReplyComment}
        updateComments={props.updateComments}
      />
      :
      null
  }

  return (
    <Layout>
      <Layout>
        <List
          itemLayout="horizontal"
          dataSource={props.comments}
          renderItem={(comment, commentIndex) => (
            <List.Item>
              <CommentCard
                userId={comment.user_id}
                datePosted={comment.date_posted}
                body={comment.body}
                actions={replyToCommentButton(commentIndex)}
                children={commentReplyBox(commentIndex, comment.id)}
              />
            </List.Item>
          )}
        />
        <AddComment
          id={props.codeId}
          updateComments={props.updateComments}
        />
      </Layout>
    </Layout>
  );
}

Here's my AddComment component

function AddComment(props) {
  const { t } = useTranslation();
  const { TextArea } = Input;
  const [form] = Form.useForm();
  const [comment, setComment] = useState();
  const [onCommentAddSuccess, setOnCommentAddSuccess] = useState(0);

  const buttonStyle = {
    float: 'right'
  };

  function onCommentChange(newComment) {
    setComment(newComment.target.value);
  }

  function updateOnCommentAddSuccess(onCommentAddSuccess) {
    setOnCommentAddSuccess(onCommentAddSuccess + 1);
  }

  function resetCommentInput() {
    setComment('');
  }

  function onFormReset() {
    form.resetFields();
  }

  function toggleHasReplyCommentOnPost(parentCommentId, commentIndex) {
    if (parentCommentId !== undefined) {
      console.log('comentariu adaugat cu succes');
      props.toggleHasReplyComment(commentIndex);
    }
  }

  function submitComment() {
    let request = {
      body: comment,
      code_id: props.id,
      parent_comment_id: props.parentCommentId
    };
    fetch('/api/comment/add',
      {
        method: 'POST',
        body: JSON.stringify(request)
      }
    ).then(response => response.json())
    .then(data => {
      if (data.success === 1) {
        updateOnCommentAddSuccess(onCommentAddSuccess);
        props.updateComments(onCommentAddSuccess);
        resetCommentInput();
        toggleHasReplyCommentOnPost(props.parentCommentId, props.commentIndex);
      }
    });
  }

  return (
    <>
      <Form form={form} name="comment" className="comment-form"
        onFinish={submitComment}>
        <Form.Item name="body" label={t('Comment')}>
          <TextArea placeholder={t('Leave a comment')}
            onChange={onCommentChange}
            id={"parent-comment-" + props.parentCommentId} />
        </Form.Item>
        <Form.Item style={buttonStyle}>
          <Space>
            {props.parentCommentId
              ?
              <Button id={"cancelAddReplyComment-" + props.parentCommentId}
                  type="secondary" className = "comment-form-button"
                  onClick={
                    () => props.toggleHasReplyComment(props.commentIndex)
                  }>
                  {t('Cancel')}
              </Button>
              :
              null
            }
            <Button type="primary" htmlType="submit"
              className = "comment-form-button"
              id={"post-comment-button-" + props.parentCommentId}
              onClick={onFormReset}>
              {t('Post')}
            </Button>
          </Space>
        </Form.Item>
      </Form>
    </>
  );
}

And here's how my test looks like

test ('Toggle displaying add reply to comments', async () => {
  const comments = [
    {
      id: 'ID-1',
      user_id: 'USER-ID-1',
      date_posted: '2020-01-01 01:00:00',
      body: 'First comment'
    }
  ];

  await act(async () => {
    Promise.resolve(render(
      <CommentsList comments={comments} />, container
    ));
  });

  // Open AddComment component
  const replyButton = container.querySelector("#replyButton-0");
  await fireEvent.click(replyButton);
  
  // Insert input in the text area
  const userInput = container.querySelector("#parent-comment-ID-1");
  await userEvent.type((userInput), 'reply');
  
  // Submit the input
  const postButton = container.querySelector("#post-comment-button-ID-1");
  await fireEvent.click(postButton);
  
  // Check if the AddComment component is closed
  expect(container.querySelector("#cancelAddReplyComment-ID-1")).toBeFalsy();
});
0 Answers
Related