Jest URL.createObjectURL is not a function
Asked Answered
S

8

98

I'm developping a reactJs application. I'm using jest to test my application. I want to test a function that download a blob.

But unfortunately I receve this error:

URL.createObjectURL is not a function

my test function:

describe('download', () => {
    const documentIntial = { content: 'aaa' };
    it('msSaveOrOpenBlob should not have been called when navigao is undefined', () => {
      window.navigator.msSaveOrOpenBlob = null;
      download(documentIntial);
      expect(window.navigator.msSaveOrOpenBlob).toHaveBeenCalledTimes(0);
    });
  });

The function I want to test:

export const download = document => {
  const blob = new Blob([base64ToArrayBuffer(document.content)], {
    type: 'application/pdf',
  });
  if (window.navigator && window.navigator.msSaveOrOpenBlob) {
    window.navigator.msSaveOrOpenBlob(blob);
    return;
  }

  const fileURL = URL.createObjectURL(blob);
  window.open(fileURL);
};
Saphra answered 24/10, 2018 at 12:26 Comment(2)
Now reading a bit about your jest, it even seems your question is a jsdom one.Lollis
Possible duplicate of How to mock the JavaScript window object using Jest?Claver
I
88

This would appear to be as simple as setting up URL on the Global in Jest. Something like

describe('download', () => {
  const documentIntial = { content: 'aaa' };
  global.URL.createObjectURL = jest.fn();
  it('msSaveOrOpenBlob should not have been called when navigao is undefined', () => {
    global.URL.createObjectURL = jest.fn(() => 'details');
    window.navigator.msSaveOrOpenBlob = jest.fn(() => 'details');
    download(documentIntial);
    expect(window.navigator.msSaveOrOpenBlob).toHaveBeenCalledTimes(1);
  });
});

This should result in a test that you can also use for checking if global.URL.createObjectURL was called. As a side note: you may also run into a similar issue with window.open I would suggest mocking that as well if this becomes the case.

Immerse answered 24/10, 2018 at 13:5 Comment(2)
I have this result : expect(jest.fn())[.not].toHaveBeenCalledTimes() jest.fn() value must be a mock function or spy. Received: undefinedSaphra
Take a look at that I actually tested it this time. You should not be nulling the msSaveOrOpenBlob. You will want to mock it to be able to tell how often it has been called... otherwise it is not going to give you a call count.Immerse
S
35

Since window.URL.createObjectURL is not (yet) available in jest-dom, you need to provide a mock implementation for it.

Don't forget to reset the mock implementation after each test.

describe("your test suite", () => {
  window.URL.createObjectURL = jest.fn();

  afterEach(() => {
    window.URL.createObjectURL.mockReset();
  });

  it("your test case", () => {
    expect(true).toBeTruthy();
  });
});
Squama answered 18/6, 2019 at 7:8 Comment(0)
L
19

jsdom, the JavaScript implementation of the WHATWG DOM used by jest doesn't implement this method yet.

You can find an open ticket about this exact issue on their github page where some workarounds are provided in comments. But if you need the blobURL to actually work you'll have to wait this FR is solved.

Workaround proposed in the comments of the issue for jest:

function noOp () { }
if (typeof window.URL.createObjectURL === 'undefined') { 
  Object.defineProperty(window.URL, 'createObjectURL', { value: noOp})
}
Lollis answered 24/10, 2018 at 13:1 Comment(0)
S
19

You just have to Write this in your setupTest.js

window.URL.createObjectURL = function() {};
Stupid answered 13/6, 2020 at 10:22 Comment(1)
This finally worked for me when several other options didn't.Ulna
E
9

Just mocking the function global.URL.createObjectURL did not work for me, because the function was used by some modules during import and I got the error Jest URL.createObjectURL is not a function during import.

Instead it did help to create a file mockJsdom.js

Object.defineProperty(URL, 'createObjectURL', {
  writable: true,
  value: jest.fn()
})

Then import this file as the first import in your file containing the test

import './mockJsdom'
import { MyObjects} from '../../src/lib/mylib'

test('my test', () => {
  // test code
}

Found here: https://jestjs.io/docs/manual-mocks#mocking-methods-which-are-not-implemented-in-jsdom

Eggett answered 8/11, 2021 at 20:6 Comment(2)
I wrote the mock function just before starting of the test suite. That too worked. Thanks a lot for this useful answer and reference link as well.Oba
Also add "import {jest} from '@jest/globals';" at the top of mockJsdom file. From : jestjs.io/docs/ecmascript-modulesCheree
Z
7

The package jsdom-worker happens to provide this method, as well as adding support for web workers. The following worked for me:

npm install -D jsdom-worker

Then in package.json, edit or add a jest key:

{
  ...
  "jest": {
    "setupFiles": [
      "jsdom-worker"
    ]
  }
}
Zebapda answered 25/3, 2022 at 20:22 Comment(2)
Out of the box, Create React App does not support overriding the Jest option "setupFiles".Scythia
@Scythia but you can create an isolate jest.config.js file, which is better to isolate functionalityHaematogenous
L
0

Using react-testing-library with jest:

I had to add jest.spyOn(URL, 'createObjectURL').mockImplementation(); inside my test. This worked in conjunction with creating a mock file for the dom object

Object.defineProperty(URL, 'createObjectURL', {writable: true, value: jest.fn()})

and adding the import statement for that mock file as the first import inside my testing file.

Note: the mock file cannot be inside the tests directory

Leptophyllous answered 25/7, 2023 at 19:48 Comment(0)
P
0

this code simulates a blob url to test your full cycle

    global.URL.createObjectURL = jest.fn(
      (blob: any) => `blob:${blob.size}#t=${Date.now()}`
    )
Propaedeutic answered 10/12, 2023 at 2:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.