skip to Main Content

This is probably more of a JavaScript/TypeScript question then it is about React/Testing.

But I’ll give the complete story. So I have a test app with basic routing functionality and tests to verify that the routing works.

App.tsx
https://github.com/Leejjon/pwa-seo/blob/6f621968de1184b03744a262a68d291b4571c5c1/src/App.tsx

App.test.tsx
https://github.com/Leejjon/pwa-seo/blob/6f621968de1184b03744a262a68d291b4571c5c1/src/App.test.tsx

Everything worked fine. Then I added an useEffect hook to initialize my internationalization library:

useEffect(() => {
    async function initMessages() {
        await intl.init({
            currentLocale: "en-US",
            locales
        });
    }

    initMessages().then(() => setLoading(false));
}, [loading]);

This loads all my text assets in English. This works fine, but broke all my tests with the following error message:
Warning: An update to App inside a test was not wrapped in act(...).

After some reading up on the internet I managed to fix my tests by adding this ‘act’ function, here is one example:

import React from 'react';
import {act, render, fireEvent, waitForElement} from '@testing-library/react';
import "@testing-library/jest-dom/extend-expect";
import App from './App';

test('Verify home page header', async() => {
    let app: HTMLElement;
    await act(async () => {
        const {container} = render(<App/>);
        app = container;
    });
    // @ts-ignore
    if (app) {
        const pageHeaderContent = app.querySelector("#pageHeader")?.firstChild?.textContent;
        expect(pageHeaderContent).toMatch('Home page');
    } else {
        fail("The app should have been initialized.");
    }
});

Right now I’m suppressing the TS2454: Variable 'app' is used before being assigned. warning with the @ts-ignore. This is ugly. If I move my assertions into the act function, I get the same Warning: An update to App inside a test was not wrapped in act(...). error again.

Is there a way to obtain the container object destructured from the render function without having to use the @ts-ignore and the if clause to do null checking?

I created a tag for the current code related to this question:
https://github.com/Leejjon/pwa-seo/releases/tag/uglylines
Link to last commit: https://github.com/Leejjon/pwa-seo/commit/2434f78c0619be2d55f9de965149f6bd6d1a0b90

2

Answers


  1. Chosen as BEST ANSWER

    After puzzling this is my result

    test('Verify home page header', async() => {
        let app: HTMLElement | undefined = undefined;
        await act(async () => {
            const {container} = render(<App/>);
            app = container;
        });
        let appAsHtmlElement = (app as unknown as HTMLElement);
        const pageHeaderContent = appAsHtmlElement.querySelector("#pageHeader")?.firstChild?.textContent;
        expect(pageHeaderContent).toMatch('Home page');
    });
    

    Better suggestions (if there is some way of not having to use the 'act' function) are still welcome.


  2. Typescript is complaining about the app variable to not have been initialised when you access it in the if-statement. You can simply fix that by assigning null to it.

    let app: HTMLElement = null;
    

    In case you use strict null checks you have to allow null on the type:

    let app: HTMLElement | null = null;
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search