React Testing Library / Jest -渲染父组件时不存在子组件



我正试图测试我的子组件中的单击处理程序,该处理程序正在改变父组件的状态,然后在父组件中显示条件jsx,但我无法找出最好的方法来这样做,我也有调试困难。我的其他测试分别测试父组件和子组件正在工作(因为我能够找到我期望出现的dom元素),但是当我试图通过呈现父组件来测试子组件中的按钮单击时,我的测试失败了。

预期行为:

  1. 用户点击className为'open-comparison-btn'的div
  2. 子组件调用props。setmodalusing 'true'函数
  3. 父组件modalshow状态更新为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 before render…

我认为这可能是因为我的子组件正在做一个API调用,但我不确定为什么我不需要在渲染和测试子组件时分别使用await。

相关内容

最新更新