[英]React Testing Library / Jest - Child component not present when rendering parent component
I'm trying to test that a click handler in my child component that is changing the parent component's state and then displaying conditional jsx in my parent component, but I can't figure out the best way to do so and I'm also having trouble debugging.我正在尝试测试我的子组件中的点击处理程序,该处理程序正在更改父组件的状态,然后在我的父组件中显示条件 jsx,但我无法找出最好的方法,而且我也有麻烦调试。 My other tests that test the parent component and child component separately are working (as in I'm able to find dom elements that I expect to be present), but when I try to test the clicking of a button in the child component by rendering the parent component, my test fails.我的其他分别测试父组件和子组件的测试正在运行(因为我能够找到我希望存在的 dom 元素),但是当我尝试通过渲染测试子组件中按钮的点击时父组件,我的测试失败了。
Expected behavior:预期行为:
The functionality is working in the localhost browser, but not in my test, and I can't even find the child component's html at all in my test.该功能在本地主机浏览器中有效,但在我的测试中无效,我什至在我的测试中根本找不到子组件的 html。
Parent component:父组件:
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;
Child component:子组件:
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;
Test:测试:
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();
});
Any advice would be appreciated.任何意见,将不胜感激。
The answer ended up being simply using await before render...答案最终只是在渲染之前简单地使用 await ...
I think it might be because my child component was doing an API call, but I'm not sure why I did not need to use await when rendering and testing the child component separately.我想这可能是因为我的子组件正在执行 API 调用,但我不确定为什么在单独渲染和测试子组件时不需要使用 await。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.