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 import
ing 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
queryRenderedFeatures
or similar. If so, I'll at that key to the return object ofMap
and set the value tojest.fn()
. Not sure if this is the right way, but it's the way that's working at the moment.