Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 888
jagadish c Avatar asked Dec 22 '20 03:12

jagadish c


1 Answers

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.

like image 58
MrSpaceman Avatar answered Oct 05 '22 04:10

MrSpaceman