Reputation: 35
I'm struggling to get my 2nd React test to pass.
import logo from './logo.svg';
import './App.css';
import {useState} from 'react';
import {Link, Route, Routes} from "react-router-dom";
import Home from "./pages/home.js";
import Products from './pages/products.js';
import { HashRouter } from 'react-router-dom';
import Nav from './nav.js'
import './MaterialDesign-Webfont-master/css/materialdesignicons.css';
function App() {
const productArray = []
productArray[0] = new Image()
productArray[0].src = 'imgs/smartphone-one.jpg'
productArray[0].dataset.key = '0'
productArray[0].alt = 'Blueberry 5000 S'
productArray[0].dataset.price = '£499.99'
productArray[1] = new Image ()
productArray[1].src = 'imgs/smartphone-two.jpg'
productArray[1].dataset.key = '1'
productArray[1].alt = 'Banana 12'
productArray[1].dataset.price = '£999.99'
productArray[2] = new Image()
productArray[2].src = 'imgs/smartphone-three.jpg'
productArray[2].dataset.key = '2'
productArray[2].alt = 'Meg 3'
productArray[2].dataset.price = '£799.99'
productArray[3] = new Image ()
productArray[3].src = 'imgs/smartphone-four.jpg'
productArray[3].dataset.key = '3'
productArray[3].alt = 'Communicator 5000'
productArray[3].dataset.price = '£399.99'
productArray[4] = new Image ()
productArray[4].src = 'imgs/smartphone-five.jpg'
productArray[4].dataset.key = '4'
productArray[4].alt = 'Simplex 2'
productArray[4].dataset.price = '£99.99'
return (
<HashRouter>
<Nav/>
<div>
<Routes>
<Route path="/" element={<Home/>} />
<Route path="/products" element={<Products
prods={productArray}/>}
/>
</Routes>
</div>
</HashRouter>
)
}
export default App;
import React from 'react-dom'
function Products({prods}) {
return (
<>
<h2 className="products-title">Products</h2>
<div className="products" data-testid="products-div">
{prods.map(prod => {
return <div className="product" style = {{
backgroundImage: `url("${prod.src}")`,
width: 400,
height: 400,
display: 'flex',
flexFlow: 'column nowrap',
alignItems: 'center',
backgroundRepeat: 'no-repeat',
backgroundPosition: 'top',
backgroundSize: 'contain',
}} id = {prod.dataset.key} >
<p className="prod-copy">{prod.alt}</p>
<p className="prod-price">{prod.dataset.price}</p>
<button className="add">Add to cart</button>
</div>
})}
</div>
</>
)
}
export default Products;
import { render, screen } from '@testing-library/react';
import App from './App';
import React from "react";
import Products from './App'
test('Product in nav', () => {
render(<App />)
expect(screen.getByText('Products')).toBeInTheDocument()
});
test('phones', () => {
render(<App />)
expect(screen.getByText('£499,99')).toBeInTheDocument()
});
The first test passes, it gets the string 'Products' from the nav bar. There is a div lower down in the DOM with heading 'Products' also - but it doesn't find this when I test by dataset-id - the same with how the second test fails. I've searched high and low but cannot find a solution. I've tried async and await.
Most interestingly, when I do screen.debug(), it doesn't seem to be picking up my <Products /> component, it's like it's not rendered in the DOM when it is I can see all the content on the page loaded. It picks up the <Home/> component though which loads in the <Routes> - is it something to do with this?
For the 2nd test to pass
Upvotes: 3
Views: 131
Reputation: 202618
Generally speaking, node.js test environments are not DOM environments, and since the App
component is rendering the router, you've no control over what the current URL is to set the path to "/products"
so the Products
component is rendered.
You've a couple options:
Unit test Products
correctly in isolation.
test('phones', () => {
const productArray = [.......];
render(<Products prods={productArray}/>);
expect(screen.getByText('£499,99')).toBeInTheDocument();
});
Refactor the App
component so it's easier to provide a different router for testing purposes. Basically this means removing the HashRouter
from the App
component.
import logo from './logo.svg';
import './App.css';
import { useState } from 'react';
import { Link, Route, Routes } from "react-router-dom";
import Home from "./pages/home.js";
import Products from './pages/products.js';
import Nav from './nav.js'
import './MaterialDesign-Webfont-master/css/materialdesignicons.css';
function App() {
const productArray = []
...
return (
<>
<Nav />
<div>
<Routes>
<Route path="/" element={<Home />} />
<Route
path="/products"
element={<Products prods={productArray} />}
/>
</Routes>
</div>
</>
)
}
export default App;
In the actual app code you still import the HashRouter
and wrap App
in order to provide a routing context.
import { HashRouter } from 'react-router-dom';
...
<HashRouter>
<App />
</HashRouter>
For unit testing purposes import and use the MemoryRouter
and provide the initial routes and the current route index.
import { render, screen } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import App from './App';
import React from "react";
import Products from './App';
test('Product in nav', () => {
render(
<MemoryRouter initialEntries={["/products"]}>
<App />
</MemoryRouter>
);
expect(screen.getByText('Products')).toBeInTheDocument();
});
test('phones', () => {
render(<MemoryRouter initialEntries={["/products"]}>
<App />
</MemoryRouter>
);
expect(screen.getByText('£499,99')).toBeInTheDocument();
});
Upvotes: 0