Closed neoeno closed 8 years ago
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 π
This issue seems very similar to what I am encountering when attempting to use Quokka on the mapbox-gl library, and initializing a very basic map. Quokka enables live logging and testing of function results to streamline debugging. See Quokka's Website and a Youtube Demo of the basics of Quokka for more info.
TLDR/Summary How do I get Quokka's Live logging to work in a basic mapbox sandbox/testing-environment?
While this issue is closed, and I'm sure I'm doing a lot wrong on my end, but I am having a lot of trouble making progress and finding additional information online. This thread seems to be the closest to my current problem.
The Error:
window.URL.createObjectURL is not a functionβ
βββββat ββββββββdefineβββ β./node_modules/mapbox-gl/dist/mapbox-gl.js:25β
βββββat ββββββ./node_modules/mapbox-gl/dist/mapbox-gl.js:35β
βββββat ββββββ./node_modules/mapbox-gl/dist/mapbox-gl.js:3β
βββββat ββββββ./node_modules/mapbox-gl/dist/mapbox-gl.js:6β
βββββat ββββββββObject.<anonymous>βββ β./node_modules/mapbox-gl/dist/mapbox-gl.js:46β
Navigating to at ββββββββdefineβββ β./node_modules/mapbox-gl/dist/mapbox-gl.js:25
, it is the following code...
if (typeof window !== 'undefined') {
mapboxgl.workerUrl = window.URL.createObjectURL(new Blob([workerBundleString], { type: 'text/javascript' }));
}
Steps to reproduce: (probably unnecessarily long-winded --- sorry)
import mapboxgl from 'mapbox-gl';
βInstall "mapbox-gl" package for the current quokka fileβ βInstall "mapbox-gl" package into the projectβ β Cannot find module 'mapbox-gl'β Require stack:β
6. Clicking **Install "mapbox-gl" package into the project** will `npm init` node and a package.json with mapbox-gl as a dependency.
7. Fill out the rest of the javascript file by initializing the map...
// import mapbox import mapboxgl from 'mapbox-gl';
// set accessToken mapboxgl.accessToken = 'YOUR_PERSONAL_TOKEN_HERE'
// init map const map = new mapboxgl.Map({ container: 'map', style: 'mapbox://styles/mapbox/streets-v11', center: [-74.50, 40], zoom: 9 });
8. This results in the error I listed above `window.URL.createObjectURL is not a function`
9. If I delete the code below from `node_modules/mapbox-gl/dist/mapbox-gl.js`, (which I imagine is *is very bad* but it works as a workaround.)
if (typeof window !== 'undefined') {
mapboxgl.workerUrl = window.URL.createObjectURL(new Blob([workerBundleString], { type: 'text/javascript' }));
}
... I get a new error: `Container 'map' not found`
10. So I create an `index.html` in the same root directory with the following
<!DOCTYPE html>
And I still get the error
Container 'map' not found.
**My Machine**
Ubuntu 20.04
node: v16.13.1
Quokka global config fileββββ (`~/.quokka/config.json`) is as follows:
{"pro":true,"plugins":["jsdom-quokka-plugin"]}
I'd really appreciate some feedback.
Best
Dylan
Hey @Dylansc22 unfortunately, this seems to be a limitation of Quokka as documented in their How Does It Work? section:
Quokka may not work if you are trying to use code or components that require special runtime initialization, or if you are running code whose runtime is not compatible with node.js.
For example, if you try and run Quokka on a React functional component, while the component will work in the Browser because of Reactβs browser runtime, no code will be executed by Quokka in node.js
This would explain why you're encountering an error around accessing window
properties since those are not available in Node. I suspect the container 'map' not found
error is similar since Node does not have a DOM object. You can attempt to stub/mock these functions (there are npm packages that do this sort of thing) in order to get GL JS running, but I suspect that will be a lot of work for limited payoff. At the end of the day, GL JS is designed to run in a browser environment with the window
and document
APIs.
@ryanhamley Thanks for the info! While not the answer I was hoping for, it's good to have the confirmation the error is not on my end (and learn something along the way -- wasn't even familiar with the concept of "mock" libraries). I see mapbox-gl-js-mock
isn't really maintained. Very unfortunate that there isn't a simple workaround, as it would really help my workflow.
If I do self-teach my way through a solution, I'll post it here in case someone stumbles across this issue.
Cheers
Just in case anybody in 2024 is solving the same issue with Vitest:
vi.mock("mapbox-gl/dist/mapbox-gl", () => {
const defaultExport = {
GeolocateControl: vi.fn(),
Map: vi.fn(() => ({
addControl: vi.fn(),
on: vi.fn(),
remove: vi.fn(),
})),
NavigationControl: vi.fn(),
default: vi.fn(),
}
return {
...defaultExport,
default: defaultExport,
}
})
mapbox-gl-js 0.26.0:
Steps to Trigger Behavior
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 fileExpected Behavior
No error
Actual Behavior
Throws this error:
Consequences
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!Cause
This file is included in the built file: /js/util/browser/web_worker.js, which includes this line:
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.