Using Leaflet 0.7.7 and jsdom 9.4.2, I'm unable to successfully execute a unit test on L.map.
I'm not using a test runner--just plain ol' mocha under Node.
$ mocha --require test/support/bugReportSetup.js --compilers js:babel-register --require babel-polyfill test/unit/components/MaxBoundsBugTest.jsx
MaxBoundsBug
when maxBounds is provided
1) mounts without problems
0 passing (898ms)
1 failing
1) MaxBoundsBug when maxBounds is provided mounts without problems:
Error: Invalid LatLng object: (NaN, NaN)
at Object.L.LatLng (node_modules/leaflet/dist/leaflet-src.js:1122:9)
at Object.L.Projection.SphericalMercator.unproject (node_modules/leaflet/dist/leaflet-src.js:1403:10)
at Object.L.CRS.pointToLatLng (node_modules/leaflet/dist/leaflet-src.js:1439:26)
at L.Map.L.Class.extend.unproject (node_modules/leaflet/dist/leaflet-src.js:1930:27)
at L.Map.L.Class.extend._limitCenter (node_modules/leaflet/dist/leaflet-src.js:2279:15)
at L.Map.include.setView (node_modules/leaflet/dist/leaflet-src.js:8687:17)
at L.Map.L.Class.extend.initialize (node_modules/leaflet/dist/leaflet-src.js:1545:9)
at new NewClass (node_modules/leaflet/dist/leaflet-src.js:229:20)
at Object.L.map (node_modules/leaflet/dist/leaflet-src.js:2318:9)
at Context.<anonymous> (MaxBoundsBugTest.jsx:13:9)
Here are the gory details...
$ npm show leaflet version
0.7.7
$ npm show jsdom version
9.4.2
// File: MaxBoundsBugTest.jsx
import { expect } from 'chai'
import L from 'leaflet'
describe('MaxBoundsBug', () => {
context('when maxBounds is provided', () => {
const maxBounds = [
[-80, -180], // SW
[90, 180], // NE
]
it('mounts without problems', () => {
L.map('test', {
center: [30, 0],
zoom: 13,
maxBounds: maxBounds
});
})
})
})
// File: bugReportSetup.js
// From https://github.com/airbnb/enzyme/blob/master/docs/guides/jsdom.md
import { jsdom } from 'jsdom'
const EXPOSED_PROPERTIES = ['window', 'navigator', 'document'];
MAIN: {
global.document = jsdom('<div id="test"></div>')
global.window = document.defaultView
Object.keys(document.defaultView).forEach((property) => {
if ( global[property] === void 0 ) {
EXPOSED_PROPERTIES.push(property)
global[property] = document.defaultView[property]
}
})
}
Can you try the same with Leaflet 1.0 rc3?
Yes, the problem still exists in rc3. I added a debugging statement to the middle of the test to confirm that I'm using the correct version:
$ mocha --require test/support/bugReportSetup.js --compilers js:babel-register --require babel-polyfill test/unit/components/MaxBoundsBugTest.jsx
MaxBoundsBug
when maxBounds is provided
Leaflet version: 1.0.0-rc.3
1) mounts without problems
0 passing (805ms)
1 failing
1) MaxBoundsBug when maxBounds is provided mounts without problems:
Error: Invalid LatLng object: (NaN, NaN)
at Object.L.LatLng (node_modules/leaflet/dist/leaflet-src.js:1618:9)
at Object.L.Projection.SphericalMercator.unproject (node_modules/leaflet/dist/leaflet-src.js:2028:10)
at Object.L.CRS.pointToLatLng (node_modules/leaflet/dist/leaflet-src.js:2071:26)
at L.Map.L.Evented.extend.unproject (node_modules/leaflet/dist/leaflet-src.js:2877:27)
at L.Map.L.Evented.extend._limitCenter (node_modules/leaflet/dist/leaflet-src.js:3386:15)
at L.Map.include.setView (node_modules/leaflet/dist/leaflet-src.js:12558:17)
at L.Map.L.Evented.extend.initialize (node_modules/leaflet/dist/leaflet-src.js:2377:9)
at new NewClass (node_modules/leaflet/dist/leaflet-src.js:310:20)
at Object.L.map (node_modules/leaflet/dist/leaflet-src.js:3442:9)
at Context.<anonymous> (MaxBoundsBugTest.jsx:14:9)
I have no experience with jsdom, so just guessing here, but have you ensured your div has a valid size, etc? It looks like some calculation does a division by zero or similar.
Otherwise I would suggest firing up a debugger and examining what coordinate _limitCenter is trying to unproject - I'm guessing it's somehow an invalid coordinate which might come from the map's dimensions.
With Leaflet 0.7.7 we had no problem, likely because we have a bunch of setup to set navigator and other global properties:
var jsdom = require('jsdom').jsdom;
var exposedProperties = ['window', 'navigator', 'document'];
global.document = jsdom('<html><body><div id="app"></div></html>');
global.window = document.defaultView;
Object.keys(document.defaultView).forEach((property) => {
if (typeof global[property] === 'undefined') {
exposedProperties.push(property);
global[property] = document.defaultView[property];
}
});
// For some reason, this property does not get set above.
global.Image = global.window.Image;
global.console.debug = () => {};
global.navigator = {
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) nteract/0.0.12 Chrome/50.0.2661.102 Electron/1.1.3 Safari/537.36',
platform: 'MacIntel',
};
// HACK: Polyfil that allows codemirror to render in a JSDOM env.
global.window.document.createRange = function createRange() {
return {
setEnd: () => {},
setStart: () => {},
getBoundingClientRect: () => {
return { right: 0 };
},
getClientRects: () => {
return []
}
}
};
For 1.0.0 I noticed, via our Greenkeeper PR that I needed to add navigator.platform: https://github.com/nteract/nteract/pull/841
However, I'm running into this same problem about the bounds being invalid (only in tests, not in our app). I've gone ahead and created a repository for reproducing the issue:
https://github.com/rgbkrk/leaflet-issue-reproduction
git clone https://github.com/rgbkrk/leaflet-issue-reproduction
cd leaflet-issue-reproduction
npm i
node reproduce.js
Expect to see:
/Users/kylek/code/src/github.com/rgbkrk/leaflet-issue-reproduction/node_modules/leaflet/dist/leaflet-src.js:1609
throw new Error('Invalid LatLng object: (' + lat + ', ' + lng + ')');
^
Error: Invalid LatLng object: (NaN, NaN)
at Object.L.LatLng (/Users/kylek/code/src/github.com/rgbkrk/leaflet-issue-reproduction/node_modules/leaflet/dist/leaflet-src.js:1609:9)
at Object.L.Projection.SphericalMercator.unproject (/Users/kylek/code/src/github.com/rgbkrk/leaflet-issue-reproduction/node_modules/leaflet/dist/leaflet-src.js:2019:10)
at Object.L.CRS.pointToLatLng (/Users/kylek/code/src/github.com/rgbkrk/leaflet-issue-reproduction/node_modules/leaflet/dist/leaflet-src.js:2062:26)
at L.Map.L.Evented.extend.unproject (/Users/kylek/code/src/github.com/rgbkrk/leaflet-issue-reproduction/node_modules/leaflet/dist/leaflet-src.js:2877:27)
at L.GridLayer.L.Layer.extend._updateLevels (/Users/kylek/code/src/github.com/rgbkrk/leaflet-issue-reproduction/node_modules/leaflet/dist/leaflet-src.js:4135:35)
at L.GridLayer.L.Layer.extend._setView (/Users/kylek/code/src/github.com/rgbkrk/leaflet-issue-reproduction/node_modules/leaflet/dist/leaflet-src.js:4287:9)
at L.GridLayer.L.Layer.extend._resetView (/Users/kylek/code/src/github.com/rgbkrk/leaflet-issue-reproduction/node_modules/leaflet/dist/leaflet-src.js:4263:8)
at L.GridLayer.L.Layer.extend.onAdd (/Users/kylek/code/src/github.com/rgbkrk/leaflet-issue-reproduction/node_modules/leaflet/dist/leaflet-src.js:3911:8)
at L.Layer.L.Evented.extend._layerAdd (/Users/kylek/code/src/github.com/rgbkrk/leaflet-issue-reproduction/node_modules/leaflet/dist/leaflet-src.js:3542:8)
at L.Evented.L.Class.extend.fire (/Users/kylek/code/src/github.com/rgbkrk/leaflet-issue-reproduction/node_modules/leaflet/dist/leaflet-src.js:588:11)
It's ok if the solution ends up being more configuration for our JSDOM setup -- I'm just unsure what else I need to "polyfill".
We have a similar issue with fitBounds() and getBounds(), using Jest (which uses jsdom under the hood):
test('`getBounds()`', () => {
const container = document.createElement('div');
const map = createMap(leaflet)(container, {center: [50, 10], zoom: 8});
map.getBounds();
});
EDIT: Other things like getCenter() or setCenter() work fine.
Leaflet Version: 1.0.1
jsdom Version: 9.6.0
@rgbkrk @Scarysize I don't think anyone in the core team uses jsdom, so we rely a bit on you providing more information here. The team's available time for debugging issues is very limited, especially on special platforms like jsdom.
If you have a stack trace, please debug and check where the NaN comes from, it will most likely give a clue about what's happening.
@perliedman Thanks for the quick response. I will debug the error und provide my results. Do you guys have a Slack/Discord/IRC channel?
@Scarysize https://gitter.im/Leaflet/Leaflet , which is also bridged to https://riot.im/app/#/room/#Leaflet:matrix.org
Pretty stupid solution actually. Just set clientWidth and clientHeight for the container element you are passing to the map to some valid values:
const container = document.createElement('div');
container.clientWidth = 800;
container.clientHeight = 800;
const map = L.map(container);
// now returns actual bounds, doesn't throw with jsdom
map.getBounds()
@Scarysize thanks for providing info on the fix!
I just had a very similar issue myself and wanted to provide some extra info.
I'm also running automated tests with jsdom and Mocha. jsdom provides only the DOM for testing, i.e., it doesn't actually render the document. Element properties such as clientWidth and clientHeight, therefore, are not provided. Those properties, however, are checked in L.getSize in order to calculate the center of the map's containing element.
A workaround like the one @Scarysize provided works fine enough, e.g.,
if (!container.clientWidth || !container.clientHeight) {
// for testing
container.clientWidth = 1024;
container.clientHeight = 1024;
}
It might be useful, however, if getSize through an error if clientWidth or clientHeight can't be checked, or if they gracefully fell back to 0 (which seems reasonable since those properties would presumably only be absent if the document wasn't actually being rendered in a browser).
Most helpful comment
Pretty stupid solution actually. Just set
clientWidthandclientHeightfor the container element you are passing to the map to some valid values: