簡體   English   中英

React 測試庫/Jest - 呈現父組件時不存在子組件

[英]React Testing Library / Jest - Child component not present when rendering parent component

我正在嘗試測試我的子組件中的點擊處理程序,該處理程序正在更改父組件的狀態,然后在我的父組件中顯示條件 jsx,但我無法找出最好的方法,而且我也有麻煩調試。 我的其他分別測試父組件和子組件的測試正在運行(因為我能夠找到我希望存在的 dom 元素),但是當我嘗試通過渲染測試子組件中按鈕的點擊時父組件,我的測試失敗了。

預期行為:

  1. 用戶單擊類名為“open-comparison-btn”的 div
  2. 子組件使用 'true' 調用 props.setModalShowing 函數
  3. 父組件modalShowing狀態更新為true
  4. 父組件重新渲染並顯示條件 jsx className 'comparison-modal'

該功能在本地主機瀏覽器中有效,但在我的測試中無效,我什至在我的測試中根本找不到子組件的 html。

父組件:

import React, { useState, useEffect } from 'react';
import ProductCard from './ProductCard.jsx';

const RelatedProducts = (props) => {
  const [position, setPosition] = useState(0);
  const componentName = 'RelatedProducts';
  const [modalShowing, setModalShowing] = useState(false);
  const [currentProduct, setCurrentProduct] = useState({});
  const [comparisonProduct, setComparisonProduct] = useState({});

  useEffect(() => {
    setCurrentProduct(props.currentProduct);
  }, [props.currentProduct]);

  const getFeatures = () => {
    return [...currentProduct.features, ...comparisonProduct.features]
      .filter((v, i, a)=>a.findIndex(v2=>(v.feature === v2.feature && v.value === v2.value)) === i);
  };

  return (
    <>
      <div className='related-products-container' role="listbox" aria-label="related products" style={{marginLeft: `-${position}px`}}>
        {props.relatedProducts ?
          props.relatedProducts.map((product) => {
            return <ProductCard
              modalShowing={modalShowing}
              setModalShowing={setModalShowing}
              setComparisonProduct={setComparisonProduct}
              key={product.id}
              product={product}
              generateStars={props.generateStars}
              isFetching={props.isFetching}
              setIsFetching={props.setIsFetching}
              parentComponent={componentName}
              yourOutfit={props.yourOutfit}
              addToOutfit={props.addToOutfit}
            />;
          })
          : null
        }

      </div>
      <div className='fade-top'>
        { position > 0 ?
          <div className="arrow-container-left" role="button" aria-label="scroll left" onClick={() => { setPosition(position - 250); }}>
            <div className="arrow-left"></div>
          </div>
          : null
        }
        { props && props.relatedProducts && position <= (props.relatedProducts.length - 4) * 250 ?
          <div className="arrow-container-right" role="button" aria-label="scroll right" onClick={() => { setPosition(position + 250); }}>
            <div className="arrow-right"></div>
          </div>
          : null
        }
      </div>
      {modalShowing ?
        <div className='comparison-modal' role='dialog' aria-label='comparison window'>
          <div className='modal-top'>COMPARING</div>
          <div className='modal-product-names'>
            <div className='product-1'>{currentProduct.name}</div>
            <div className='product-2'>{comparisonProduct.name}</div>
          </div>
          <table className='modal-table'>
            <tbody>
              {getFeatures().map((feature, index) => {
                return (
                  <tr key={`${feature}-${index}`}>
                    <td className='left-check'>{currentProduct.features.filter(item => item.feature === feature.feature && item.value === feature.value).length > 0 ? '✓' : null}</td>
                    <td>{feature.value} {feature.feature}</td>
                    <td className='right-check'>{comparisonProduct.features.filter(item => item.feature === feature.feature && item.value === feature.value).length > 0 ? '✓' : null}</td>
                  </tr>
                );
              })}
            </tbody>
          </table>
          <div className="close-btn" onClick={() => { setModalShowing(false); }}></div>
        </div>
        : null}
    </>
  );
};

export default RelatedProducts;

子組件:

import React, { useState, useEffect } from 'react';
import ratingsAPI from '../../API/Ratings.js';
import { useNavigate } from 'react-router-dom';

const ProductCard = (props) => {
  const navigate = useNavigate();
  const [averageRating, setAverageRating] = useState();
  const stars = props.generateStars(averageRating, 'related');

  useEffect(() => {
    ratingsAPI.getReviewMetadata(props.product.id)
      .then((metadata) => {
        setAverageRating(getAverageRating(metadata.ratings));
        props.setIsFetching(false);
      });
  }, []);

  const routeChange = () => {
    const path = `/${props.product.id.toString()}`;
    navigate(path);
  };

  const displayComparison = (e) => {
    props.setComparisonProduct(props.product);
    props.setModalShowing(true);
    e.stopPropagation();
  };

  const getAverageRating = (ratings) => {
    var sum = 0;
    var count = 0;
    Object.keys(ratings).forEach(function(rating) {
      sum += rating * parseInt(ratings[rating]);
      count += parseInt(ratings[rating]);
    });
    return sum / count;
  };

  return (
    !props.isFetching ?
      <>
        <div className='product-card-container' onClick={() => routeChange(props.product.id)}>
          <img className='product-card-image' src={props.product.styles.results[0].photos[0].thumbnail_url}>
          </img>
          {props.parentComponent === 'RelatedProducts'
            ?
            <svg className="open-comparison-btn" role='button' aria-label='open comparison' width="20px" height="20px" viewBox="0 0 32 32" onClick={(e) => { displayComparison(e); }}>
              <path fill="White" stroke="black" strokeWidth="2px" d="M20.388,10.918L32,12.118l-8.735,7.749L25.914,31.4l-9.893-6.088L6.127,31.4l2.695-11.533L0,12.118
            l11.547-1.2L16.026,0.6L20.388,10.918z"/>
            </svg>
            :
            <div className="close-btn" onClick={() => { props.removeFromOutfit(props.product); }}></div>
          }

          <div className='product-card-description'>
            <div className='product-card-category'>{props.product.category}</div>
            <div className='product-card-name'>{props.product.name}</div>
            <div className='product-card-price'>${props.product.default_price}</div>
            <div className='product-card-stars'>{ stars }</div>
          </div>
        </div>
      </>
      : null
  );
};

export default ProductCard;

測試:

  it('tests that clicking the open-comparison-btn opens the modal window', async () => {
    render(<RelatedProducts
      addToOutfit={() => { return; }}
      yourOutfit={() => { return; }}
      relatedProducts={relatedProducts}
      generateStars={ generateStars }
      isFetching={() => { return false; }}
      setIsFetching={() => { return; }}
    />, {wrapper: Router});
    fireEvent(
      screen.getByRole('button', {name: 'open comparison'}),
      new MouseEvent('click', {
        bubbles: true,
        cancelable: true,
      }),
    );
    const modal = screen.getByRole('dialog', {name: 'comparison window'});
    expect(modal).toBeInTheDocument();
  });

任何意見,將不勝感激。

答案最終只是在渲染之前簡單地使用 await ...

我想這可能是因為我的子組件正在執行 API 調用,但我不確定為什么在單獨渲染和測試子組件時不需要使用 await。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM