mapbox-gl-js 0.26.0:
Minimal-ish reproduction repo. The specific test-case is here
Presuming a set-up using webpack and jsdom for running tests
mapbox-gl/dist/mapbox-gl.js in a test fileNo error
Throws this error:
TypeError: window.URL.createObjectURL is not a function
If I want to test a file that interacts with the mapbox-gl library, I won't be able to without mocking out the include.
I guess this might also fail on browsers that don't implement createObjectURL, but I haven't tested it, and probably there are other technologies you're using that would fail too!
This file is included in the built file: /js/util/browser/web_worker.js, which includes this line:
const workerURL = window.URL.createObjectURL(new WebWorkify(require('../../source/worker'), {bare: true}));
jsdom hasn't implemented window.URL.createObjectURL, hence the error. createObjectURL is used in /js/util/ajax.js also but since that's not in a function it doesn't throw an error unless that function is called.
I've been working around this by pinning back to 0.24 — which doesn't fail in this manner.
Possibly the recommendation is to mock out the library? Good to know if so.
This isn't a supported use of Mapbox GL JS, however, you may be able to get it to work if you don't use the dist version -- use require('mapbox-gl') instead, which will get you a version tailored for a node environment.
You might also be interested in https://github.com/mapbox/mapbox-gl-js-mock.
@neoeno How did you end up working around this? Did you go with mapbox-gl-js-mock or some other solution? Thanks, in advance, for the advice.
+1 re: @stdavis a quick example showing how to correctly utilize mapbox-gl-js-mock would be really helpful.
Currently importing the mock lib is causing:
./node_modules/mapbox-gl-js-mock/node_modules/mapbox-gl/src/geo/transform.js
Module parse failed: Unexpected token (23:12)
Yeah, the readme for 'mapbox-gl-js-mock' is useless.
You can add this line to your jest.stubs.js file:
window.URL.createObjectURL = function() {};
For others landing here looking for a solution on how to mock mapbox-gl the solution can be found in Jest documentation: Mocking Node Modules
I found the mocking solution that worked for me here.
jest.mock('mapbox-gl/dist/mapbox-gl', () => ({
Map: () => ({}),
}));
Thanks @jenyeeiam
I know this is a long closed issue, but I had to keep going a bit further from the last comment https://github.com/mapbox/mapbox-gl-js/issues/3436#issuecomment-459460798 to get a working set up. I started with the code there, but kept hitting undefined errors for every static method or Map instance method. To get around those I just kept tagging them on to where the code was expecting them. At the time of this writing, I'm at:
In src/setupTests.ts
jest.mock('mapbox-gl/dist/mapbox-gl', () => ({
GeolocateControl: jest.fn(),
Map: jest.fn(() => ({
addControl: jest.fn(),
on: jest.fn(),
remove: jest.fn()
})),
NavigationControl: jest.fn()
}));
export default undefined;
Assuming tests will fail again if I use another static or instance method like queryRenderedFeatures or similar. If so, I'll at that key to the return object of Map and set the value to jest.fn(). Not sure if this is the right way, but it's the way that's working at the moment.
If you put this at __mocks__/mapbox-gl.js, then it'll be loaded automatically on all jest tests.
module.exports = {
// whatever properties and functions you need access to
};
Another path, if you prefer to keep your mocking per-test:
// ...imports here, _except_ the offending module
function loadOffendingModule() {
const {default, namedExport} = require('path/to/offendingModule');
return {offendingModule: default, namedExport};
}
describe('testBlock', () => {
/* global global */
// JSDOM what do we pay you for??
const originalURL = global.URL;
const originalWindow = global.window;
const originalDocument = global.document;
beforeEach(() => {
global.URL = {createObjectURL: () => ''};
global.window = {
...originalWindow,
URL: global.URL,
// ...anything else you need to mock
};
global.document = {
...originalDocument,
createElement: () => ({
setAttribute: () => {},
}),
// ...anything else you need to mock
};
});
afterEach(() => {
global.URL = originalURL;
global.window = originalWindow;
global.document = originalDocument;
});
test('a test...', () => {
const {offendingModule, namedExport} = loadOffendingModule();
// ...use the "imports" as normal in the test
});
});
This works when the "offending module" is importing something that immediately (on import/require) executes code that throws in some environment, e.g. Node/JSDOM-mocked browser. The idea here is:
URL/window/document mocks in a beforeEach()require()afterEach().Messy, but it works. Note also that I mocked out more here than you'd likely need just for mapbox-gl, I was hitting all sorts of NPEs (kepler.gl, d3, react-sortable-hoc). There goes my morning 😝
Most helpful comment
I know this is a long closed issue, but I had to keep going a bit further from the last comment https://github.com/mapbox/mapbox-gl-js/issues/3436#issuecomment-459460798 to get a working set up. I started with the code there, but kept hitting undefined errors for every static method or Map instance method. To get around those I just kept tagging them on to where the code was expecting them. At the time of this writing, I'm at:
In src/setupTests.ts
Assuming tests will fail again if I use another static or instance method like
queryRenderedFeaturesor similar. If so, I'll at that key to the return object ofMapand set the value tojest.fn(). Not sure if this is the right way, but it's the way that's working at the moment.