Reputation: 31
I want to test a React functional component for a currency exchange list item. This component should show also a flag image and a currency code. The UI gets the currency code from the component's props (currencyCode
) and the image alt text from a state slice (imgAlt
).
Component:
import React from "react";
import { useSelector } from "react-redux";
import classes from "./FXPair.module.css";
const FXPair = ({ currencyCode }) => {
const fxPair = Object.values(useSelector((state) => state.fxPairs)).filter((pair) => pair.currency === currencyCode)[0];
const [fXRate, setFXRate] = React.useState(1.0);
const [flagImgSrc, setFlagImgSrc] = React.useState("");
const [imgAlt, setImgAlt] = React.useState("");
React.useEffect(() => {
if (fxPair !== undefined) {
(async function fetchImg() {
await import(`../../flags/${fxPair.currency.slice(0, 2).toLowerCase()}.png`).then((image) => {
setImgAlt(`${fxPair.currency.slice(0, 2).toLowerCase()}-flag`);
setFlagImgSrc(image.default);
});
})();
setFXRate(fxPair.exchangeRate.middle);
}
}, [fxPair, flagImgSrc, imgAlt, fXRate]);
return (
<div className={classes.FXPair}>
<img src={flagImgSrc} alt={imgAlt} />
<p className={classes.CurrencyToBuy}>{currencyCode}</p>
<p className={classes.fXRate}>EUR {(1 / fXRate).toFixed(3)}</p>
</div>
);
};
export default FXPair;
When testing the component by the test below, I want to make sure the correct flag-image alt text and the currency code get displayed. The test evaluation, however, does not work. I have tried using a different approach in each test, but none of them does the job.
Tests:
import { render, screen, waitFor } from "@testing-library/react";
import ProviderWrapper from "../../testUtils/ProviderWrapper";
import FXPair from "./FXPair";
test("Flag gets displayed for the currency-related country", async () => {
render(
<ProviderWrapper>
<FXPair currency={"HUF"} />
</ProviderWrapper>
);
await waitFor(() => {
screen.getByAltText("hu-flag");
});
expect(screen.getByAltText("hu-flag")).toBeInTheDocument();
});
test("Currency code gets displayed", async () => {
const currencyCode = "HUF";
render(
<ProviderWrapper>
<FXPair currency={currencyCode} />
</ProviderWrapper>
);
const items = await screen.findByText(/HUF/);
expect(items.getByText(/HUF/)).toBeInTheDocument();
});
Test results:
You can see that the value "HUF" (Hungarian Forint) which I pass in for the prop currencyCode
is not taken into consideration. Also the tests do not wait for the state slice imgAlt
, rather the tests evaluate based on the initial value of this slice.
● Flag gets displayed for the currency-related country
Unable to find an element with the alt text: hu-flag
Ignored nodes: comments, <script />, <style />
<body>
<div>
<div
class="FXPair"
>
<img
alt=""
src=""
/>
<p
class="CurrencyToBuy"
/>
<p
class="fXRate"
>
EUR
1.000
</p>
</div>
</div>
</body>
10 | );
11 |
> 12 | await waitFor(() => {
| ^
13 | screen.getByAltText("hu-flag");
14 | });
15 | expect(screen.getByAltText("hu-flag")).toBeInTheDocument();
at waitForWrapper (node_modules/@testing-library/dom/dist/wait-for.js:175:27)
at Object.<anonymous> (src/components/FXPair/FXPair.test.jsx:12:9)
● Currency code gets displayed
Unable to find an element with the text: /HUF/. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.
Ignored nodes: comments, <script />, <style />
<body>
<div>
<div
class="FXPair"
>
<img
alt=""
src=""
/>
<p
class="CurrencyToBuy"
/>
<p
class="fXRate"
>
EUR
1.000
</p>
</div>
</div>
</body>
24 | );
25 |
> 26 | const items = await screen.findByText(/HUF/);
| ^
27 | expect(items.getByText(/HUF/)).toBeInTheDocument();
28 | });
29 |
at waitForWrapper (node_modules/@testing-library/dom/dist/wait-for.js:175:27)
at findByText (node_modules/@testing-library/dom/dist/query-helpers.js:101:33)
at Object.<anonymous> (src/components/FXPair/FXPair.test.jsx:26:30)
Upvotes: 3
Views: 1903
Reputation: 420
You get fxPair from redux, do you mock redux in tests? Try to put console.log in component and debug.
if (fxPair !== undefined) {
Upvotes: 0
Reputation: 2867
try using findBy rather than getBy for your first check. This will return a promise which waits 1000ms(by default) to find the element, and if it still cant find it then it will fail.
await waitFor(() => {
screen.findByAltText("hu-flag");
});
expect(screen.getByAltText("hu-flag")).toBeInTheDocument();
With my tests I often use findBy first and the getBy in all of them after that.
Upvotes: 3