Jest error - Not implemented: navigation (except hash changes) when click event is triggered on an anchor element
Asked Answered
S

3

13

I have written unit test using jest version 26.0.0 with react-testing-library and node version 14.2.0. Below is my React functional component which has a button, on clicking makes an ajax call to download a report file.

const ReportListTable = () => {
    const { downloadReports } = useReports();

    const onClickDownload = () => {
        let fileName = `Report.zip`;
        downloadReports()
            .then((response) => response.arrayBuffer())
            .then((data) => {
                const blob = new Blob([data], { type: "octet/stream", endings: "native" });
                const url = window.URL.createObjectURL(blob);
                const anchor = document.createElement("a");
                anchor.setAttribute("href", url);
                anchor.setAttribute("download", fileName);
                anchor.click();
                window.URL.revokeObjectURL(url);
            })
            .catch((error?: Error | Response) => {
                if (error instanceof Error) {
                    message.error(error?.message);
                }
            });
    };

    return (
        <div>
            <Button data-testid="download-btn" onClick={onClickDownload}>
                Download
            </Button>
        </div>
    );
};

And I have below test suite where the createObjectURL and revokeObjectURL before the test runs.

describe("Test Reports List Table component", () => {
    const { URL } = window;

    beforeAll(() => {
        window.URL.createObjectURL = jest.fn();
        window.URL.revokeObjectURL = jest.fn();

        jest.spyOn(useReportsHook, "useReports").mockReturnValue(useReportsMockValue);
    });

    afterAll(() => {
        window.URL = URL;
        jest.clearAllMocks();
    });

    it("should handle row button enabled and download on button clicked", async () => {
        jest.spyOn(useReportsContextHook, "useReportsContext").mockReturnValue([{ checkedIds: ["report_1"] }, jest.fn]);
        const { asFragment } = render(<ReportListTable />);
        
        fireEvent.click(await screen.getByTestId(elements.downloadTestId));
        await waitFor(() => expect(window.URL.createObjectURL).toHaveBeenCalled());
        await waitFor(() => expect(window.URL.revokeObjectURL).toHaveBeenCalled());
        expect(asFragment).toMatchSnapshot();
    });
});

The test case is passed however it throws the below error.

  console.error
    Error: Not implemented: navigation (except hash changes)
        at module.exports (/Users/user/Documents/lydia-github/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/browser/not-implemented.js:9:17)
        at navigateFetch (/Users/user/Documents/lydia-github/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/window/navigation.js:76:3)
        at exports.navigate (/Users/user/Documents/lydia-github/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/window/navigation.js:54:3)
        at Timeout._onTimeout (/Users/user/Documents/lydia-github/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/nodes/HTMLHyperlinkElementUtils-impl.js:81:7)
        at listOnTimeout (internal/timers.js:549:17)
        at processTimers (internal/timers.js:492:7) undefined

I tried to mock the location object after referring this thread How to fix Error: Not implemented: navigation (except hash changes), however it didn't help. I need help to resolve this error.

Sivia answered 22/12, 2020 at 3:17 Comment(1)
Not an answer, but I have a similar situation for which I'm looking for a decent resolution. I've found that the error message is not emitted when the anchor.click(); line is removed (or rather the equivalent line in my code). Have you tried selectively commenting out the business logic and finding which line emits the error in your case?Frerichs
F
23

I've worked out the problem:

JSDom doesn't like you to do navigation - cf., https://github.com/jsdom/jsdom/issues/2112#issuecomment-359297866

The issue we have here is that the code is performing a click event on an anchor:

const anchor = document.createElement("a");
anchor.setAttribute("href", url);
anchor.setAttribute("download", fileName);
anchor.click();

A click on an anchor is a navigation, usually, which I presume JSDom is taking exception to.

To solve the annoying logging, rather than mocking the location, mock the click():

HTMLAnchorElement.prototype.click = jest.fn();

This worked for me.

Incidentally, I also had to mock the window.URL.createObjectUrl call as I was getting:

TypeError: window.URL.createObjectURL is not a function

...so:

global.window.URL.createObjectURL = jest.fn();

Hope this works for you.

Frerichs answered 18/6, 2021 at 17:0 Comment(3)
Another approach is to listen for and cancel the click event in the test, to prevent the JSDom navigation occurring: anchor.addEventListener("click", (e) => { e.preventDefault(); })Opportune
I also had to add e.stopPropagation()Mantinea
Could also use a hash href value instead of a full URL, if that works for your test case.Muriate
K
1

This approach solved my issue when using NextJS Link.

jest.mock("next/link", () => {
  const mockLink = ({
    children,
    href,
    onClick,
  }: {
    children: React.ReactNode;
    href: string;
    onClick: () => void;
  }) => {
    return (
      <a
        href={href}
        onClick={(e) => {
          e.preventDefault();
          onClick();
        }}
      >
        {children}
      </a>
    );
  };
  mockLink.displayName = "Link";
  return mockLink;
});
Karelian answered 3/6, 2024 at 19:15 Comment(0)
H
-1

This warning appears during tests when you try to navigate, which is not (yet) supported by JSDOM (See this issue).

Generally you can just ignore this warning, unless you intend to run your tests in a browser or something. It's a test-only warning, and doesn't actually indicate a problem in your code; you're just running into a feature that JSDOM hasn't implemented yet.

There are several ways to address it, if the warning bothers you (see above, or this post), depending on how precisely you're navigating.

Hurt answered 17/1, 2023 at 12:38 Comment(1)
This does not address the question, as the navigation is done by anchor click, not window.locationFlugelhorn

© 2022 - 2025 — McMap. All rights reserved.