0

I have a function that allows users to register. After successful registration, the user is redirected to the home page (after 5 seconds). Before being redirected, the user receives a toast notification to see whether the registration was successful or not.

The problem that I have: I don't know how to remove the setTimeout function in the test, because I want to check whether the message is displayed correctly and the redirection is working. I have already tried fakeTimers from vi, but when I use it, I get a timeout error for every subsequent test + the current test.

Code:

const onSubmit = async (formData) => {
        try {
            await registerUserAPI(formData, setError);
        } catch (error) {
            toast.error(data.detail || "Registration failed: An unknown page error occurred. You will be redirected shortly...");

            setTimeout(() => {
                  window.location.href = "/";
             }, 5000);
        };
    };

Test function:

it("Registration was successful and redirects after 5 seconds", async () => {
        vi.useFakeTimers();

        delete window.location;
        window.location = { href: "" };

        fetch.mockResolvedValueOnce({
            ok: true,
            status: 201,
            json: async () => ({})
        });

        render(<Register />);

        await userEvent.type(usernameInput, testUsername);
        await userEvent.type(emailInput, testEmail);
        await userEvent.type(passwordInput, testPassword);
        await userEvent.click(submitButton);

        const successMessage = await screen.findByText(
            "Registration successful! You will be redirected to the homepage shortly."
        );
        expect(successMessage).toBeInTheDocument();

        await act(async () => {
            vi.advanceTimersByTime(5000);
        });

        expect(window.location.href).toBe("/");

        vi.useRealTimers();
    });

Error:

 FAIL  src/__test__/pages/auth/Register.test.jsx > Register > Registration was successful and redirects after 5 seconds
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
 ❯ src/__test__/pages/auth/Register.test.jsx:81:5
     79|
     80|
     81|     it("Registration was successful and redirects after 5 seconds", async () => { 
       |     ^
     82|         vi.useFakeTimers();
     83|
2
  • It would be helpful to see the setTimeout function in the code itself. Commented Oct 16 at 15:16
  • @stakolee right. I'm sorry. I updated the post. Commented Oct 16 at 17:24

2 Answers 2

0

I found a solution for my problem: I divide my tests up.

The following will be done:

const realSetTimeout = global.setTimeout;

global.setTimeout = (cb, _ms, ...args) => {
    try { 
        cb(...args); 
    } catch (e) {
        // let test fail normally 
    }
        return 0; // fake id
    };

delete window.location;
window.location = { href: "" };

// Code ...

// Check the redirection
expect(window.location.href).toBe("/");

// Restore setTimeout
global.setTimeout = realSetTimeout;

With this code, I do the following: I mock setTimeout directly and ensure that it is executed immediately, but before that, the normal state of setTimeout is temporarily saved so that other tests are not affected. Then I “delete” window.location in order to obtain the actual href later. And only now does the normal code that leads to redirection come into play. After that, you simply check whether the redirection was successful or not and restore the state of setTimeout.

For me, however, it was important that I could check both the message displayed before the redirect and whether the redirection was working correctly. Since I couldn't find a solution, I simply split it up. One test function checks the message and one checks the redirection with the mock of setTimeout. However, I was able to solve this elegantly with .each to comply with the DRY principle.

Sign up to request clarification or add additional context in comments.

Comments

-1

In this case I would make a new function for the redirection, and pass it in as a dependency:

const redirectFunc = () => window.location.href = '/';

const onSubmit = async (formData, redirect = redirectFunc) => {
        try {
            await registerUserAPI(formData, setError);
        } catch (error) {
            toast.error(data.detail || "Registration failed: An unknown page error occurred. You will be redirected shortly...");

            setTimeout(redirect, 5000);
        };
    };

That way you can mock out redirect in the tests.

1 Comment

Unfortunately, that doesn't work for me. Could you show me how you want to test the redirection in vitest as code?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.