Started user control

This commit is contained in:
TheGreyDiamond
2021-05-25 23:50:46 +02:00
parent 1fc78a3b44
commit 6e3104eef3
175 changed files with 121801 additions and 214 deletions

View File

@@ -0,0 +1,2 @@
// put after Leaflet files as imagePath can't be detected in a PhantomJS env
L.Icon.Default.imagePath = "../dist/images";

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,93 @@
// https://github.com/tmcw/happen
!(function(context) {
var h = {};
// Make inheritance bearable: clone one level of properties
function extend(child, parent) {
for (var property in parent) {
if (typeof child[property] == 'undefined') {
child[property] = parent[property];
}
}
return child;
}
h.once = function(x, o) {
var evt;
if (o.type.slice(0, 3) === 'key') {
if (typeof Event === 'function') {
evt = new Event(o.type);
evt.keyCode = o.keyCode || 0;
evt.charCode = o.charCode || 0;
evt.shift = o.shift || false;
evt.meta = o.meta || false;
evt.ctrl = o.ctrl || false;
evt.alt = o.alt || false;
} else {
evt = document.createEvent('KeyboardEvent');
// https://developer.mozilla.org/en/DOM/event.initKeyEvent
// https://developer.mozilla.org/en/DOM/KeyboardEvent
evt[(evt.initKeyEvent) ? 'initKeyEvent'
: 'initKeyboardEvent'](
o.type, // in DOMString typeArg,
true, // in boolean canBubbleArg,
true, // in boolean cancelableArg,
null, // in nsIDOMAbstractView viewArg, Specifies UIEvent.view. This value may be null.
o.ctrl || false, // in boolean ctrlKeyArg,
o.alt || false, // in boolean altKeyArg,
o.shift || false, // in boolean shiftKeyArg,
o.meta || false, // in boolean metaKeyArg,
o.keyCode || 0, // in unsigned long keyCodeArg,
o.charCode || 0 // in unsigned long charCodeArg);
);
}
} else {
evt = document.createEvent('MouseEvents');
// https://developer.mozilla.org/en/DOM/event.initMouseEvent
evt.initMouseEvent(o.type,
true, // canBubble
true, // cancelable
window, // 'AbstractView'
o.clicks || 0, // click count
o.screenX || 0, // screenX
o.screenY || 0, // screenY
o.clientX || 0, // clientX
o.clientY || 0, // clientY
o.ctrl || 0, // ctrl
o.alt || false, // alt
o.shift || false, // shift
o.meta || false, // meta
o.button || false, // mouse button
null // relatedTarget
);
}
x.dispatchEvent(evt);
};
var shortcuts = ['click', 'mousedown', 'mouseup', 'mousemove', 'keydown', 'keyup', 'keypress'],
s, i = 0;
while (s = shortcuts[i++]) {
h[s] = (function(s) {
return function(x, o) {
h.once(x, extend(o || {}, { type: s }));
};
})(s);
}
h.dblclick = function(x, o) {
h.once(x, extend(o || {}, {
type: 'dblclick',
clicks: 2
}));
};
this.happen = h;
if (typeof module !== 'undefined') {
module.exports = this.happen;
}
})(this);

View File

@@ -0,0 +1,79 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Spec Runner</title>
<link rel="stylesheet" type="text/css" href="../node_modules/mocha/mocha.css">
</head>
<body>
<div id="mocha"></div>
<script src="expect.js"></script>
<script type="text/javascript" src="../node_modules/mocha/mocha.js"></script>
<script type="text/javascript" src="happen.js"></script>
<script type="text/javascript" src="sinon.js"></script>
<script type="text/javascript" src="../node_modules/leaflet/dist/leaflet-src.js"></script>
<!-- source files -->
<script type="text/javascript" src="../src/DistanceGrid.js"></script>
<script type="text/javascript" src="../src/MarkerCluster.js"></script>
<script type="text/javascript" src="../src/MarkerClusterGroup.js"></script>
<script type="text/javascript" src="../src/MarkerCluster.QuickHull.js"></script>
<script type="text/javascript" src="../src/MarkerCluster.Spiderfier.js"></script>
<script type="text/javascript" src="../src/MarkerOpacity.js"></script>
<script type="text/javascript" src="../src/MarkerClusterGroup.Refresh.js"></script>
<script>
mocha.setup('bdd');
mocha.ignoreLeaks();
</script>
<!-- spec files -->
<script type="text/javascript" src="suites/SpecHelper.js"></script>
<script type="text/javascript" src="suites/LeafletSpec.js"></script>
<script type="text/javascript" src="suites/DistanceGridSpec.js"></script>
<script type="text/javascript" src="suites/QuickHullSpec.js"></script>
<script type="text/javascript" src="suites/AddLayer.MultipleSpec.js"></script>
<script type="text/javascript" src="suites/AddLayer.SingleSpec.js"></script>
<script type="text/javascript" src="suites/AddLayersSpec.js"></script>
<script type="text/javascript" src="suites/animateOptionSpec.js"></script>
<script type="text/javascript" src="suites/singleMarkerModeSpec.js"></script>
<script type="text/javascript" src="suites/ChildChangingIconSupportSpec.js"></script>
<script type="text/javascript" src="suites/markerMoveSupportSpec.js"></script>
<script type="text/javascript" src="suites/CircleMarkerSupportSpec.js"></script>
<script type="text/javascript" src="suites/CircleSupportSpec.js"></script>
<script type="text/javascript" src="suites/onAddSpec.js"></script>
<script type="text/javascript" src="suites/onRemoveSpec.js"></script>
<script type="text/javascript" src="suites/clearLayersSpec.js"></script>
<script type="text/javascript" src="suites/eachLayerSpec.js"></script>
<script type="text/javascript" src="suites/eventsSpec.js"></script>
<script type="text/javascript" src="suites/getBoundsSpec.js"></script>
<script type="text/javascript" src="suites/getLayersSpec.js"></script>
<script type="text/javascript" src="suites/getVisibleParentSpec.js"></script>
<script type="text/javascript" src="suites/NonPointSpec.js"></script>
<script type="text/javascript" src="suites/RemoveLayerSpec.js"></script>
<script type="text/javascript" src="suites/removeLayersSpec.js"></script>
<script type="text/javascript" src="suites/spiderfySpec.js"></script>
<script type="text/javascript" src="suites/unspiderfySpec.js"></script>
<script type="text/javascript" src="suites/zoomAnimationSpec.js"></script>
<script type="text/javascript" src="suites/RememberOpacity.js"></script>
<script type="text/javascript" src="suites/supportNegativeZoomSpec.js"></script>
<script type="text/javascript" src="suites/RefreshSpec.js"></script>
<script type="text/javascript" src="suites/removeOutsideVisibleBoundsSpec.js"></script>
<script type="text/javascript" src="suites/nonIntegerZoomSpec.js"></script>
<script>
(window.mochaPhantomJS || window.mocha).run();
</script>
</body>
</html>

View File

@@ -0,0 +1,67 @@
// Karma configuration
var libSources = require(__dirname+'/../build/build.js').getFiles();
var leafletSources = require(__dirname+'/../node_modules/leaflet/build/build.js').getFiles(); // Caution Leaflet 1.0.0-beta.2 build needs magic-string
// base path, that will be used to resolve files and exclude
basePath = '';
for (var i=0; i < libSources.length; i++) {
libSources[i] = "../" + libSources[i];
}
for (var i=0; i < leafletSources.length; i++) {
leafletSources[i] = "../node_modules/leaflet/" + leafletSources[i];
}
// list of files / patterns to load in the browser
files = [].concat([
"../node_modules/mocha/mocha.js",
MOCHA_ADAPTER,
"sinon.js",
"expect.js"
], leafletSources, libSources, [
"after.js",
"happen.js",
"suites/SpecHelper.js",
"suites/**/*.js"
]);
// list of files to exclude
exclude = [
];
// test results reporter to use
// possible values: 'dots', 'progress', 'junit'
reporters = ['dots'];
// web server port
port = 8080;
// cli runner port
runnerPort = 9100;
// enable / disable colors in the output (reporters and logs)
colors = true;
// level of logging
// possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
logLevel = LOG_WARN;
// enable / disable watching file and executing tests whenever any file changes
autoWatch = false;
// Start these browsers, currently available:
// - Chrome
// - ChromeCanary
// - Firefox
// - Opera
// - Safari (only Mac)
// - PhantomJS
// - IE (only Windows)
browsers = ['PhantomJS'];
// If browser does not capture in given timeout [ms], kill it
captureTimeout = 5000;
// Continuous Integration mode
// if true, it capture browsers, run tests and exit
singleRun = true;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,114 @@
describe('addLayer adding multiple markers', function () {
var map, div, clock;
beforeEach(function () {
clock = sinon.useFakeTimers();
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
});
afterEach(function () {
clock.restore();
document.body.removeChild(div);
});
it('creates a cluster when 2 overlapping markers are added before the group is added to the map', function () {
var group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
group.addLayer(marker);
group.addLayer(marker2);
map.addLayer(group);
expect(marker._icon).to.be(undefined);
expect(marker2._icon).to.be(undefined);
expect(map._panes.markerPane.childNodes.length).to.be(1);
});
it('creates a cluster when 2 overlapping markers are added after the group is added to the map', function () {
var group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
map.addLayer(group);
group.addLayer(marker);
group.addLayer(marker2);
expect(marker._icon).to.be(null); //Null as was added and then removed
expect(marker2._icon).to.be(undefined);
expect(map._panes.markerPane.childNodes.length).to.be(1);
});
it('creates a cluster with an animation when 2 overlapping markers are added after the group is added to the map', function () {
var group = new L.MarkerClusterGroup({ animateAddingMarkers: true });
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
map.addLayer(group);
group.addLayer(marker);
group.addLayer(marker2);
expect(marker._icon.parentNode).to.be(map._panes.markerPane);
expect(marker2._icon.parentNode).to.be(map._panes.markerPane);
expect(map._panes.markerPane.childNodes.length).to.be(3);
//Run the the animation
clock.tick(1000);
//Then markers should be removed from map
expect(marker._icon).to.be(null);
expect(marker2._icon).to.be(null);
expect(map._panes.markerPane.childNodes.length).to.be(1);
});
it('creates a cluster and marker when 2 overlapping markers and one non-overlapping are added before the group is added to the map', function () {
var group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
var marker3 = new L.Marker([3.0, 1.5]);
group.addLayer(marker);
group.addLayer(marker2);
group.addLayer(marker3);
map.addLayer(group);
expect(marker._icon).to.be(undefined);
expect(marker2._icon).to.be(undefined);
expect(marker3._icon.parentNode).to.be(map._panes.markerPane);
expect(map._panes.markerPane.childNodes.length).to.be(2);
});
it('creates a cluster and marker when 2 overlapping markers and one non-overlapping are added after the group is added to the map', function () {
var group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
var marker3 = new L.Marker([3.0, 1.5]);
map.addLayer(group);
group.addLayer(marker);
group.addLayer(marker2);
group.addLayer(marker3);
expect(marker._icon).to.be(null); //Null as was added and then removed
expect(marker2._icon).to.be(undefined);
expect(marker3._icon.parentNode).to.be(map._panes.markerPane);
expect(map._panes.markerPane.childNodes.length).to.be(2);
});
});

View File

@@ -0,0 +1,154 @@
describe('addLayer adding a single marker', function () {
/**
* Avoid as much as possible creating and destroying objects for each test.
* Instead, try re-using them, except for the ones under test of course.
* PhantomJS does not perform garbage collection for the life of the page,
* i.e. during the entire test process (Karma runs all tests in a single page).
* http://stackoverflow.com/questions/27239708/how-to-get-around-memory-error-with-karma-phantomjs
*
* The `beforeEach` and `afterEach do not seem to cause much issue.
* => they can still be used to initialize some setup between each test.
* Using them keeps a readable spec/index.
*
* But refrain from re-creating div and map every time. Re-use those objects.
*/
/////////////////////////////
// SETUP FOR EACH TEST
/////////////////////////////
beforeEach(function () {
// Nothing for this test suite.
});
afterEach(function () {
if (group instanceof L.MarkerClusterGroup) {
group.clearLayers();
map.removeLayer(group);
}
// Throw away group as it can be assigned with different configurations between tests.
group = null;
});
/////////////////////////////
// PREPARATION CODE
/////////////////////////////
var div, map, group;
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
// Corresponds to zoom level 8 for the above div dimensions.
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
/////////////////////////////
// TESTS
/////////////////////////////
it('appears when added to the group before the group is added to the map', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
group.addLayer(marker);
map.addLayer(group);
expect(marker._icon).to.not.be(undefined);
expect(marker._icon.parentNode).to.be(map._panes.markerPane);
});
it('appears when added to the group after the group is added to the map', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
map.addLayer(group);
group.addLayer(marker);
expect(marker._icon).to.not.be(undefined);
expect(marker._icon.parentNode).to.be(map._panes.markerPane);
});
it('appears (using animations) when added after the group is added to the map', function () {
group = new L.MarkerClusterGroup({ animateAddingMarkers: true });
var marker = new L.Marker([1.5, 1.5]);
map.addLayer(group);
group.addLayer(marker);
expect(marker._icon).to.not.be(undefined);
expect(marker._icon.parentNode).to.be(map._panes.markerPane);
});
it('does not appear when too far away when added before the group is added to the map', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([3.5, 1.5]);
group.addLayer(marker);
map.addLayer(group);
expect(marker._icon).to.be(undefined);
});
it('does not appear when too far away when added after the group is added to the map', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([3.5, 1.5]);
map.addLayer(group);
group.addLayer(marker);
expect(marker._icon).to.be(undefined);
});
it('passes control to addLayers when marker is a Layer Group', function () {
group = new L.MarkerClusterGroup();
var marker1 = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
var layerGroup = new L.LayerGroup([marker1, marker2]);
map.addLayer(group);
group.addLayer(layerGroup);
expect(group._topClusterLevel.getChildCount()).to.equal(2);
expect(marker1._icon).to.be(undefined);
expect(marker2._icon).to.be(undefined);
expect(map._panes.markerPane.childNodes.length).to.be(1);
});
/////////////////////////////
// CLEAN UP CODE
/////////////////////////////
map.remove();
document.body.removeChild(div);
});

View File

@@ -0,0 +1,159 @@
describe('addLayers adding multiple markers', function () {
/**
* Avoid as much as possible creating and destroying objects for each test.
* Instead, try re-using them, except for the ones under test of course.
* PhantomJS does not perform garbage collection for the life of the page,
* i.e. during the entire test process (Karma runs all tests in a single page).
* http://stackoverflow.com/questions/27239708/how-to-get-around-memory-error-with-karma-phantomjs
*
* The `beforeEach` and `afterEach do not seem to cause much issue.
* => they can still be used to initialize some setup between each test.
* Using them keeps a readable spec/index.
*
* But refrain from re-creating div and map every time. Re-use those objects.
*/
/////////////////////////////
// SETUP FOR EACH TEST
/////////////////////////////
beforeEach(function () {
// Nothing for this test suite.
});
afterEach(function () {
if (group instanceof L.MarkerClusterGroup) {
group.clearLayers();
map.removeLayer(group);
}
// Throw away group as it can be assigned with different configurations between tests.
group = null;
});
/////////////////////////////
// PREPARATION CODE
/////////////////////////////
var div, map, group;
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
// Corresponds to zoom level 8 for the above div dimensions.
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
/////////////////////////////
// TESTS
/////////////////////////////
it('creates a cluster when 2 overlapping markers are added before the group is added to the map', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
group.addLayers([marker, marker2]);
map.addLayer(group);
expect(marker._icon).to.be(undefined);
expect(marker2._icon).to.be(undefined);
expect(map._panes.markerPane.childNodes.length).to.be(1);
});
it('creates a cluster when 2 overlapping markers are added after the group is added to the map', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
map.addLayer(group);
group.addLayers([marker, marker2]);
expect(marker._icon).to.be(undefined);
expect(marker2._icon).to.be(undefined);
expect(map._panes.markerPane.childNodes.length).to.be(1);
});
it('creates a cluster and marker when 2 overlapping markers and one non-overlapping are added before the group is added to the map', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
var marker3 = new L.Marker([3.0, 1.5]);
group.addLayers([marker, marker2, marker3]);
map.addLayer(group);
expect(marker._icon).to.be(undefined);
expect(marker2._icon).to.be(undefined);
expect(marker3._icon.parentNode).to.be(map._panes.markerPane);
expect(map._panes.markerPane.childNodes.length).to.be(2);
});
it('creates a cluster and marker when 2 overlapping markers and one non-overlapping are added after the group is added to the map', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
var marker3 = new L.Marker([3.0, 1.5]);
map.addLayer(group);
group.addLayers([marker, marker2, marker3]);
expect(marker._icon).to.be(undefined);
expect(marker2._icon).to.be(undefined);
expect(marker3._icon.parentNode).to.be(map._panes.markerPane);
expect(map._panes.markerPane.childNodes.length).to.be(2);
});
it('handles nested Layer Groups', function () {
group = new L.MarkerClusterGroup();
var marker1 = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
var marker3 = new L.Marker([3.0, 1.5]);
var layerGroup = new L.LayerGroup([marker1, new L.LayerGroup([marker2])]);
map.addLayer(group);
group.addLayers([layerGroup, marker3]);
expect(marker1._icon).to.be(undefined);
expect(marker2._icon).to.be(undefined);
expect(marker3._icon.parentNode).to.be(map._panes.markerPane);
expect(map._panes.markerPane.childNodes.length).to.be(2);
});
/////////////////////////////
// CLEAN UP CODE
/////////////////////////////
map.remove();
document.body.removeChild(div);
});

View File

@@ -0,0 +1,45 @@
describe('support child markers changing icon', function () {
var map, div, clock;
beforeEach(function () {
clock = sinon.useFakeTimers();
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
});
afterEach(function () {
clock.restore();
document.body.removeChild(div);
});
it('child markers end up with the right icon after becoming unclustered', function () {
var group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5], { icon: new L.DivIcon({html: 'Inner1Text' }) });
var marker2 = new L.Marker([1.5, 1.5]);
map.addLayer(group);
group.addLayer(marker);
expect(marker._icon.parentNode).to.be(map._panes.markerPane);
expect(marker._icon.innerHTML).to.contain('Inner1Text');
group.addLayer(marker2);
expect(marker._icon).to.be(null); //Have been removed from the map
marker.setIcon(new L.DivIcon({ html: 'Inner2Text' })); //Change the icon
group.removeLayer(marker2); //Remove the other marker, so we'll become unclustered
expect(marker._icon.innerHTML).to.contain('Inner2Text');
});
});

View File

@@ -0,0 +1,184 @@
describe('support for CircleMarker elements', function () {
/**
* Avoid as much as possible creating and destroying objects for each test.
* Instead, try re-using them, except for the ones under test of course.
* PhantomJS does not perform garbage collection for the life of the page,
* i.e. during the entire test process (Karma runs all tests in a single page).
* http://stackoverflow.com/questions/27239708/how-to-get-around-memory-error-with-karma-phantomjs
*
* The `beforeEach` and `afterEach do not seem to cause much issue.
* => they can still be used to initialize some setup between each test.
* Using them keeps a readable spec/index.
*
* But refrain from re-creating div and map every time. Re-use those objects.
*/
/////////////////////////////
// SETUP FOR EACH TEST
/////////////////////////////
beforeEach(function () {
clock = sinon.useFakeTimers();
});
afterEach(function () {
if (group instanceof L.MarkerClusterGroup) {
group.removeLayers(group.getLayers());
map.removeLayer(group);
}
// group must be thrown away since we are testing it with a potentially
// different configuration at each test.
group = null;
clock.restore();
clock = null;
});
/////////////////////////////
// PREPARATION CODE
/////////////////////////////
var div, map, group, clock;
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
// Corresponds to zoom level 8 for the above div dimensions.
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
/////////////////////////////
// TESTS
/////////////////////////////
it('appears when added to the group before the group is added to the map', function () {
group = new L.MarkerClusterGroup();
var marker = new L.CircleMarker([1.5, 1.5]);
group.addLayer(marker);
map.addLayer(group);
// Leaflet 1.0.0 now uses an intermediate L.Renderer.
// marker > _path > _rootGroup (g) > _container (svg) > pane (div)
expect(marker._path.parentNode.parentNode).to.not.be(undefined);
expect(marker._path.parentNode.parentNode.parentNode).to.be(map.getPane('overlayPane'));
clock.tick(1000);
});
it('appears when added to the group after the group is added to the map', function () {
group = new L.MarkerClusterGroup();
var marker = new L.CircleMarker([1.5, 1.5]);
group.addLayer(marker);
map.addLayer(group);
expect(marker._path.parentNode.parentNode).to.not.be(undefined);
expect(marker._path.parentNode.parentNode.parentNode).to.be(map.getPane('overlayPane'));
clock.tick(1000);
});
it('appears animated when added to the group after the group is added to the map', function () {
group = new L.MarkerClusterGroup({ animateAddingMarkers: true });
var marker = new L.CircleMarker([1.5, 1.5]);
var marker2 = new L.CircleMarker([1.5, 1.5]);
map.addLayer(group);
group.addLayer(marker);
group.addLayer(marker2);
expect(marker._path.parentNode.parentNode.parentNode).to.be(map.getPane('overlayPane'));
expect(marker2._path.parentNode.parentNode.parentNode).to.be(map.getPane('overlayPane'));
clock.tick(1000);
expect(marker._path.parentNode).to.be(null);
expect(marker2._path.parentNode).to.be(null);
});
it('creates a cluster when 2 overlapping markers are added before the group is added to the map', function () {
group = new L.MarkerClusterGroup();
var marker = new L.CircleMarker([1.5, 1.5]);
var marker2 = new L.CircleMarker([1.5, 1.5]);
group.addLayers([marker, marker2]);
map.addLayer(group);
expect(marker._path).to.be(undefined);
expect(marker2._path).to.be(undefined);
expect(map._panes.markerPane.childNodes.length).to.be(1);
clock.tick(1000);
});
it('creates a cluster when 2 overlapping markers are added after the group is added to the map', function () {
group = new L.MarkerClusterGroup();
var marker = new L.CircleMarker([1.5, 1.5]);
var marker2 = new L.CircleMarker([1.5, 1.5]);
map.addLayer(group);
group.addLayer(marker);
group.addLayer(marker2);
expect(marker._path.parentNode).to.be(null); //Removed then re-added, so null
expect(marker2._path).to.be(undefined);
expect(map._panes.markerPane.childNodes.length).to.be(1);
clock.tick(1000);
});
it('disappears when removed from the group', function () {
group = new L.MarkerClusterGroup();
var marker = new L.CircleMarker([1.5, 1.5]);
group.addLayer(marker);
map.addLayer(group);
expect(marker._path.parentNode).to.not.be(undefined);
expect(marker._path.parentNode.parentNode.parentNode).to.be(map.getPane('overlayPane'));
group.removeLayer(marker);
expect(marker._path.parentNode).to.be(null);
clock.tick(1000);
});
/////////////////////////////
// CLEAN UP CODE
/////////////////////////////
map.remove();
document.body.removeChild(div);
});

View File

@@ -0,0 +1,181 @@
describe('support for Circle elements', function () {
/**
* Avoid as much as possible creating and destroying objects for each test.
* Instead, try re-using them, except for the ones under test of course.
* PhantomJS does not perform garbage collection for the life of the page,
* i.e. during the entire test process (Karma runs all tests in a single page).
* http://stackoverflow.com/questions/27239708/how-to-get-around-memory-error-with-karma-phantomjs
*
* The `beforeEach` and `afterEach do not seem to cause much issue.
* => they can still be used to initialize some setup between each test.
* Using them keeps a readable spec/index.
*
* But refrain from re-creating div and map every time. Re-use those objects.
*/
/////////////////////////////
// SETUP FOR EACH TEST
/////////////////////////////
beforeEach(function () {
clock = sinon.useFakeTimers();
});
afterEach(function () {
if (group instanceof L.MarkerClusterGroup) {
group.removeLayers(group.getLayers());
map.removeLayer(group);
}
// group must be thrown away since we are testing it with a potentially
// different configuration at each test.
group = null;
clock.restore();
clock = null;
});
/////////////////////////////
// PREPARATION CODE
/////////////////////////////
var div, map, group, clock;
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
// Corresponds to zoom level 8 for the above div dimensions.
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
/////////////////////////////
// TESTS
/////////////////////////////
it('appears when added to the group before the group is added to the map', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Circle([1.5, 1.5], 200);
group.addLayer(marker);
map.addLayer(group);
// Leaflet 1.0.0 now uses an intermediate L.Renderer.
// marker > _path > _rootGroup (g) > _container (svg) > pane (div)
expect(marker._path.parentNode).to.not.be(undefined);
expect(marker._path.parentNode.parentNode.parentNode).to.be(map.getPane('overlayPane'));
clock.tick(1000);
});
it('appears when added to the group after the group is added to the map', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Circle([1.5, 1.5], 200);
group.addLayer(marker);
map.addLayer(group);
expect(marker._path.parentNode).to.not.be(undefined);
expect(marker._path.parentNode.parentNode.parentNode).to.be(map.getPane('overlayPane'));
clock.tick(1000);
});
it('appears animated when added to the group after the group is added to the map', function () {
group = new L.MarkerClusterGroup({ animateAddingMarkers: true });
var marker = new L.Circle([1.5, 1.5], 200);
var marker2 = new L.Circle([1.5, 1.5], 200);
map.addLayer(group);
group.addLayer(marker);
group.addLayer(marker2);
expect(marker._path.parentNode.parentNode.parentNode).to.be(map.getPane('overlayPane'));
expect(marker2._path.parentNode.parentNode.parentNode).to.be(map.getPane('overlayPane'));
clock.tick(1000);
});
it('creates a cluster when 2 overlapping markers are added before the group is added to the map', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Circle([1.5, 1.5], 200);
var marker2 = new L.Circle([1.5, 1.5], 200);
group.addLayers([marker, marker2]);
map.addLayer(group);
expect(marker._path).to.be(undefined);
expect(marker2._path).to.be(undefined);
expect(map._panes.markerPane.childNodes.length).to.be(1);
clock.tick(1000);
});
it('creates a cluster when 2 overlapping markers are added after the group is added to the map', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Circle([1.5, 1.5], 200);
var marker2 = new L.Circle([1.5, 1.5], 200);
map.addLayer(group);
group.addLayer(marker);
group.addLayer(marker2);
expect(marker._path.parentNode).to.be(null); //Removed then re-added, so null
expect(marker2._path).to.be(undefined);
expect(map._panes.markerPane.childNodes.length).to.be(1);
clock.tick(1000);
});
it('disappears when removed from the group', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Circle([1.5, 1.5], 200);
group.addLayer(marker);
map.addLayer(group);
expect(marker._path.parentNode).to.not.be(undefined);
expect(marker._path.parentNode.parentNode.parentNode).to.be(map.getPane('overlayPane'));
group.removeLayer(marker);
expect(marker._path.parentNode).to.be(null);
clock.tick(1000);
});
/////////////////////////////
// CLEAN UP CODE
/////////////////////////////
map.remove();
document.body.removeChild(div);
});

View File

@@ -0,0 +1,21 @@
describe('distance grid', function () {
it('addObject', function () {
var grid = new L.DistanceGrid(100),
obj = {};
expect(grid.addObject(obj, { x: 0, y: 0 })).to.eql(undefined);
expect(grid.removeObject(obj, { x: 0, y: 0 })).to.eql(true);
});
it('eachObject', function (done) {
var grid = new L.DistanceGrid(100),
obj = {};
expect(grid.addObject(obj, { x: 0, y: 0 })).to.eql(undefined);
grid.eachObject(function(o) {
expect(o).to.eql(obj);
done();
});
});
});

View File

@@ -0,0 +1,6 @@
describe('L#noConflict', function() {
it('restores the previous L value and returns Leaflet namespace', function(){
expect(L.version).to.be.ok();
});
});

View File

@@ -0,0 +1,276 @@
describe('adding non point data works', function () {
/**
* Avoid as much as possible creating and destroying objects for each test.
* Instead, try re-using them, except for the ones under test of course.
* PhantomJS does not perform garbage collection for the life of the page,
* i.e. during the entire test process (Karma runs all tests in a single page).
* http://stackoverflow.com/questions/27239708/how-to-get-around-memory-error-with-karma-phantomjs
*
* The `beforeEach` and `afterEach do not seem to cause much issue.
* => they can still be used to initialize some setup between each test.
* Using them keeps a readable spec/index.
*
* But refrain from re-creating div and map every time. Re-use those objects.
*/
/////////////////////////////
// SETUP FOR EACH TEST
/////////////////////////////
beforeEach(function () {
// Nothing for this test suite.
});
afterEach(function () {
if (group instanceof L.MarkerClusterGroup) {
group.removeLayers(group.getLayers());
map.removeLayer(group);
}
// group must be thrown away since we are testing it with a potentially
// different configuration at each test.
group = null;
});
/////////////////////////////
// PREPARATION CODE
/////////////////////////////
var div, map, group;
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
// Corresponds to zoom level 8 for the above div dimensions.
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
/////////////////////////////
// TESTS
/////////////////////////////
it('Allows adding a polygon before via addLayer', function () {
group = new L.MarkerClusterGroup();
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0,2.0], [1.5, 2.0]]);
group.addLayer(polygon);
map.addLayer(group);
// Leaflet 1.0.0 now uses an intermediate L.Renderer.
// polygon > _path > _rootGroup (g) > _container (svg) > pane (div)
expect(polygon._path).to.not.be(undefined);
expect(polygon._path.parentNode.parentNode.parentNode).to.be(map.getPane('overlayPane'));
expect(group.hasLayer(polygon));
});
it('Allows adding a polygon before via addLayers([])', function () {
group = new L.MarkerClusterGroup();
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
group.addLayers([polygon]);
map.addLayer(group);
expect(polygon._path).to.not.be(undefined);
expect(polygon._path.parentNode.parentNode.parentNode).to.be(map.getPane('overlayPane'));
});
it('Removes polygons from map when removed', function () {
group = new L.MarkerClusterGroup();
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
group.addLayer(polygon);
map.addLayer(group);
map.removeLayer(group);
expect(polygon._path.parentNode).to.be(null);
});
describe('hasLayer', function () {
it('returns false when not added', function () {
group = new L.MarkerClusterGroup();
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
expect(group.hasLayer(polygon)).to.be(false);
map.addLayer(group);
expect(group.hasLayer(polygon)).to.be(false);
map.addLayer(polygon);
expect(group.hasLayer(polygon)).to.be(false);
});
it('returns true before adding to map', function() {
group = new L.MarkerClusterGroup();
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
group.addLayers([polygon]);
expect(group.hasLayer(polygon)).to.be(true);
});
it('returns true after adding to map after adding polygon', function () {
group = new L.MarkerClusterGroup();
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
group.addLayer(polygon);
map.addLayer(group);
expect(group.hasLayer(polygon)).to.be(true);
});
it('returns true after adding to map before adding polygon', function () {
group = new L.MarkerClusterGroup();
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
map.addLayer(group);
group.addLayer(polygon);
expect(group.hasLayer(polygon)).to.be(true);
});
});
describe('removeLayer', function() {
it('removes before adding to map', function () {
group = new L.MarkerClusterGroup();
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
group.addLayer(polygon);
expect(group.hasLayer(polygon)).to.be(true);
group.removeLayer(polygon);
expect(group.hasLayer(polygon)).to.be(false);
});
it('removes before adding to map', function () {
group = new L.MarkerClusterGroup();
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
group.addLayers([polygon]);
expect(group.hasLayer(polygon)).to.be(true);
group.removeLayer(polygon);
expect(group.hasLayer(polygon)).to.be(false);
});
it('removes after adding to map after adding polygon', function () {
group = new L.MarkerClusterGroup();
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
group.addLayer(polygon);
map.addLayer(group);
expect(group.hasLayer(polygon)).to.be(true);
group.removeLayer(polygon);
expect(group.hasLayer(polygon)).to.be(false);
});
it('removes after adding to map before adding polygon', function () {
group = new L.MarkerClusterGroup();
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
map.addLayer(group);
group.addLayer(polygon);
expect(group.hasLayer(polygon)).to.be(true);
group.removeLayer(polygon);
expect(group.hasLayer(polygon)).to.be(false);
});
});
describe('removeLayers', function () {
it('removes before adding to map', function () {
group = new L.MarkerClusterGroup();
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
group.addLayer(polygon);
expect(group.hasLayer(polygon)).to.be(true);
group.removeLayers([polygon]);
expect(group.hasLayer(polygon)).to.be(false);
});
it('removes before adding to map', function () {
group = new L.MarkerClusterGroup();
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
group.addLayers([polygon]);
expect(group.hasLayer(polygon)).to.be(true);
group.removeLayers([polygon]);
expect(group.hasLayer(polygon)).to.be(false);
});
it('removes after adding to map after adding polygon', function () {
group = new L.MarkerClusterGroup();
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
group.addLayer(polygon);
map.addLayer(group);
expect(group.hasLayer(polygon)).to.be(true);
group.removeLayers([polygon]);
expect(group.hasLayer(polygon)).to.be(false);
});
it('removes after adding to map before adding polygon', function () {
group = new L.MarkerClusterGroup();
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
map.addLayer(group);
group.addLayer(polygon);
expect(group.hasLayer(polygon)).to.be(true);
group.removeLayers([polygon]);
expect(group.hasLayer(polygon)).to.be(false);
});
});
/////////////////////////////
// CLEAN UP CODE
/////////////////////////////
map.remove();
document.body.removeChild(div);
});

View File

@@ -0,0 +1,97 @@
describe('Map pane selection', function() {
/**
* Avoid as much as possible creating and destroying objects for each test.
* Instead, try re-using them, except for the ones under test of course.
* PhantomJS does not perform garbage collection for the life of the page,
* i.e. during the entire test process (Karma runs all tests in a single page).
* http://stackoverflow.com/questions/27239708/how-to-get-around-memory-error-with-karma-phantomjs
*
* The `beforeEach` and `afterEach do not seem to cause much issue.
* => they can still be used to initialize some setup between each test.
* Using them keeps a readable spec/index.
*
* But refrain from re-creating div and map every time. Re-use those objects.
*/
/////////////////////////////
// SETUP FOR EACH TEST
/////////////////////////////
beforeEach(function () {
// Nothing for this test suite.
});
afterEach(function () {
if (group instanceof L.MarkerClusterGroup) {
group.clearLayers();
map.removeLayer(group);
}
// Throw away group as it can be assigned with different configurations between tests.
group = null;
});
/////////////////////////////
// PREPARATION CODE
/////////////////////////////
var div, map, group;
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
// Create map pane
map.createPane('testPane');
// Corresponds to zoom level 8 for the above div dimensions.
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
/////////////////////////////
// TESTS
/////////////////////////////
it('recognizes and applies option', function() {
group = new L.MarkerClusterGroup({clusterPane: 'testPane'});
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
group.addLayers([marker, marker2]);
map.addLayer(group);
expect(map._panes.testPane.childNodes.length).to.be(1);
});
it('defaults to default marker pane', function() {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
group.addLayers([marker, marker2]);
map.addLayer(group);
expect(map._panes[L.Marker.prototype.options.pane].childNodes.length).to.be(1);
});
/////////////////////////////
// CLEAN UP CODE
/////////////////////////////
map.remove();
document.body.removeChild(div);
});

View File

@@ -0,0 +1,52 @@
describe('quickhull', function () {
describe('getDistant', function () {
it('zero distance', function () {
var bl = [
{ lat: 0, lng: 0 },
{ lat: 0, lng: 10 }
];
expect(L.QuickHull.getDistant({ lat: 0, lng: 0 }, bl)).to.eql(0);
});
it('non-zero distance', function () {
var bl = [
{ lat: 0, lng: 0 },
{ lat: 0, lng: 10 }
];
expect(L.QuickHull.getDistant({ lat: 5, lng: 5 }, bl)).to.eql(-50);
});
});
describe('getConvexHull', function () {
it('creates a hull', function () {
expect(L.QuickHull.getConvexHull([ { lat: 0, lng: 0 },
{ lat: 10, lng: 0 },
{ lat: 10, lng: 10 },
{ lat: 0, lng: 10 },
{ lat: 5, lng: 5 }
])).to.eql([
{ lat: 0, lng: 10 },
{ lat: 10, lng: 10 },
{ lat: 10, lng: 0 },
{ lat: 0, lng: 0 }
]);
});
it('creates a hull for vertically-aligned objects', function () {
expect(L.QuickHull.getConvexHull([ { lat: 0, lng: 0 },
{ lat: 5, lng: 0 },
{ lat: 10, lng: 0 }
])).to.eql([
{ lat: 0, lng: 0 },
{ lat: 10, lng: 0 }
]);
});
it('creates a hull for horizontally-aligned objects', function () {
expect(L.QuickHull.getConvexHull([ { lat: 0, lng: 0 },
{ lat: 0, lng: 5 },
{ lat: 0, lng: 10 }
])).to.eql([
{ lat: 0, lng: 0 },
{ lat: 0, lng: 10 }
]);
});
});
});

View File

@@ -0,0 +1,481 @@
describe('refreshClusters', function () {
/**
* Avoid as much as possible creating and destroying objects for each test.
* Instead, try re-using them, except for the ones under test of course.
* PhantomJS does not perform garbage collection for the life of the page,
* i.e. during the entire test process (Karma runs all tests in a single page).
* http://stackoverflow.com/questions/27239708/how-to-get-around-memory-error-with-karma-phantomjs
*
* The `beforeEach` and `afterEach do not seem to cause much issue.
* => they can still be used to initialize some setup between each test.
* Using them keeps a readable spec/index.
*
* But refrain from re-creating div and map every time. Re-use those objects.
*/
/////////////////////////////
// SETUP FOR EACH TEST
/////////////////////////////
beforeEach(function () {
clock = sinon.useFakeTimers();
// Look away to avoid refreshing the display while adding markers.
// By adding markers one by one (instead of using addLayers) we make
// sure we never start an async process.
map.fitBounds(new L.LatLngBounds([
[-11, -11],
[-10, -10]
]))
});
afterEach(function () {
if (group instanceof L.MarkerClusterGroup) {
group.removeLayers(group.getLayers());
map.removeLayer(group);
}
// group must be thrown away since we are testing it with a potentially
// different configuration at each test.
group = null;
clock.restore();
clock = null;
});
/////////////////////////////
// PREPARATION CODE
/////////////////////////////
var div, map, group, clock;
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
// Corresponds to zoom level 8 for the above div dimensions.
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
function getClusterAtZoom(marker, zoom) {
var parent = marker.__parent;
while (parent && parent._zoom !== zoom) {
parent = parent.__parent;
}
return parent;
}
function setMapView() {
// Now look at the markers to force cluster icons drawing.
// Corresponds to zoom level 8 for the above div dimensions.
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
}
/////////////////////////////
// TESTS
/////////////////////////////
it('flags all non-visible parent clusters of a given marker', function () {
group = L.markerClusterGroup().addTo(map);
var marker1 = L.marker([1.5, 1.5]).addTo(group),
marker2 = L.marker([1.5, 1.5]).addTo(group); // Needed to force a cluster.
setMapView();
var marker1cluster10 = getClusterAtZoom(marker1, 10),
marker1cluster2 = getClusterAtZoom(marker1, 2),
marker1cluster5 = getClusterAtZoom(marker1, 5);
// First go to some zoom levels so that Leaflet initializes clusters icons.
expect(marker1cluster10._iconNeedsUpdate).to.be.ok();
map.setZoom(10, {animate: false});
expect(marker1cluster10._iconNeedsUpdate).to.not.be.ok();
expect(marker1cluster2._iconNeedsUpdate).to.be.ok();
map.setZoom(2, {animate: false});
expect(marker1cluster2._iconNeedsUpdate).to.not.be.ok();
// Finish on an intermediate zoom level.
expect(marker1cluster5._iconNeedsUpdate).to.be.ok();
map.setZoom(5, {animate: false});
expect(marker1cluster5._iconNeedsUpdate).to.not.be.ok();
// Run any animation.
clock.tick(1000);
// Then request clusters refresh.
// No need to actually modify the marker.
group.refreshClusters(marker1);
// Now check that non-visible clusters are flagged as "dirty".
expect(marker1cluster10._iconNeedsUpdate).to.be.ok();
expect(marker1cluster2._iconNeedsUpdate).to.be.ok();
// Also check that visible clusters are "un-flagged" since they should be re-drawn.
expect(marker1cluster5._iconNeedsUpdate).to.not.be.ok();
});
it('re-draws visible clusters', function () {
group = L.markerClusterGroup({
iconCreateFunction: function (cluster) {
var markers = cluster.getAllChildMarkers();
for(var i in markers) {
if (markers[i].changed) {
return new L.DivIcon({
className: "changed"
});
}
}
return new L.DivIcon({
className: "original"
});
}
}).addTo(map);
var marker1 = L.marker([1.5, 1.5]).addTo(group),
marker2 = L.marker([1.5, 1.5]).addTo(group); // Needed to force a cluster.
setMapView();
var marker1cluster9 = getClusterAtZoom(marker1, 9);
// First go to some zoom levels so that Leaflet initializes clusters icons.
expect(marker1cluster9._iconNeedsUpdate).to.be.ok();
map.setZoom(9, {animate: false});
expect(marker1cluster9._iconNeedsUpdate).to.not.be.ok();
expect(marker1cluster9._icon.className).to.contain("original");
expect(marker1cluster9._icon.className).to.not.contain("changed");
// Run any animation.
clock.tick(1000);
// Alter the marker.
marker1.changed = true;
// Then request clusters refresh.
group.refreshClusters(marker1);
// Now check that visible clusters icon is re-drawn.
expect(marker1cluster9._icon.className).to.contain("changed");
expect(marker1cluster9._icon.className).to.not.contain("original");
});
// Shared code for the 2 below tests
function iconCreateFunction(cluster) {
var markers = cluster.getAllChildMarkers();
for(var i in markers) {
if (markers[i].changed) {
return new L.DivIcon({
className: "changed"
});
}
}
return new L.DivIcon({
className: "original"
});
}
it('re-draws markers in singleMarkerMode', function () {
group = L.markerClusterGroup({
singleMarkerMode: true,
iconCreateFunction: iconCreateFunction
}).addTo(map);
var marker1 = L.marker([1.5, 1.5]).addTo(group);
setMapView();
expect(marker1._icon.className).to.contain("original");
// Alter the marker.
marker1.changed = true;
// Then request clusters refresh.
group.refreshClusters(marker1);
expect(marker1._icon.className).to.contain("changed");
expect(marker1._icon.className).to.not.contain("original");
});
it('does not modify markers that do not belong to the current group (in singleMarkerMode)', function () {
group = L.markerClusterGroup({
singleMarkerMode: true,
iconCreateFunction: iconCreateFunction
}).addTo(map);
var marker1 = L.marker([1.5, 1.5]).addTo(group),
marker2 = L.marker([1.5, 1.5], {
icon: iconCreateFunction({
getAllChildMarkers: function () {
return marker2;
}
})
}).addTo(map);
setMapView();
expect(marker1._icon.className).to.contain("original");
expect(marker2._icon.className).to.contain("original");
// Alter the markers.
marker1.changed = true;
marker2.changed = true;
// Then request clusters refresh.
group.refreshClusters([marker1, marker2]);
expect(marker1._icon.className).to.contain("changed");
expect(marker1._icon.className).to.not.contain("original");
expect(marker2._icon.className).to.contain("original");
expect(marker2._icon.className).to.not.contain("changed");
});
// Shared code for below tests.
var marker1 = L.marker([1.5, 1.5]),
marker2 = L.marker([1.5, 1.5]), // Needed to force a cluster.
marker3 = L.marker([1.1, 1.1]),
marker4 = L.marker([1.1, 1.1]), // Needed to force a cluster.
marker5 = L.marker([1.9, 1.9]),
marker6 = L.marker([1.9, 1.9]), // Needed to force a cluster.
marker1cluster8,
marker1cluster3,
marker1cluster5,
marker3cluster8,
marker3cluster3,
marker3cluster5,
marker5cluster8,
marker5cluster3,
marker5cluster5;
function init3clusterBranches() {
group = L.markerClusterGroup({
maxClusterRadius: 2 // Make sure we keep distinct clusters.
}).addTo(map);
// Populate Marker Cluster Group.
marker1.addTo(group);
marker2.addTo(group);
marker3.addTo(group);
marker4.addTo(group);
marker5.addTo(group);
marker6.addTo(group);
setMapView();
marker1cluster8 = getClusterAtZoom(marker1, 8);
marker1cluster3 = getClusterAtZoom(marker1, 3);
marker1cluster5 = getClusterAtZoom(marker1, 5);
marker3cluster8 = getClusterAtZoom(marker3, 8);
marker3cluster3 = getClusterAtZoom(marker3, 3);
marker3cluster5 = getClusterAtZoom(marker3, 5);
marker5cluster8 = getClusterAtZoom(marker5, 8);
marker5cluster3 = getClusterAtZoom(marker5, 3);
marker5cluster5 = getClusterAtZoom(marker5, 5);
// Make sure we have 3 distinct clusters up to zoom level Z (let's choose Z = 3)
expect(marker1cluster3._childCount).to.equal(2);
expect(marker3cluster3._childCount).to.equal(2);
expect(marker5cluster3._childCount).to.equal(2);
// First go to some zoom levels so that Leaflet initializes clusters icons.
expect(marker1cluster8._iconNeedsUpdate).to.not.be.ok();
expect(marker3cluster8._iconNeedsUpdate).to.not.be.ok();
expect(marker5cluster8._iconNeedsUpdate).to.not.be.ok();
expect(marker1cluster3._iconNeedsUpdate).to.be.ok();
expect(marker3cluster3._iconNeedsUpdate).to.be.ok();
expect(marker5cluster3._iconNeedsUpdate).to.be.ok();
map.setZoom(3, {animate: false});
expect(marker1cluster3._iconNeedsUpdate).to.not.be.ok();
expect(marker3cluster3._iconNeedsUpdate).to.not.be.ok();
expect(marker5cluster3._iconNeedsUpdate).to.not.be.ok();
// Finish on an intermediate zoom level.
expect(marker1cluster5._iconNeedsUpdate).to.be.ok();
expect(marker3cluster5._iconNeedsUpdate).to.be.ok();
expect(marker5cluster5._iconNeedsUpdate).to.be.ok();
map.setZoom(5, {animate: false});
expect(marker1cluster5._iconNeedsUpdate).to.not.be.ok();
expect(marker3cluster5._iconNeedsUpdate).to.not.be.ok();
expect(marker5cluster5._iconNeedsUpdate).to.not.be.ok();
// Run any animation.
clock.tick(1000);
// Ready to refresh clusters with method of choice and assess result.
}
it('does not flag clusters of other markers', function () {
init3clusterBranches();
// Then request clusters refresh.
// No need to actually modify the marker.
group.refreshClusters(marker1);
// Now check that non-visible clusters are flagged as "dirty".
expect(marker1cluster8._iconNeedsUpdate).to.be.ok();
expect(marker1cluster3._iconNeedsUpdate).to.be.ok();
// Finally check that non-involved clusters are not "dirty".
expect(marker3cluster8._iconNeedsUpdate).to.not.be.ok();
expect(marker3cluster3._iconNeedsUpdate).to.not.be.ok();
expect(marker5cluster8._iconNeedsUpdate).to.not.be.ok();
expect(marker5cluster3._iconNeedsUpdate).to.not.be.ok();
});
it('processes itself when no argument is passed', function () {
init3clusterBranches();
// Then request clusters refresh.
// No need to actually modify the marker.
group.refreshClusters();
// Now check that non-visible clusters are flagged as "dirty".
expect(marker1cluster8._iconNeedsUpdate).to.be.ok();
expect(marker1cluster3._iconNeedsUpdate).to.be.ok();
expect(marker3cluster8._iconNeedsUpdate).to.be.ok();
expect(marker3cluster3._iconNeedsUpdate).to.be.ok();
expect(marker5cluster8._iconNeedsUpdate).to.be.ok();
expect(marker5cluster3._iconNeedsUpdate).to.be.ok();
});
it('accepts an array of markers', function () {
init3clusterBranches();
// Then request clusters refresh.
// No need to actually modify the markers.
group.refreshClusters([marker1, marker5]);
// Clusters of marker3 and 4 shall not be flagged.
// Now check that non-visible clusters are flagged as "dirty".
expect(marker1cluster8._iconNeedsUpdate).to.be.ok();
expect(marker1cluster3._iconNeedsUpdate).to.be.ok();
expect(marker5cluster8._iconNeedsUpdate).to.be.ok();
expect(marker5cluster3._iconNeedsUpdate).to.be.ok();
// Clusters of marker3 and 4 shall not be flagged.
expect(marker3cluster8._iconNeedsUpdate).to.not.be.ok();
expect(marker3cluster3._iconNeedsUpdate).to.not.be.ok();
});
it('accepts a mapping of markers', function () {
init3clusterBranches();
// Then request clusters refresh.
// No need to actually modify the markers.
group.refreshClusters({
id1: marker1,
id2: marker5
}); // Clusters of marker3 and 4 shall not be flagged.
// Now check that non-visible clusters are flagged as "dirty".
expect(marker1cluster8._iconNeedsUpdate).to.be.ok();
expect(marker1cluster3._iconNeedsUpdate).to.be.ok();
expect(marker5cluster8._iconNeedsUpdate).to.be.ok();
expect(marker5cluster3._iconNeedsUpdate).to.be.ok();
// Clusters of marker3 and 4 shall not be flagged.
expect(marker3cluster8._iconNeedsUpdate).to.not.be.ok();
expect(marker3cluster3._iconNeedsUpdate).to.not.be.ok();
});
it('accepts an L.LayerGroup', function () {
init3clusterBranches();
// Then request clusters refresh.
// No need to actually modify the markers.
var layerGroup = new L.LayerGroup([marker1, marker5]);
group.refreshClusters(layerGroup);
// Clusters of marker3 and 4 shall not be flagged.
// Now check that non-visible clusters are flagged as "dirty".
expect(marker1cluster8._iconNeedsUpdate).to.be.ok();
expect(marker1cluster3._iconNeedsUpdate).to.be.ok();
expect(marker5cluster8._iconNeedsUpdate).to.be.ok();
expect(marker5cluster3._iconNeedsUpdate).to.be.ok();
// Clusters of marker3 and 4 shall not be flagged.
expect(marker3cluster8._iconNeedsUpdate).to.not.be.ok();
expect(marker3cluster3._iconNeedsUpdate).to.not.be.ok();
});
it('accepts an L.MarkerCluster', function () {
init3clusterBranches();
// Then request clusters refresh.
// No need to actually modify the markers.
group.refreshClusters(marker1cluster8);
// Clusters of marker3, 4, 5 and 6 shall not be flagged.
// Now check that non-visible clusters are flagged as "dirty".
expect(marker1cluster8._iconNeedsUpdate).to.be.ok();
expect(marker1cluster3._iconNeedsUpdate).to.be.ok();
// Clusters of marker3 and 4 shall not be flagged.
expect(marker3cluster8._iconNeedsUpdate).to.not.be.ok();
expect(marker3cluster3._iconNeedsUpdate).to.not.be.ok();
expect(marker5cluster8._iconNeedsUpdate).to.not.be.ok();
expect(marker5cluster3._iconNeedsUpdate).to.not.be.ok();
});
/////////////////////////////
// CLEAN UP CODE
/////////////////////////////
map.remove();
document.body.removeChild(div);
});

View File

@@ -0,0 +1,151 @@
describe('Remember opacity', function () {
var map, div, clock, markers;
var markerDefs = [
{latLng: [ 0, 0], opts: {opacity: 0.9}},
{latLng: [ 0, 1], opts: {opacity: 0.5}},
{latLng: [ 0,-1], opts: {opacity: 0.5}},
{latLng: [ 1, 0], opts: {opacity: 0.5}},
{latLng: [-1, 0], opts: {opacity: 0.5}},
{latLng: [ 1, 1], opts: {opacity: 0.2}},
{latLng: [ 1,-1], opts: {opacity: 0.2}},
{latLng: [-1, 1], opts: {opacity: 0.2}},
{latLng: [-1,-1], opts: {opacity: 0.2}}
];
var bounds = L.latLngBounds( L.latLng( -1.1, -1.1),
L.latLng( 1.1, 1.1) );
beforeEach(function () {
clock = sinon.useFakeTimers();
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
markers = [];
for (var i = 0; i < markerDefs.length; i++) {
markers.push( L.marker(markerDefs[i].latLng, markerDefs[i].opts ) );
}
});
afterEach(function () {
clock.restore();
document.body.removeChild(div);
});
it('clusters semitransparent markers into an opaque one', function () {
map.setView(new L.LatLng(0,0), 1);
var clusterLayer = new L.MarkerClusterGroup({
maxClusterRadius: 20
});
clusterLayer.addLayers(markers);
map.addLayer(clusterLayer);
var visibleClusters = clusterLayer._featureGroup.getLayers();
expect(visibleClusters.length).to.be(1);
expect(visibleClusters[0].options.opacity).to.be(1);
});
it('unclusters an opaque marker into semitransparent ones', function () {
map.setView(new L.LatLng(0,0), 1);
var visibleClusters;
var clusterLayer = new L.MarkerClusterGroup({
maxClusterRadius: 20
});
clusterLayer.addLayers(markers);
map.addLayer(clusterLayer);
map.fitBounds(bounds);
clock.tick(1000);
visibleClusters = clusterLayer._featureGroup.getLayers();
expect(visibleClusters.length).to.be(9);
for (var i=0; i<9; i++) {
expect(visibleClusters[i].options.opacity).to.be.within(0.2,0.9);
}
// It shall also work after zooming in/out a second time.
map.setView(new L.LatLng(0,0), 1);
clock.tick(1000);
map.fitBounds(bounds);
clock.tick(1000);
visibleClusters = clusterLayer._featureGroup.getLayers();
expect(visibleClusters.length).to.be(9);
for (var i=0; i<9; i++) {
expect(visibleClusters[i].options.opacity).to.be.within(0.2,0.9);
}
});
it('has no problems zooming in and out several times', function () {
var visibleClusters;
var clusterLayer = new L.MarkerClusterGroup({
maxClusterRadius: 20
});
clusterLayer.addLayers(markers);
map.addLayer(clusterLayer);
// Zoom in and out a couple times
for (var i=0; i<10; i++) {
map.fitBounds(bounds);
clock.tick(1000);
visibleClusters = clusterLayer._featureGroup.getLayers();
expect(visibleClusters.length).to.be(9);
for (var i=0; i<9; i++) {
expect(visibleClusters[i].options.opacity).to.be.within(0.2,0.9);
}
map.setView(new L.LatLng(0,0), 1);
clock.tick(1000);
visibleClusters = clusterLayer._featureGroup.getLayers();
expect(visibleClusters.length).to.be(1);
expect(visibleClusters[0].options.opacity).to.be(1);
}
});
it('retains the opacity of each individual marker', function () {
map.setView(new L.LatLng(0,0), 1);
var visibleClusters;
var clusterLayer = new L.MarkerClusterGroup({
maxClusterRadius: 20
});
clusterLayer.addLayers(markers);
map.addLayer(clusterLayer);
// Zoom in and out a couple times
for (var i=0; i<5; i++) {
map.fitBounds(bounds);
clock.tick(1000);
map.setView(new L.LatLng(0,0), 1);
clock.tick(1000);
}
for (var i=0; i<markerDefs.length; i++) {
// console.log(markerDefs[i].latLng, markerDefs[i].opts.opacity);
map.setView(L.latLng(markerDefs[i].latLng), 18);
clock.tick(1000);
visibleClusters = clusterLayer._featureGroup.getLayers();
expect(visibleClusters.length).to.be(1);
expect(visibleClusters[0].options.opacity).to.be(markerDefs[i].opts.opacity);
}
});
});

View File

@@ -0,0 +1,239 @@
describe('removeLayer', function () {
/**
* Avoid as much as possible creating and destroying objects for each test.
* Instead, try re-using them, except for the ones under test of course.
* PhantomJS does not perform garbage collection for the life of the page,
* i.e. during the entire test process (Karma runs all tests in a single page).
* http://stackoverflow.com/questions/27239708/how-to-get-around-memory-error-with-karma-phantomjs
*
* The `beforeEach` and `afterEach do not seem to cause much issue.
* => they can still be used to initialize some setup between each test.
* Using them keeps a readable spec/index.
*
* But refrain from re-creating div and map every time. Re-use those objects.
*/
/////////////////////////////
// SETUP FOR EACH TEST
/////////////////////////////
beforeEach(function () {
clock = sinon.useFakeTimers();
});
afterEach(function () {
if (group instanceof L.MarkerClusterGroup) {
group.clearLayers();
map.removeLayer(group);
}
// Throw away group as it can be assigned with different configurations between tests.
group = null;
clock.restore();
});
/////////////////////////////
// PREPARATION CODE
/////////////////////////////
var div, map, group, clock;
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
// Corresponds to zoom level 8 for the above div dimensions.
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
/////////////////////////////
// TESTS
/////////////////////////////
it('removes a layer that was added to it', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
map.addLayer(group);
group.addLayer(marker);
expect(marker._icon.parentNode).to.be(map._panes.markerPane);
group.removeLayer(marker);
expect(marker._icon).to.be(null);
});
it('doesnt remove a layer not added to it', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
map.addLayer(group);
map.addLayer(marker);
expect(marker._icon.parentNode).to.be(map._panes.markerPane);
group.removeLayer(marker);
expect(marker._icon.parentNode).to.be(map._panes.markerPane);
});
it('removes a layer that was added to it (before being on the map) that is shown in a cluster', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
group.addLayers([marker, marker2]);
map.addLayer(group);
group.removeLayer(marker);
expect(marker._icon).to.be(undefined);
expect(marker2._icon.parentNode).to.be(map._panes.markerPane);
});
it('removes a layer that was added to it (after being on the map) that is shown in a cluster', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
map.addLayer(group);
group.addLayer(marker);
group.addLayer(marker2);
group.removeLayer(marker);
expect(marker._icon).to.be(null);
expect(marker2._icon.parentNode).to.be(map._panes.markerPane);
});
it('removes a layer that was added to it (before being on the map) that is individually', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1, 1.5]);
var marker2 = new L.Marker([3, 1.5]);
map.addLayer(group);
group.addLayer(marker);
group.addLayer(marker2);
expect(marker._icon.parentNode).to.be(map._panes.markerPane);
expect(marker2._icon.parentNode).to.be(map._panes.markerPane);
group.removeLayer(marker);
expect(marker._icon).to.be(null);
expect(marker2._icon.parentNode).to.be(map._panes.markerPane);
});
it('removes a layer (with animation) that was added to it (after being on the map) that is shown in a cluster', function () {
group = new L.MarkerClusterGroup({ animateAddingMarkers: true });
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
map.addLayer(group);
group.addLayer(marker);
group.addLayer(marker2);
//Run the the animation
clock.tick(1000);
expect(marker._icon).to.be(null);
expect(marker2._icon).to.be(null);
group.removeLayer(marker);
//Run the the animation
clock.tick(1000);
expect(marker._icon).to.be(null);
expect(marker2._icon.parentNode).to.be(map._panes.markerPane);
});
it('removes the layers that are in the given LayerGroup', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
map.addLayer(group);
group.addLayers([marker, marker2]);
var layer = L.layerGroup();
layer.addLayer(marker2);
group.removeLayer(layer);
expect(marker._icon).to.not.be(undefined);
expect(marker2._icon).to.be(undefined);
});
it('removes the layers that are in the given LayerGroup when not on the map', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
group.addLayers([marker, marker2]);
var layer = L.layerGroup();
layer.addLayer(marker2);
group.removeLayer(layer);
expect(group.hasLayer(marker)).to.be(true);
expect(group.hasLayer(marker2)).to.be(false);
});
it('passes control to removeLayers when marker is a Layer Group', function () {
group = new L.MarkerClusterGroup();
var marker1 = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
group.addLayers([marker1, marker2]);
var layer = L.layerGroup();
layer.addLayer(marker2);
group.removeLayer(new L.LayerGroup([layer]));
expect(group.hasLayer(marker1)).to.be(true);
expect(group.hasLayer(marker2)).to.be(false);
});
/////////////////////////////
// CLEAN UP CODE
/////////////////////////////
map.remove();
document.body.removeChild(div);
});

View File

@@ -0,0 +1,26 @@
function noSpecs() {
xit('has no specs');
}
if (!Array.prototype.map) {
Array.prototype.map = function(fun /*, thisp */) {
"use strict";
if (this === void 0 || this === null)
throw new TypeError();
var t = Object(this);
var len = t.length >>> 0;
if (typeof fun !== "function")
throw new TypeError();
var res = new Array(len);
var thisp = arguments[1];
for (var i = 0; i < len; i++) {
if (i in t)
res[i] = fun.call(thisp, t[i], i, t);
}
return res;
};
}

View File

@@ -0,0 +1,147 @@
describe('animate option', function () {
/**
* Avoid as much as possible creating and destroying objects for each test.
* Instead, try re-using them, except for the ones under test of course.
* PhantomJS does not perform garbage collection for the life of the page,
* i.e. during the entire test process (Karma runs all tests in a single page).
* http://stackoverflow.com/questions/27239708/how-to-get-around-memory-error-with-karma-phantomjs
*
* The `beforeEach` and `afterEach do not seem to cause much issue.
* => they can still be used to initialize some setup between each test.
* Using them keeps a readable spec/index.
*
* But refrain from re-creating div and map every time. Re-use those objects.
*/
/////////////////////////////
// SETUP FOR EACH TEST
/////////////////////////////
beforeEach(function () {
previousTransitionSetting = L.DomUtil.TRANSITION;
});
afterEach(function () {
// Restore previous setting so that next tests are unaffected.
L.DomUtil.TRANSITION = previousTransitionSetting;
if (group instanceof L.MarkerClusterGroup) {
group.removeLayers(group.getLayers());
map.removeLayer(group);
}
// Throw away group as it can be assigned with different configurations between tests.
group = null;
});
/////////////////////////////
// PREPARATION CODE
/////////////////////////////
var div, map, group, previousTransitionSetting;
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
// Corresponds to zoom level 8 for the above div dimensions.
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
/////////////////////////////
// TESTS
/////////////////////////////
it('hooks animated methods version by default', function () {
// Need to add to map so that we have the top cluster level created.
group = L.markerClusterGroup().addTo(map);
var withAnimation = L.MarkerClusterGroup.prototype._withAnimation;
// MCG animated methods.
expect(group._animationStart).to.be(withAnimation._animationStart);
expect(group._animationZoomIn).to.be(withAnimation._animationZoomIn);
expect(group._animationZoomOut).to.be(withAnimation._animationZoomOut);
expect(group._animationAddLayer).to.be(withAnimation._animationAddLayer);
// MarkerCluster spiderfy animated methods
var cluster = group._topClusterLevel;
withAnimation = L.MarkerCluster.prototype;
expect(cluster._animationSpiderfy).to.be(withAnimation._animationSpiderfy);
expect(cluster._animationUnspiderfy).to.be(withAnimation._animationUnspiderfy);
});
it('hooks non-animated methods version when set to false', function () {
// Need to add to map so that we have the top cluster level created.
group = L.markerClusterGroup({animate: false}).addTo(map);
var noAnimation = L.MarkerClusterGroup.prototype._noAnimation;
// MCG non-animated methods.
expect(group._animationStart).to.be(noAnimation._animationStart);
expect(group._animationZoomIn).to.be(noAnimation._animationZoomIn);
expect(group._animationZoomOut).to.be(noAnimation._animationZoomOut);
expect(group._animationAddLayer).to.be(noAnimation._animationAddLayer);
// MarkerCluster spiderfy non-animated methods
var cluster = group._topClusterLevel;
noAnimation = L.MarkerClusterNonAnimated.prototype;
expect(cluster._animationSpiderfy).to.be(noAnimation._animationSpiderfy);
expect(cluster._animationUnspiderfy).to.be(noAnimation._animationUnspiderfy);
});
it('always hooks non-animated methods version when L.DomUtil.TRANSITION is false', function () {
// Fool Leaflet, make it think the browser does not support transitions.
L.DomUtil.TRANSITION = false;
// Need to add to map so that we have the top cluster level created.
group = L.markerClusterGroup({animate: true}).addTo(map);
var noAnimation = L.MarkerClusterGroup.prototype._noAnimation;
// MCG non-animated methods.
expect(group._animationStart).to.be(noAnimation._animationStart);
expect(group._animationZoomIn).to.be(noAnimation._animationZoomIn);
expect(group._animationZoomOut).to.be(noAnimation._animationZoomOut);
expect(group._animationAddLayer).to.be(noAnimation._animationAddLayer);
// MarkerCluster spiderfy non-animated methods
var cluster = group._topClusterLevel;
noAnimation = L.MarkerClusterNonAnimated.prototype;
expect(cluster._animationSpiderfy).to.be(noAnimation._animationSpiderfy);
expect(cluster._animationUnspiderfy).to.be(noAnimation._animationUnspiderfy);
});
/////////////////////////////
// CLEAN UP CODE
/////////////////////////////
map.remove();
document.body.removeChild(div);
});

View File

@@ -0,0 +1,44 @@
describe('clearLayer', function () {
var map, div;
beforeEach(function () {
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
});
afterEach(function () {
document.body.removeChild(div);
});
it('clears everything before adding to map', function () {
var group = new L.MarkerClusterGroup();
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
var marker = new L.Marker([1.5, 1.5]);
group.addLayers([polygon, marker]);
group.clearLayers();
expect(group.hasLayer(polygon)).to.be(false);
expect(group.hasLayer(marker)).to.be(false);
});
it('hits polygons and markers after adding to map', function () {
var group = new L.MarkerClusterGroup();
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
var marker = new L.Marker([1.5, 1.5]);
group.addLayers([polygon, marker]);
map.addLayer(group);
group.clearLayers();
expect(group.hasLayer(polygon)).to.be(false);
expect(group.hasLayer(marker)).to.be(false);
});
});

View File

@@ -0,0 +1,103 @@
describe('disableClusteringAtZoom option', function () {
/**
* Avoid as much as possible creating and destroying objects for each test.
* Instead, try re-using them, except for the ones under test of course.
* PhantomJS does not perform garbage collection for the life of the page,
* i.e. during the entire test process (Karma runs all tests in a single page).
* http://stackoverflow.com/questions/27239708/how-to-get-around-memory-error-with-karma-phantomjs
*
* The `beforeEach` and `afterEach do not seem to cause much issue.
* => they can still be used to initialize some setup between each test.
* Using them keeps a readable spec/index.
*
* But refrain from re-creating div and map every time. Re-use those objects.
*/
/////////////////////////////
// SETUP FOR EACH TEST
/////////////////////////////
beforeEach(function () {
clock = sinon.useFakeTimers();
});
afterEach(function () {
if (group instanceof L.MarkerClusterGroup) {
group.clearLayers();
map.removeLayer(group);
}
// group must be thrown away since we are testing it with a potentially
// different configuration at each test.
group = null;
clock.restore();
clock = null;
});
/////////////////////////////
// PREPARATION CODE
/////////////////////////////
var div, map, group, clock;
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
// Corresponds to zoom level 8 for the above div dimensions.
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
/////////////////////////////
// TESTS
/////////////////////////////
it('unclusters at zoom level equal or higher', function () {
var maxZoom = 15;
group = new L.MarkerClusterGroup({
disableClusteringAtZoom: maxZoom
});
group.addLayers([
new L.Marker([1.5, 1.5]),
new L.Marker([1.5, 1.5])
]);
map.addLayer(group);
expect(group._maxZoom).to.equal(maxZoom - 1);
expect(map._panes.markerPane.childNodes.length).to.equal(1); // 1 cluster.
map.setZoom(14);
clock.tick(1000);
expect(map._panes.markerPane.childNodes.length).to.equal(1); // 1 cluster.
map.setZoom(15);
clock.tick(1000);
expect(map._panes.markerPane.childNodes.length).to.equal(2); // 2 markers.
});
/////////////////////////////
// CLEAN UP CODE
/////////////////////////////
map.remove();
document.body.removeChild(div);
});

View File

@@ -0,0 +1,54 @@
describe('eachLayer', function () {
var map, div;
beforeEach(function () {
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
});
afterEach(function () {
document.body.removeChild(div);
});
it('hits polygons and markers before adding to map', function () {
var group = new L.MarkerClusterGroup();
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
var marker = new L.Marker([1.5, 1.5]);
group.addLayers([polygon, marker]);
var layers = [];
group.eachLayer(function (l) {
layers.push(l);
});
expect(layers.length).to.be(2);
expect(layers).to.contain(marker);
expect(layers).to.contain(polygon);
});
it('hits polygons and markers after adding to map', function () {
var group = new L.MarkerClusterGroup();
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
var marker = new L.Marker([1.5, 1.5]);
group.addLayers([polygon, marker]);
map.addLayer(group);
var layers = [];
group.eachLayer(function (l) {
layers.push(l);
});
expect(layers.length).to.be(2);
expect(layers).to.contain(marker);
expect(layers).to.contain(polygon);
});
});

View File

@@ -0,0 +1,409 @@
describe('events', function() {
/**
* Avoid as much as possible creating and destroying objects for each test.
* Instead, try re-using them, except for the ones under test of course.
* PhantomJS does not perform garbage collection for the life of the page,
* i.e. during the entire test process (Karma runs all tests in a single page).
* http://stackoverflow.com/questions/27239708/how-to-get-around-memory-error-with-karma-phantomjs
*
* The `beforeEach` and `afterEach do not seem to cause much issue.
* => they can still be used to initialize some setup between each test.
* Using them keeps a readable spec/index.
*
* But refrain from re-creating div and map every time. Re-use those objects.
*/
/////////////////////////////
// SETUP FOR EACH TEST
/////////////////////////////
beforeEach(function () {
//
});
afterEach(function () {
if (group instanceof L.MarkerClusterGroup) {
group.removeLayers(group.getLayers());
map.removeLayer(group);
}
// group must be thrown away since we are testing it with a potentially
// different configuration at each test.
group = null;
});
/////////////////////////////
// PREPARATION CODE
/////////////////////////////
var div, map, group;
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
// Corresponds to zoom level 8 for the above div dimensions.
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
/////////////////////////////
// TESTS
/////////////////////////////
it('is fired for a single child marker', function () {
var callback = sinon.spy();
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
group.on('click', callback);
group.addLayer(marker);
map.addLayer(group);
// In Leaflet 1.0.0, event propagation must be explicitly set by 3rd argument.
marker.fire('click', null, true);
expect(callback.called).to.be(true);
});
it('is fired for a child polygon', function () {
var callback = sinon.spy();
group = new L.MarkerClusterGroup();
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
group.on('click', callback);
group.addLayer(polygon);
map.addLayer(group);
polygon.fire('click', null, true);
expect(callback.called).to.be(true);
});
it('is fired for a cluster click', function () {
var callback = sinon.spy();
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
group.on('clusterclick', callback);
group.addLayers([marker, marker2]);
map.addLayer(group);
var cluster = group.getVisibleParent(marker);
expect(cluster instanceof L.MarkerCluster).to.be(true);
cluster.fire('click', null, true);
expect(callback.called).to.be(true);
});
describe('after being added, removed, re-added from the map', function() {
it('still fires events for nonpoint data', function() {
var callback = sinon.spy();
group = new L.MarkerClusterGroup();
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
group.on('click', callback);
group.addLayer(polygon);
map.addLayer(group);
map.removeLayer(group);
map.addLayer(group);
polygon.fire('click', null, true);
expect(callback.called).to.be(true);
});
it('still fires events for point data', function() {
var callback = sinon.spy();
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
group.on('click', callback);
group.addLayer(marker);
map.addLayer(group);
map.removeLayer(group);
map.addLayer(group);
marker.fire('click', null, true);
expect(callback.called).to.be(true);
});
it('still fires cluster events', function() {
var callback = sinon.spy();
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
group.on('clusterclick', callback);
group.addLayers([marker, marker2]);
map.addLayer(group);
map.removeLayer(group);
map.addLayer(group);
var cluster = group.getVisibleParent(marker);
expect(cluster instanceof L.MarkerCluster).to.be(true);
cluster.fire('click', null, true);
expect(callback.called).to.be(true);
});
it('does not break map events', function () {
var callback = sinon.spy();
group = new L.MarkerClusterGroup();
map.on('zoomend', callback);
map.addLayer(group);
map.removeLayer(group);
map.addLayer(group);
map.fire('zoomend');
expect(callback.called).to.be(true);
});
//layeradd
it('fires layeradd when markers are added while not on the map', function() {
var callback = sinon.spy();
group = new L.MarkerClusterGroup();
group.on('layeradd', callback);
var marker = new L.Marker([1.5, 1.5]);
group.addLayer(marker);
expect(callback.callCount).to.be(1);
});
it('fires layeradd when vectors are added while not on the map', function() {
var callback = sinon.spy();
group = new L.MarkerClusterGroup();
group.on('layeradd', callback);
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
group.addLayer(polygon);
expect(callback.callCount).to.be(1);
});
it('fires layeradd when markers are added while on the map', function() {
var callback = sinon.spy();
group = new L.MarkerClusterGroup();
group.on('layeradd', callback);
map.addLayer(group);
var marker = new L.Marker([1.5, 1.5]);
group.addLayer(marker);
expect(callback.callCount).to.be(1);
});
it('fires layeradd when vectors are added while on the map', function() {
var callback = sinon.spy();
group = new L.MarkerClusterGroup();
group.on('layeradd', callback);
map.addLayer(group);
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
group.addLayer(polygon);
expect(callback.callCount).to.be(1);
});
it('fires layeradd when markers are added using addLayers while on the map with chunked loading', function() {
var callback = sinon.spy();
group = new L.MarkerClusterGroup({ chunkedLoading: true });
group.on('layeradd', callback);
map.addLayer(group);
var marker = new L.Marker([1.5, 1.5]);
group.addLayers([marker]);
expect(callback.callCount).to.be(1);
});
it('fires layeradd when vectors are added using addLayers while on the map with chunked loading', function() {
var callback = sinon.spy();
group = new L.MarkerClusterGroup({ chunkedLoading: true });
group.on('layeradd', callback);
map.addLayer(group);
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
group.addLayers([polygon]);
expect(callback.callCount).to.be(1);
});
//layerremove
it('fires layerremove when a marker is removed while not on the map', function() {
var callback = sinon.spy();
group = new L.MarkerClusterGroup();
group.on('layerremove', callback);
var marker = new L.Marker([1.5, 1.5]);
group.addLayer(marker);
group.removeLayer(marker);
expect(callback.callCount).to.be(1);
});
it('fires layerremove when a vector is removed while not on the map', function() {
var callback = sinon.spy();
group = new L.MarkerClusterGroup();
group.on('layerremove', callback);
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
group.addLayer(polygon);
group.removeLayer(polygon);
expect(callback.callCount).to.be(1);
});
it('fires layerremove when a marker is removed while on the map', function() {
var callback = sinon.spy();
group = new L.MarkerClusterGroup();
group.on('layerremove', callback);
map.addLayer(group);
var marker = new L.Marker([1.5, 1.5]);
group.addLayer(marker);
group.removeLayer(marker);
expect(callback.callCount).to.be(1);
});
it('fires layerremove when a vector is removed while on the map', function() {
var callback = sinon.spy();
group = new L.MarkerClusterGroup();
group.on('layerremove', callback);
map.addLayer(group);
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
group.addLayer(polygon);
group.removeLayer(polygon);
expect(callback.callCount).to.be(1);
});
it('fires layerremove when a marker is removed using removeLayers while on the map with chunked loading', function() {
var callback = sinon.spy();
group = new L.MarkerClusterGroup({ chunkedLoading: true });
group.on('layerremove', callback);
map.addLayer(group);
var marker = new L.Marker([1.5, 1.5]);
group.addLayers([marker]);
group.removeLayers([marker]);
expect(callback.callCount).to.be(1);
});
it('fires layerremove when a vector is removed using removeLayers while on the map with chunked loading', function() {
var callback = sinon.spy();
group = new L.MarkerClusterGroup({ chunkedLoading: true });
group.on('layerremove', callback);
map.addLayer(group);
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
group.addLayers([polygon]);
group.removeLayers([polygon]);
expect(callback.callCount).to.be(1);
});
it('fires layerremove when a marker is removed using removeLayers while not on the map with chunked loading', function() {
var callback = sinon.spy();
group = new L.MarkerClusterGroup({ chunkedLoading: true });
group.on('layerremove', callback);
var marker = new L.Marker([1.5, 1.5]);
group.addLayers([marker]);
group.removeLayers([marker]);
expect(callback.callCount).to.be(1);
});
it('fires layerremove when a vector is removed using removeLayers while not on the map with chunked loading', function() {
var callback = sinon.spy();
group = new L.MarkerClusterGroup({ chunkedLoading: true });
group.on('layerremove', callback);
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
group.addLayers([polygon]);
group.removeLayers([polygon]);
expect(callback.callCount).to.be(1);
});
});
/*
//No normal events can be fired by a clustered marker, so probably don't need this.
it('is fired for a clustered child marker', function() {
var callback = sinon.spy();
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
group.on('click', callback);
group.addLayers([marker, marker2]);
map.addLayer(group);
marker.fire('click');
expect(callback.called).to.be(true);
});
*/
/////////////////////////////
// CLEAN UP CODE
/////////////////////////////
map.remove();
document.body.removeChild(div);
});

View File

@@ -0,0 +1,118 @@
describe('getBounds', function() {
var map, div;
beforeEach(function() {
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
});
afterEach(function() {
document.body.removeChild(div);
});
describe('polygon layer', function() {
it('returns the correct bounds before adding to the map', function() {
var group = new L.MarkerClusterGroup();
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
group.addLayer(polygon);
expect(group.getBounds().equals(polygon.getBounds())).to.be(true);
});
it('returns the correct bounds after adding to the map after adding polygon', function() {
var group = new L.MarkerClusterGroup();
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
group.addLayer(polygon);
map.addLayer(group);
expect(group.getBounds().equals(polygon.getBounds())).to.be(true);
});
it('returns the correct bounds after adding to the map before adding polygon', function() {
var group = new L.MarkerClusterGroup();
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
map.addLayer(group);
group.addLayer(polygon);
expect(group.getBounds().equals(polygon.getBounds())).to.be(true);
});
});
describe('marker layers', function () {
it('returns the correct bounds before adding to the map', function () {
var group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.0, 5.0]);
var marker3 = new L.Marker([6.0, 2.0]);
group.addLayers([marker, marker2, marker3]);
expect(group.getBounds().equals(L.latLngBounds([1.0, 5.0], [6.0, 1.5]))).to.be(true);
});
it('returns the correct bounds after adding to the map after adding markers', function () {
var group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.0, 5.0]);
var marker3 = new L.Marker([6.0, 2.0]);
group.addLayers([marker, marker2, marker3]);
map.addLayer(group);
expect(group.getBounds().equals(L.latLngBounds([1.0, 5.0], [6.0, 1.5]))).to.be(true);
});
it('returns the correct bounds after adding to the map before adding markers', function () {
var group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.0, 5.0]);
var marker3 = new L.Marker([6.0, 2.0]);
map.addLayer(group);
group.addLayers([marker, marker2, marker3]);
expect(group.getBounds().equals(L.latLngBounds([1.0, 5.0], [6.0, 1.5]))).to.be(true);
});
});
describe('marker and polygon layers', function() {
it('returns the correct bounds before adding to the map', function() {
var group = new L.MarkerClusterGroup();
var marker = new L.Marker([6.0, 3.0]);
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
group.addLayers([marker, polygon]);
expect(group.getBounds().equals(L.latLngBounds([1.5, 1.5], [6.0, 3.0]))).to.be(true);
});
it('returns the correct bounds after adding to the map', function () {
var group = new L.MarkerClusterGroup();
var marker = new L.Marker([6.0, 3.0]);
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
map.addLayer(group);
group.addLayers([marker, polygon]);
expect(group.getBounds().equals(L.latLngBounds([1.5, 1.5], [6.0, 3.0]))).to.be(true);
});
});
describe('blank layer', function () {
it('returns a blank bounds', function () {
var group = new L.MarkerClusterGroup();
expect(group.getBounds().isValid()).to.be(false);
});
});
});

View File

@@ -0,0 +1,65 @@
describe('getLayers', function () {
var map, div;
beforeEach(function () {
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
});
afterEach(function () {
document.body.removeChild(div);
});
it('hits polygons and markers before adding to map', function () {
var group = new L.MarkerClusterGroup();
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
var marker = new L.Marker([1.5, 1.5]);
group.addLayers([polygon, marker]);
var layers = group.getLayers();
expect(layers.length).to.be(2);
expect(layers).to.contain(marker);
expect(layers).to.contain(polygon);
});
it('hits polygons and markers after adding to map', function () {
var group = new L.MarkerClusterGroup();
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
var marker = new L.Marker([1.5, 1.5]);
group.addLayers([polygon, marker]);
map.addLayer(group);
var layers = group.getLayers();
expect(layers.length).to.be(2);
expect(layers).to.contain(marker);
expect(layers).to.contain(polygon);
});
it('skips markers and polygons removed while not on the map', function () {
var group = new L.MarkerClusterGroup();
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
var marker = new L.Marker([1.5, 1.5]);
group.addLayers([polygon, marker]);
map.addLayer(group);
map.removeLayer(group);
group.removeLayers([polygon, marker]);
var layers = group.getLayers();
expect(layers.length).to.be(0);
});
});

View File

@@ -0,0 +1,59 @@
describe('getVisibleParent', function () {
var map, div;
beforeEach(function () {
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
});
afterEach(function () {
document.body.removeChild(div);
});
it('gets the marker if the marker is visible', function () {
var group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
group.addLayer(marker);
map.addLayer(group);
var vp = group.getVisibleParent(marker);
expect(vp).to.be(marker);
});
it('gets the visible cluster if it is clustered', function () {
var group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
group.addLayers([marker, marker2]);
map.addLayer(group);
var vp = group.getVisibleParent(marker);
expect(vp).to.be.a(L.MarkerCluster);
expect(vp._icon).to.not.be(null);
expect(vp._icon).to.not.be(undefined);
});
it('returns null if the marker and parents are all not visible', function () {
var group = new L.MarkerClusterGroup();
var marker = new L.Marker([5.5, 1.5]);
var marker2 = new L.Marker([5.5, 1.5]);
group.addLayers([marker, marker2]);
map.addLayer(group);
var vp = group.getVisibleParent(marker);
expect(vp).to.be(null);
});
});

View File

@@ -0,0 +1,111 @@
describe('moving markers', function () {
/**
* Avoid as much as possible creating and destroying objects for each test.
* Instead, try re-using them, except for the ones under test of course.
* PhantomJS does not perform garbage collection for the life of the page,
* i.e. during the entire test process (Karma runs all tests in a single page).
* http://stackoverflow.com/questions/27239708/how-to-get-around-memory-error-with-karma-phantomjs
*
* The `beforeEach` and `afterEach do not seem to cause much issue.
* => they can still be used to initialize some setup between each test.
* Using them keeps a readable spec/index.
*
* But refrain from re-creating div and map every time. Re-use those objects.
*/
/////////////////////////////
// SETUP FOR EACH TEST
/////////////////////////////
beforeEach(function () {
clock = sinon.useFakeTimers();
});
afterEach(function () {
if (group instanceof L.MarkerClusterGroup) {
group.clearLayers();
map.removeLayer(group);
}
// Throw away group as it can be assigned with different configurations between tests.
group = null;
clock.restore();
});
/////////////////////////////
// PREPARATION CODE
/////////////////////////////
var div, map, group, clock;
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
// Corresponds to zoom level 8 for the above div dimensions.
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
/////////////////////////////
// TESTS
/////////////////////////////
it('moves a marker that was moved while off the map', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([10, 10]);
map.addLayer(group);
group.addLayer(marker);
map.removeLayer(group);
marker.setLatLng([1.5, 1.5]);
map.addLayer(group);
expect(group.getLayers().length).to.be(1);
});
it('moves multiple markers that were moved while off the map', function () {
group = new L.MarkerClusterGroup();
map.addLayer(group);
var markers = [];
for (var i = 0; i < 10; i++) {
var marker = new L.Marker([10, 10]);
group.addLayer(marker);
markers.push(marker);
}
map.removeLayer(group);
for (var i = 0; i < 10; i++) {
var marker = markers[i];
marker.setLatLng([1.5, 1.5]);
}
map.addLayer(group);
expect(group.getLayers().length).to.be(10);
});
/////////////////////////////
// CLEAN UP CODE
/////////////////////////////
map.remove();
document.body.removeChild(div);
});

View File

@@ -0,0 +1,44 @@
describe('non-integer min/max zoom', function () {
var map, div, clock;
beforeEach(function () {
clock = sinon.useFakeTimers();
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { minZoom: 0.5, maxZoom: 18.5 });
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
});
afterEach(function () {
clock.restore();
document.body.removeChild(div);
});
it('dont break adding and removing markers', function () {
var group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
var marker3 = new L.Marker([1.5, 1.5]);
group.addLayer(marker);
group.addLayer(marker2);
map.addLayer(group);
group.addLayer(marker3);
expect(marker._icon).to.be(undefined);
expect(marker2._icon).to.be(undefined);
expect(marker3._icon).to.be(undefined);
expect(map._panes.markerPane.childNodes.length).to.be(1);
group.removeLayer(marker2);
});
});

View File

@@ -0,0 +1,55 @@
describe('onAdd', function () {
var map, div;
beforeEach(function () {
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div);
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
});
afterEach(function () {
document.body.removeChild(div);
});
it('throws an error if maxZoom is not specified', function () {
var group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
group.addLayer(marker);
var ex = null;
try {
map.addLayer(group);
} catch (e) {
ex = e;
}
expect(ex).to.not.be(null);
});
it('successfully handles removing and re-adding a layer while not on the map', function () {
map.options.maxZoom = 18;
var group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
map.addLayer(group);
group.addLayer(marker);
map.removeLayer(group);
group.removeLayer(marker);
group.addLayer(marker);
map.addLayer(group);
expect(map.hasLayer(group)).to.be(true);
expect(group.hasLayer(marker)).to.be(true);
});
});

View File

@@ -0,0 +1,42 @@
describe('onRemove', function () {
var map, div;
beforeEach(function () {
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
});
afterEach(function () {
document.body.removeChild(div);
});
it('removes the shown coverage polygon', function () {
var group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
var marker3 = new L.Marker([1.5, 1.5]);
group.addLayer(marker);
group.addLayer(marker2);
group.addLayer(marker3);
map.addLayer(group);
group._showCoverage({ layer: group._topClusterLevel });
expect(group._shownPolygon).to.not.be(null);
map.removeLayer(group);
expect(group._shownPolygon).to.be(null);
});
});

View File

@@ -0,0 +1,209 @@
describe('removeLayers', function () {
/**
* Avoid as much as possible creating and destroying objects for each test.
* Instead, try re-using them, except for the ones under test of course.
* PhantomJS does not perform garbage collection for the life of the page,
* i.e. during the entire test process (Karma runs all tests in a single page).
* http://stackoverflow.com/questions/27239708/how-to-get-around-memory-error-with-karma-phantomjs
*
* The `beforeEach` and `afterEach do not seem to cause much issue.
* => they can still be used to initialize some setup between each test.
* Using them keeps a readable spec/index.
*
* But refrain from re-creating div and map every time. Re-use those objects.
*/
/////////////////////////////
// SETUP FOR EACH TEST
/////////////////////////////
beforeEach(function () {
clock = sinon.useFakeTimers();
});
afterEach(function () {
if (group instanceof L.MarkerClusterGroup) {
group.clearLayers();
map.removeLayer(group);
}
// Throw away group as it can be assigned with different configurations between tests.
group = null;
clock.restore();
});
/////////////////////////////
// PREPARATION CODE
/////////////////////////////
var div, map, group, clock;
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
// Corresponds to zoom level 8 for the above div dimensions.
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
/////////////////////////////
// TESTS
/////////////////////////////
it('removes all the layer given to it', function () {
group = new L.MarkerClusterGroup();
var markers = [
new L.Marker([1.5, 1.5]),
new L.Marker([1.5, 1.5]),
new L.Marker([1.5, 1.5])
];
map.addLayer(group);
group.addLayers(markers);
group.removeLayers(markers);
expect(group.hasLayer(markers[0])).to.be(false);
expect(group.hasLayer(markers[1])).to.be(false);
expect(group.hasLayer(markers[2])).to.be(false);
expect(group.getLayers().length).to.be(0);
});
it('removes all the layer given to it even though they move', function () {
group = new L.MarkerClusterGroup();
var markers = [
new L.Marker([10, 10]),
new L.Marker([20, 20]),
new L.Marker([30, 30])
];
var len = markers.length;
map.addLayer(group);
group.addLayers(markers);
markers.forEach(function (marker) {
marker.setLatLng([1.5, 1.5]);
group.removeLayer(marker);
expect(group.getLayers().length).to.be(len - 1);
group.addLayer(marker);
expect(group.getLayers().length).to.be(len);
});
expect(group.getLayers().length).to.be(len);
});
it('removes all the layer given to it even if the group is not on the map', function () {
group = new L.MarkerClusterGroup();
var markers = [
new L.Marker([1.5, 1.5]),
new L.Marker([1.5, 1.5]),
new L.Marker([1.5, 1.5])
];
map.addLayer(group);
group.addLayers(markers);
map.removeLayer(group);
group.removeLayers(markers);
map.addLayer(group);
expect(group.hasLayer(markers[0])).to.be(false);
expect(group.hasLayer(markers[1])).to.be(false);
expect(group.hasLayer(markers[2])).to.be(false);
expect(group.getLayers().length).to.be(0);
});
it('doesnt break if we are spiderfied', function () {
group = new L.MarkerClusterGroup();
var markers = [
new L.Marker([1.5, 1.5]),
new L.Marker([1.5, 1.5]),
new L.Marker([1.5, 1.5])
];
map.addLayer(group);
group.addLayers(markers);
markers[0].__parent.spiderfy();
// We must wait for the spiderfy animation to timeout
clock.tick(200);
group.removeLayers(markers);
expect(group.hasLayer(markers[0])).to.be(false);
expect(group.hasLayer(markers[1])).to.be(false);
expect(group.hasLayer(markers[2])).to.be(false);
expect(group.getLayers().length).to.be(0);
group.on('spiderfied', function() {
expect(group._spiderfied).to.be(null);
});
});
it('handles nested Layer Groups', function () {
group = new L.MarkerClusterGroup();
var marker1 = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
var marker3 = new L.Marker([1.5, 1.5]);
map.addLayer(group);
group.addLayers([marker1, marker2, marker3]);
expect(group.hasLayer(marker1)).to.be(true);
expect(group.hasLayer(marker2)).to.be(true);
expect(group.hasLayer(marker3)).to.be(true);
group.removeLayers([
marker1,
new L.LayerGroup([
marker2, new L.LayerGroup([
marker3
])
])
]);
expect(group.hasLayer(marker1)).to.be(false);
expect(group.hasLayer(marker2)).to.be(false);
expect(group.hasLayer(marker3)).to.be(false);
expect(group.getLayers().length).to.be(0);
});
/////////////////////////////
// CLEAN UP CODE
/////////////////////////////
map.remove();
document.body.removeChild(div);
});

View File

@@ -0,0 +1,278 @@
describe('Option removeOutsideVisibleBounds', function () {
/**
* Avoid as much as possible creating and destroying objects for each test.
* Instead, try re-using them, except for the ones under test of course.
* PhantomJS does not perform garbage collection for the life of the page,
* i.e. during the entire test process (Karma runs all tests in a single page).
* http://stackoverflow.com/questions/27239708/how-to-get-around-memory-error-with-karma-phantomjs
*
* The `beforeEach` and `afterEach do not seem to cause much issue.
* => they can still be used to initialize some setup between each test.
* Using them keeps a readable spec/index.
*
* But refrain from re-creating div and map every time. Re-use those objects.
*/
/////////////////////////////
// SETUP FOR EACH TEST
/////////////////////////////
beforeEach(function () {
// Nothing for this test suite.
clock = sinon.useFakeTimers();
});
afterEach(function () {
// Restore the previous setting, so that even in case of test failure, next tests are not affected.
L.Browser.mobile = previousMobileSetting;
if (group instanceof L.MarkerClusterGroup) {
//group.removeLayers(group.getLayers());
group.clearLayers();
map.removeLayer(group);
}
// group must be thrown away since we are testing it with a potentially
// different configuration at each test.
group = null;
clock.restore();
clock = null;
});
/////////////////////////////
// PREPARATION CODE
/////////////////////////////
var marker1 = L.marker([1.5, -0.4]), // 2 screens width away.
marker2 = L.marker([1.5, 0.6]), // 1 screen width away.
marker3 = L.marker([1.5, 1.5]), // In view port.
marker4 = L.marker([1.5, 2.4]), // 1 screen width away.
marker5 = L.marker([1.5, 3.4]), // 2 screens width away.
markers = [marker1, marker2, marker3, marker4, marker5],
previousMobileSetting = L.Browser.mobile,
div, map, group, i, clock;
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
// Corresponds to zoom level 8 for the above div dimensions.
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
// Add all markers once to map then remove them immediately so that their icon is null (instead of undefined).
for (i = 0; i < markers.length; i++) {
map.removeLayer(markers[i].addTo(map));
}
function prepareGroup() {
// "group" should be assigned with a Marker Cluster Group before calling this function.
group.addTo(map);
group.addLayers(markers);
}
/////////////////////////////
// TESTS
/////////////////////////////
it('removes objects more than 1 screen away from view port by default', function () {
group = L.markerClusterGroup();
prepareGroup();
expect(marker1._icon).to.be(null);
expect(map._panes.markerPane.childNodes.length).to.be(3); // markers 2, 3 and 4.
expect(marker5._icon).to.be(null);
});
it('removes objects out of view port by default for mobile device', function () {
// Fool Leaflet, make it thinks it runs on a mobile device.
L.Browser.mobile = true;
group = L.markerClusterGroup();
prepareGroup();
expect(marker1._icon).to.be(null);
expect(marker2._icon).to.be(null);
expect(map._panes.markerPane.childNodes.length).to.be(1); // marker 3 only.
expect(marker4._icon).to.be(null);
expect(marker5._icon).to.be(null);
});
it('leaves all objects on map when set to false', function () {
group = L.markerClusterGroup({
removeOutsideVisibleBounds: false
});
prepareGroup();
expect(map._panes.markerPane.childNodes.length).to.be(5); // All 5 markers.
});
// Following tests need markers at very high latitude.
// They test the _checkBoundsMaxLat method against the default Web/Spherical Mercator projection maximum latitude (85.0511287798).
// The actual map view should be '-1.0986328125,84.92929204957956,1.0986328125,85.11983467698401'
// The expanded bounds without correction should be '-3.2958984375,84.7387494221751,3.2958984375,85.31037730438847'
var latLngsMaxLatDefault = [
[100, 3], // Impossible in real world, but nothing prevents the user from entering such latitude, and Web/Spherical Mercator projection will still display it at 85.0511287798
[85.2, 1.5], // 1 "screen" heights away.
[85, 0], // In center of view.
[84.8, -1.5], // 1 "screen" height away.
[84.6, -3] // 2 "screens" height away.
];
function moveMarkersAndMapToMaxLat(latLngs, isSouth) {
for (i = 0; i < markers.length; i++) {
if (isSouth) {
markers[i].setLatLng([-latLngs[i][0], latLngs[i][1]]);
} else {
markers[i].setLatLng(latLngs[i]);
}
}
map.fitBounds([
[isSouth ? -86 : 85, -1],
[isSouth ? -85 : 86, 1] // The actual map view longitude span will be wider. '-1.0986328125,84.92929204957956,1.0986328125,85.11983467698401'
]);
}
function checkProjection(latLngs) {
expect(map.options.crs).to.equal(L.CRS.EPSG3857);
expect(L.CRS.EPSG3857.projection).to.equal(L.Projection.SphericalMercator);
expect(L.Projection.SphericalMercator.MAX_LATITUDE).to.be.a('number');
var mapZoom = map.getZoom();
for (i = 0; i < markers.length; i++) {
try {
expect(markers[i].__parent._zoom).to.be.below(mapZoom);
} catch (e) {
console.log("Failed marker: " + (i + 1));
throw e;
}
}
}
it('includes objects above the Web Mercator projection maximum limit by default', function () {
moveMarkersAndMapToMaxLat(latLngsMaxLatDefault);
group = L.markerClusterGroup();
prepareGroup();
checkProjection(latLngsMaxLatDefault);
expect(map._panes.markerPane.childNodes.length).to.be(4); // Markers 1, 2, 3 and 4.
expect(marker5._icon).to.be(null);
});
it('includes objects below the Web Mercator projection minimum limit by default', function () {
moveMarkersAndMapToMaxLat(latLngsMaxLatDefault, true);
// Make sure we are really in Southern hemisphere.
expect(map.getBounds().getNorth()).to.be.below(-80);
group = L.markerClusterGroup();
prepareGroup();
checkProjection(latLngsMaxLatDefault);
clock.tick(1000);
expect(map._panes.markerPane.childNodes.length).to.be(4); // Markers 1, 2, 3 and 4.
expect(marker5._icon).to.be(null);
});
// The actual map view should be '-1.0986328125,84.92929204957956,1.0986328125,85.11983467698401'
var latLngsMaxLatMobile = [
[100, 1], // Impossible in real world, but nothing prevents the user from entering such latitude, and Web/Spherical Mercator projection will still display it at 85.0511287798
[85.2, 0.5], // 1 "screen" heights away, but should be included by the correction.
[85, 0], // In center of view.
[84.9, -1], // 1 "screen" height away.
[84.8, -1.5] // 2 "screens" height away.
];
it('includes objects above the Web Mercator projection maximum limit for mobile device', function () {
// Fool Leaflet, make it thinks it runs on a mobile device.
L.Browser.mobile = true;
moveMarkersAndMapToMaxLat(latLngsMaxLatMobile);
group = L.markerClusterGroup({
maxClusterRadius: 10
});
prepareGroup();
checkProjection(latLngsMaxLatMobile);
expect(map._panes.markerPane.childNodes.length).to.be(3); // Markers 1, 2 and 3.
expect(marker4._icon).to.be(null);
expect(marker5._icon).to.be(null);
});
it('includes objects below the Web Mercator projection minimum limit for mobile device', function () {
// Fool Leaflet, make it thinks it runs on a mobile device.
L.Browser.mobile = true;
moveMarkersAndMapToMaxLat(latLngsMaxLatMobile, true);
// Make sure we are really in Southern hemisphere.
expect(map.getBounds().getNorth()).to.be.below(-80);
group = L.markerClusterGroup({
maxClusterRadius: 10
});
prepareGroup();
checkProjection(latLngsMaxLatMobile);
expect(map._panes.markerPane.childNodes.length).to.be(3); // Markers 1, 2 and 3.
expect(marker4._icon).to.be(null);
expect(marker5._icon).to.be(null);
});
/////////////////////////////
// CLEAN UP CODE
/////////////////////////////
map.remove();
document.body.removeChild(div);
});

View File

@@ -0,0 +1,110 @@
describe('singleMarkerMode option', function () {
/**
* Avoid as much as possible creating and destroying objects for each test.
* Instead, try re-using them, except for the ones under test of course.
* PhantomJS does not perform garbage collection for the life of the page,
* i.e. during the entire test process (Karma runs all tests in a single page).
* http://stackoverflow.com/questions/27239708/how-to-get-around-memory-error-with-karma-phantomjs
*
* The `beforeEach` and `afterEach do not seem to cause much issue.
* => they can still be used to initialize some setup between each test.
* Using them keeps a readable spec/index.
*
* But refrain from re-creating div and map every time. Re-use those objects.
*/
/////////////////////////////
// SETUP FOR EACH TEST
/////////////////////////////
beforeEach(function () {
// Reset the marker icon.
marker.setIcon(defaultIcon);
});
afterEach(function () {
if (group instanceof L.MarkerClusterGroup) {
group.removeLayers(group.getLayers());
map.removeLayer(group);
}
// Throw away group as it can be assigned with different configurations between tests.
group = null;
});
/////////////////////////////
// PREPARATION CODE
/////////////////////////////
var div, map, group;
var defaultIcon = new L.Icon.Default(),
clusterIcon = new L.Icon.Default(),
marker = L.marker([1.5, 1.5]);
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
// Corresponds to zoom level 8 for the above div dimensions.
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
/////////////////////////////
// TESTS
/////////////////////////////
it('overrides marker icons when set to true', function () {
group = L.markerClusterGroup({
singleMarkerMode: true,
iconCreateFunction: function (layer) {
return clusterIcon;
}
}).addTo(map);
expect(marker.options.icon).to.equal(defaultIcon);
marker.addTo(group);
expect(marker.options.icon).to.equal(clusterIcon);
});
it('does not modify marker icons by default (or set to false)', function () {
group = L.markerClusterGroup({
iconCreateFunction: function (layer) {
return clusterIcon;
}
}).addTo(map);
expect(marker.options.icon).to.equal(defaultIcon);
marker.addTo(group);
expect(marker.options.icon).to.equal(defaultIcon);
});
/////////////////////////////
// CLEAN UP CODE
/////////////////////////////
map.remove();
document.body.removeChild(div);
});

View File

@@ -0,0 +1,381 @@
describe('spiderfy', function () {
/**
* Avoid as much as possible creating and destroying objects for each test.
* Instead, try re-using them, except for the ones under test of course.
* PhantomJS does not perform garbage collection for the life of the page,
* i.e. during the entire test process (Karma runs all tests in a single page).
* http://stackoverflow.com/questions/27239708/how-to-get-around-memory-error-with-karma-phantomjs
*
* The `beforeEach` and `afterEach do not seem to cause much issue.
* => they can still be used to initialize some setup between each test.
* Using them keeps a readable spec/index.
*
* But refrain from re-creating div and map every time. Re-use those objects.
*/
/////////////////////////////
// SETUP FOR EACH TEST
/////////////////////////////
beforeEach(function () {
clock = sinon.useFakeTimers();
});
afterEach(function () {
if (group instanceof L.MarkerClusterGroup) {
group.removeLayers(group.getLayers());
map.removeLayer(group);
}
// group must be thrown away since we are testing it with a potentially
// different configuration at each test.
group = null;
clock.restore();
clock = null;
});
/////////////////////////////
// PREPARATION CODE
/////////////////////////////
var div, map, group, clock;
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
// Corresponds to zoom level 8 for the above div dimensions.
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
/////////////////////////////
// TESTS
/////////////////////////////
it('Spiderfies 2 Markers', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
group.addLayer(marker);
group.addLayer(marker2);
map.addLayer(group);
marker.__parent.spiderfy();
expect(marker._icon.parentNode).to.be(map._panes.markerPane);
expect(marker2._icon.parentNode).to.be(map._panes.markerPane);
});
it('Spiderfies 2 CircleMarkers', function () {
group = new L.MarkerClusterGroup();
var marker = new L.CircleMarker([1.5, 1.5]);
var marker2 = new L.CircleMarker([1.5, 1.5]);
group.addLayer(marker);
group.addLayer(marker2);
map.addLayer(group);
marker.__parent.spiderfy();
// Leaflet 1.0.0 now uses an intermediate L.Renderer.
// marker > _path > _rootGroup (g) > _container (svg) > pane (div)
expect(marker._path.parentNode.parentNode.parentNode).to.be(map.getPane('overlayPane'));
expect(marker2._path.parentNode.parentNode.parentNode).to.be(map.getPane('overlayPane'));
});
it('Spiderfies 2 Circles', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Circle([1.5, 1.5], 10);
var marker2 = new L.Circle([1.5, 1.5], 10);
group.addLayer(marker);
group.addLayer(marker2);
map.addLayer(group);
marker.__parent.spiderfy();
expect(marker._path.parentNode.parentNode.parentNode).to.be(map.getPane('overlayPane'));
expect(marker2._path.parentNode.parentNode.parentNode).to.be(map.getPane('overlayPane'));
});
it('Spiderfies at current zoom if all child markers are at the exact same position', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
group.addLayers([marker, marker2]);
map.addLayer(group);
// Get the appropriate cluster.
var cluster = marker.__parent,
zoom = map.getZoom();
while (cluster._zoom !== zoom) {
cluster = cluster.__parent;
}
expect(zoom).to.be.lessThan(10);
cluster.fireEvent('click', null, true);
clock.tick(1000);
expect(map.getZoom()).to.equal(zoom);
expect(marker._icon.parentNode).to.be(map._panes.markerPane);
expect(marker2._icon.parentNode).to.be(map._panes.markerPane);
});
it('Spiderfies at current zoom if all child markers are still within a single cluster at map maxZoom', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.50001]);
var marker2 = new L.Marker([1.5, 1.5]);
group.addLayers([marker, marker2]);
map.addLayer(group);
expect(marker.__parent._zoom).to.equal(18);
// Get the appropriate cluster.
var cluster = marker.__parent,
zoom = map.getZoom();
while (cluster._zoom !== zoom) {
cluster = cluster.__parent;
}
expect(zoom).to.be.lessThan(10);
cluster.fireEvent('click', null, true);
clock.tick(1000);
expect(map.getZoom()).to.equal(zoom);
expect(marker._icon.parentNode).to.be(map._panes.markerPane);
expect(marker2._icon.parentNode).to.be(map._panes.markerPane);
});
it('removes all markers and spider legs when group is removed from map', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
group.addLayers([marker, marker2]);
map.addLayer(group);
marker.__parent.spiderfy();
expect(map._panes.markerPane.childNodes.length).to.be(3); // The 2 markers + semi-transparent cluster.
expect(map.getPane('overlayPane').firstChild.firstChild.childNodes.length).to.be(2); // The 2 spider legs.
});
it('adds then removes class "leaflet-cluster-anim" from mapPane on spiderfy', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
group.addLayers([marker, marker2]);
map.addLayer(group);
marker.__parent.spiderfy();
expect(map._panes.mapPane.className).to.contain('leaflet-cluster-anim');
clock.tick(1000);
expect(map._panes.mapPane.className).to.not.contain('leaflet-cluster-anim');
});
it('adds then removes class "leaflet-cluster-anim" from mapPane on unspiderfy', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
group.addLayers([marker, marker2]);
map.addLayer(group);
marker.__parent.spiderfy();
clock.tick(1000);
marker.__parent.unspiderfy();
expect(map._panes.mapPane.className).to.contain('leaflet-cluster-anim');
clock.tick(1000);
expect(map._panes.mapPane.className).to.not.contain('leaflet-cluster-anim');
});
it('fires unspiderfied event on unspiderfy', function (done) {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
group.addLayers([marker, marker2]);
map.addLayer(group);
marker.__parent.spiderfy();
clock.tick(1000);
// Add event listener
group.on('unspiderfied', function (event) {
expect(event.target).to.be(group);
expect(event.cluster).to.be.a(L.Marker);
expect(event.markers[1]).to.be(marker);
expect(event.markers[0]).to.be(marker2);
done();
});
marker.__parent.unspiderfy();
clock.tick(1000);
});
it('does not leave class "leaflet-cluster-anim" on mapPane when group is removed while spiderfied', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
group.addLayers([marker, marker2]);
map.addLayer(group);
marker.__parent.spiderfy();
clock.tick(1000);
map.removeLayer(group);
expect(map._panes.mapPane.className).to.not.contain('leaflet-cluster-anim');
});
describe('zoomend event listener', function () {
it('unspiderfies correctly', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Circle([1.5, 1.5], 10);
var marker2 = new L.Circle([1.5, 1.5], 10);
group.addLayer(marker);
group.addLayer(marker2);
map.addLayer(group);
marker.__parent.spiderfy();
expect(group._spiderfied).to.not.be(null);
map.fire('zoomend');
//We should unspiderfy with no animation, so this should be null
expect(group._spiderfied).to.be(null);
});
});
describe('spiderfied event listener', function () {
it('Spiderfies 2 Markers', function (done) {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
group.addLayer(marker);
group.addLayer(marker2);
map.addLayer(group);
// Add event listener
group.on('spiderfied', function (event) {
expect(event.target).to.be(group);
expect(event.cluster).to.be.a(L.Marker);
expect(event.markers[1]).to.be(marker);
expect(event.markers[0]).to.be(marker2);
done();
});
marker.__parent.spiderfy();
clock.tick(200);
});
it('Spiderfies 2 Circles', function (done) {
group = new L.MarkerClusterGroup();
var marker = new L.Circle([1.5, 1.5], 10);
var marker2 = new L.Circle([1.5, 1.5], 10);
group.addLayer(marker);
group.addLayer(marker2);
map.addLayer(group);
// Add event listener
group.on('spiderfied', function (event) {
expect(event.target).to.be(group);
expect(event.cluster).to.be.a(L.Marker);
expect(event.markers[1]).to.be(marker);
expect(event.markers[0]).to.be(marker2);
done();
});
marker.__parent.spiderfy();
clock.tick(200);
});
});
/////////////////////////////
// CLEAN UP CODE
/////////////////////////////
map.remove();
document.body.removeChild(div);
});

View File

@@ -0,0 +1,124 @@
describe('things behave correctly with negative minZoom', function () {
/**
* Avoid as much as possible creating and destroying objects for each test.
* Instead, try re-using them, except for the ones under test of course.
* PhantomJS does not perform garbage collection for the life of the page,
* i.e. during the entire test process (Karma runs all tests in a single page).
* http://stackoverflow.com/questions/27239708/how-to-get-around-memory-error-with-karma-phantomjs
*
* The `beforeEach` and `afterEach do not seem to cause much issue.
* => they can still be used to initialize some setup between each test.
* Using them keeps a readable spec/index.
*
* But refrain from re-creating div and map every time. Re-use those objects.
*/
/////////////////////////////
// SETUP FOR EACH TEST
/////////////////////////////
beforeEach(function () {
// Nothing for this test suite.
});
afterEach(function () {
if (group instanceof L.MarkerClusterGroup) {
group.clearLayers();
map.removeLayer(group);
}
// Throw away group as it can be assigned with different configurations between tests.
group = null;
});
/////////////////////////////
// PREPARATION CODE
/////////////////////////////
var div, map, group;
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { minZoom: -3, maxZoom: 18 });
map.setView(L.latLng(0, 0), -3);
/////////////////////////////
// TESTS
/////////////////////////////
it('shows a single marker added to the group before the group is added to the map', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
group.addLayer(marker);
map.addLayer(group);
expect(marker._icon).to.not.be(undefined);
expect(marker._icon.parentNode).to.be(map._panes.markerPane);
});
it('shows a single marker added to the group after the group is added to the map', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
map.addLayer(group);
group.addLayer(marker);
expect(marker._icon).to.not.be(undefined);
expect(marker._icon.parentNode).to.be(map._panes.markerPane);
});
it('creates a cluster when 2 overlapping markers are added before the group is added to the map', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
group.addLayer(marker);
group.addLayer(marker2);
map.addLayer(group);
expect(marker._icon).to.be(undefined);
expect(marker2._icon).to.be(undefined);
expect(map._panes.markerPane.childNodes.length).to.be(1);
});
it('creates a cluster when 2 overlapping markers are added after the group is added to the map', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
map.addLayer(group);
group.addLayer(marker);
group.addLayer(marker2);
expect(marker._icon).to.be(null); //Null as was added and then removed
expect(marker2._icon).to.be(undefined);
expect(map._panes.markerPane.childNodes.length).to.be(1);
});
/////////////////////////////
// CLEAN UP CODE
/////////////////////////////
map.remove();
document.body.removeChild(div);
});

View File

@@ -0,0 +1,169 @@
describe('unspiderfy', function () {
/**
* Avoid as much as possible creating and destroying objects for each test.
* Instead, try re-using them, except for the ones under test of course.
* PhantomJS does not perform garbage collection for the life of the page,
* i.e. during the entire test process (Karma runs all tests in a single page).
* http://stackoverflow.com/questions/27239708/how-to-get-around-memory-error-with-karma-phantomjs
*
* The `beforeEach` and `afterEach do not seem to cause much issue.
* => they can still be used to initialize some setup between each test.
* Using them keeps a readable spec/index.
*
* But refrain from re-creating div and map every time. Re-use those objects.
*/
/////////////////////////////
// SETUP FOR EACH TEST
/////////////////////////////
beforeEach(function () {
clock = sinon.useFakeTimers();
});
afterEach(function () {
if (group instanceof L.MarkerClusterGroup) {
group.removeLayers(group.getLayers());
map.removeLayer(group);
}
// group must be thrown away since we are testing it with a potentially
// different configuration at each test.
group = null;
clock.restore();
clock = null;
});
/////////////////////////////
// PREPARATION CODE
/////////////////////////////
var div, map, group, clock;
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, {
maxZoom: 18
});
// Corresponds to zoom level 8 for the above div dimensions.
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
/////////////////////////////
// TESTS
/////////////////////////////
it('Unspiderfies 2 Markers', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
group.addLayer(marker);
group.addLayer(marker2);
map.addLayer(group);
marker.__parent.spiderfy();
clock.tick(1000);
group.unspiderfy();
clock.tick(1000);
expect(map.hasLayer(marker)).to.be(false);
expect(map.hasLayer(marker2)).to.be(false);
});
it('Unspiderfies 2 CircleMarkers', function () {
group = new L.MarkerClusterGroup();
var marker = new L.CircleMarker([1.5, 1.5]);
var marker2 = new L.CircleMarker([1.5, 1.5]);
group.addLayer(marker);
group.addLayer(marker2);
map.addLayer(group);
marker.__parent.spiderfy();
clock.tick(1000);
group.unspiderfy();
clock.tick(1000);
expect(map.hasLayer(marker)).to.be(false);
expect(map.hasLayer(marker2)).to.be(false);
});
it('Unspiderfies 2 Circles', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Circle([1.5, 1.5], 10);
var marker2 = new L.Circle([1.5, 1.5], 10);
group.addLayer(marker);
group.addLayer(marker2);
map.addLayer(group);
marker.__parent.spiderfy();
clock.tick(1000);
group.unspiderfy();
clock.tick(1000);
expect(map.hasLayer(marker)).to.be(false);
expect(map.hasLayer(marker2)).to.be(false);
});
it('fires unspiderfied event on unspiderfy', function (done) {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
group.addLayers([marker, marker2]);
map.addLayer(group);
marker.__parent.spiderfy();
clock.tick(1000);
// Add event listener
group.on('unspiderfied', function (event) {
expect(event.target).to.be(group);
expect(event.cluster).to.be.a(L.Marker);
expect(event.markers[1]).to.be(marker);
expect(event.markers[0]).to.be(marker2);
done();
});
group.unspiderfy();
clock.tick(1000);
});
});

View File

@@ -0,0 +1,410 @@
describe('zoomAnimation', function () {
/**
* Avoid as much as possible creating and destroying objects for each test.
* Instead, try re-using them, except for the ones under test of course.
* PhantomJS does not perform garbage collection for the life of the page,
* i.e. during the entire test process (Karma runs all tests in a single page).
* http://stackoverflow.com/questions/27239708/how-to-get-around-memory-error-with-karma-phantomjs
*
* The `beforeEach` and `afterEach do not seem to cause much issue.
* => they can still be used to initialize some setup between each test.
* Using them keeps a readable spec/index.
*
* But refrain from re-creating div and map every time. Re-use those objects.
*/
/////////////////////////////
// SETUP FOR EACH TEST
/////////////////////////////
beforeEach(function () {
clock = sinon.useFakeTimers();
});
afterEach(function () {
// Restore the previous setting, so that even in case of test failure, next tests are not affected.
L.Browser.mobile = previousMobileSetting;
if (group instanceof L.MarkerClusterGroup) {
group.clearLayers();
map.removeLayer(group);
}
// group must be thrown away since we are testing it with a potentially
// different configuration at each test.
group = null;
clock.restore();
clock = null;
});
/////////////////////////////
// PREPARATION CODE
/////////////////////////////
var previousMobileSetting = L.Browser.mobile,
div, map, group, clock;
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
// Corresponds to zoom level 8 for the above div dimensions.
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
/////////////////////////////
// TESTS
/////////////////////////////
it('adds the visible marker to the map when zooming in', function () {
map.setView(new L.LatLng(-37.36142550190516, 174.254150390625), 7);
group = new L.MarkerClusterGroup({
showCoverageOnHover: true,
maxClusterRadius: 20,
disableClusteringAtZoom: 15
});
var marker = new L.Marker([-37.77852090603777, 175.3103667497635]);
group.addLayer(marker); //The one we zoom in on
group.addLayer(new L.Marker([-37.711800591811055, 174.50034790039062])); //Marker that we cluster with at the top zoom level, but not 1 level down
map.addLayer(group);
clock.tick(1000);
map.setView([-37.77852090603777, 175.3103667497635], 15);
//Run the the animation
clock.tick(1000);
expect(marker._icon).to.not.be(undefined);
expect(marker._icon).to.not.be(null);
});
it('adds the visible marker to the map when jumping around', function () {
group = new L.MarkerClusterGroup();
var marker1 = new L.Marker([48.858280181884766, 2.2945759296417236]);
var marker2 = new L.Marker([16.02359962463379, -61.70280075073242]);
group.addLayer(marker1); //The one we zoom in on first
group.addLayer(marker2); //Marker that we cluster with at the top zoom level, but not 1 level down
map.addLayer(group);
//show the first
map.fitBounds(new L.LatLngBounds(new L.LatLng(41.371582, -5.142222), new L.LatLng(51.092804, 9.561556)));
clock.tick(1000);
map.fitBounds(new L.LatLngBounds(new L.LatLng(15.830972671508789, -61.807167053222656), new L.LatLng(16.516849517822266, -61.0)));
//Run the the animation
clock.tick(1000);
//Now the second one should be visible on the map
expect(marker2._icon).to.not.be(undefined);
expect(marker2._icon).to.not.be(null);
});
it('adds the visible markers to the map, but not parent clusters when jumping around', function () {
group = new L.MarkerClusterGroup();
var marker1 = new L.Marker([59.9520, 30.3307]),
marker2 = new L.Marker([59.9516, 30.3308]),
marker3 = new L.Marker([59.9513, 30.3312]);
group.addLayer(marker1);
group.addLayer(marker2);
group.addLayer(marker3);
map.addLayer(group);
//Show none of them
map.setView([53.0676, 170.6835], 16);
clock.tick(1000);
//Zoom so that all the markers will be visible (Same as zoomToShowLayer)
map.setView(marker1.getLatLng(), 18);
//Run the the animation
clock.tick(1000);
//Now the markers should all be visible, and there should be no visible clusters
expect(marker1._icon.parentNode).to.be(map._panes.markerPane);
expect(marker2._icon.parentNode).to.be(map._panes.markerPane);
expect(marker3._icon.parentNode).to.be(map._panes.markerPane);
expect(map._panes.markerPane.childNodes.length).to.be(3);
});
it('removes clicked clusters on the edge of a mobile screen', function () {
L.Browser.mobile = true;
// Corresponds to zoom level 8 for the above div dimensions.
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
group = new L.MarkerClusterGroup({
maxClusterRadius: 80
}).addTo(map);
// Add a marker 1 pixel below the initial screen bottom edge.
var bottomPoint = map.getPixelBounds().max.add([0, 1]),
bottomLatLng = map.unproject(bottomPoint),
centerLng = map.getCenter().lng,
bottomPosition = new L.LatLng(
bottomLatLng.lat,
centerLng
),
bottomMarker = new L.Marker(bottomPosition).addTo(group),
initialZoom = map.getZoom();
expect(bottomMarker._icon).to.be(undefined);
// Add many markers 79 pixels above the first one, so they cluster with it.
var newPoint = bottomPoint.add([0, -79]),
newLatLng = L.latLng(
map.unproject(newPoint).lat,
centerLng
);
for (var i = 0; i < 10; i += 1) {
group.addLayer(new L.Marker(newLatLng));
}
var parentCluster = bottomMarker.__parent;
expect(parentCluster._icon.parentNode).to.be(map._panes.markerPane);
parentCluster.fireEvent('click', null, true);
//Run the the animation
clock.tick(1000);
expect(map.getZoom()).to.equal(initialZoom + 1); // The fitBounds with 200px height should result in zooming 1 level in.
// Finally make sure that the cluster has been removed from map.
expect(parentCluster._icon).to.be(null);
expect(map._panes.markerPane.childNodes.length).to.be(2); // The bottomMarker + cluster for the 10 above markers.
});
describe('zoomToShowLayer', function () {
it('zoom to single marker inside map view', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([59.9520, 30.3307]);
group.addLayer(marker);
map.addLayer(group);
var zoomCallbackSpy = sinon.spy();
map.setView(marker.getLatLng(), 10);
clock.tick(1000);
var initialCenter = map.getCenter();
var initialZoom = map.getZoom();
group.zoomToShowLayer(marker, zoomCallbackSpy);
//Run the the animation
clock.tick(1000);
//Marker should be visible, map center and zoom level should stay the same, callback called once
expect(marker._icon).to.not.be(undefined);
expect(marker._icon).to.not.be(null);
expect(map.getBounds().contains(marker.getLatLng())).to.be.true;
expect(map.getCenter()).to.eql(initialCenter);
expect(map.getZoom()).to.equal(initialZoom);
sinon.assert.calledOnce(zoomCallbackSpy);
});
it('pan map to single marker outside map view', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([59.9520, 30.3307]);
group.addLayer(marker);
map.addLayer(group);
var zoomCallbackSpy = sinon.spy();
//Show none of them
map.setView([53.0676, 170.6835], 16);
clock.tick(1000);
var initialZoom = map.getZoom();
group.zoomToShowLayer(marker, zoomCallbackSpy);
//Run the the animation
clock.tick(1000);
//Marker should be visible, map center should be equal to marker center,
//zoom level should stay the same, callback called once
expect(marker._icon).to.not.be(undefined);
expect(marker._icon).to.not.be(null);
expect(map.getBounds().contains(marker.getLatLng())).to.be.true;
expect(map.getCenter()).to.eql(marker.getLatLng());
expect(map.getZoom()).to.equal(initialZoom);
sinon.assert.calledOnce(zoomCallbackSpy);
});
it('change view and zoom to marker in cluster inside map view', function () {
group = new L.MarkerClusterGroup();
var marker1 = new L.Marker([59.9520, 30.3307]);
var marker2 = new L.Marker([59.9516, 30.3308]);
var marker3 = new L.Marker([59.9513, 30.3312]);
group.addLayer(marker1);
group.addLayer(marker2);
group.addLayer(marker3);
map.addLayer(group);
var zoomCallbackSpy = sinon.spy();
map.setView(marker1.getLatLng(), 16);
clock.tick(1000);
group.zoomToShowLayer(marker1, zoomCallbackSpy);
//Run the the animation
clock.tick(1000);
//Now the markers should all be visible, there should be no visible clusters, and callback called once
expect(marker1._icon.parentNode).to.be(map._panes.markerPane);
expect(marker2._icon.parentNode).to.be(map._panes.markerPane);
expect(marker3._icon.parentNode).to.be(map._panes.markerPane);
expect(map._panes.markerPane.childNodes.length).to.be(3);
expect(map.getBounds().contains(marker1.getLatLng())).to.be.true;
sinon.assert.calledOnce(zoomCallbackSpy);
});
it('change view and zoom to marker in cluster outside map view', function () {
group = new L.MarkerClusterGroup();
var marker1 = new L.Marker([59.9520, 30.3307]);
var marker2 = new L.Marker([59.9516, 30.3308]);
var marker3 = new L.Marker([59.9513, 30.3312]);
group.addLayer(marker1);
group.addLayer(marker2);
group.addLayer(marker3);
map.addLayer(group);
var zoomCallbackSpy = sinon.spy();
//Show none of them
map.setView([53.0676, 170.6835], 16);
clock.tick(1000);
group.zoomToShowLayer(marker1, zoomCallbackSpy);
//Run the the animation
clock.tick(1000);
//Now the markers should all be visible, there should be no visible clusters, and callback called once
expect(marker1._icon.parentNode).to.be(map._panes.markerPane);
expect(marker2._icon.parentNode).to.be(map._panes.markerPane);
expect(marker3._icon.parentNode).to.be(map._panes.markerPane);
expect(map._panes.markerPane.childNodes.length).to.be(3);
expect(map.getBounds().contains(marker1.getLatLng())).to.be.true;
sinon.assert.calledOnce(zoomCallbackSpy);
});
it('spiderfy overlapping markers', function () {
group = new L.MarkerClusterGroup();
var marker1 = new L.Marker([59.9520, 30.3307]);
var marker2 = new L.Marker([59.9520, 30.3307]);
var marker3 = new L.Marker([59.9520, 30.3307]);
group.addLayer(marker1);
group.addLayer(marker2);
group.addLayer(marker3);
map.addLayer(group);
var zoomCallbackSpy = sinon.spy();
//Show none of them
map.setView([53.0676, 170.6835], 16);
clock.tick(1000);
group.zoomToShowLayer(marker1, zoomCallbackSpy);
//Run the the animation
clock.tick(1000);
//Now the markers should all be visible, parent cluster should be spiderfied, and callback called once
expect(marker1._icon.parentNode).to.be(map._panes.markerPane);
expect(marker2._icon.parentNode).to.be(map._panes.markerPane);
expect(marker3._icon.parentNode).to.be(map._panes.markerPane);
expect(map._panes.markerPane.childNodes.length).to.be(4);//3 markers + spiderfied parent cluster
sinon.assert.calledOnce(zoomCallbackSpy);
});
it('zoom or spiderfy markers if they visible on next level of zoom', function () {
group = new L.MarkerClusterGroup();
var marker1 = new L.Marker([59.9520, 30.3307]);
var marker2 = new L.Marker([59.9516, 30.3308]);
var marker3 = new L.Marker([59.9513, 30.3312]);
group.addLayer(marker1);
group.addLayer(marker2);
group.addLayer(marker3);
map.addLayer(group);
var zoomCallbackSpy = sinon.spy();
//Markers will be visible on zoom 18
map.setView([59.9520, 30.3307], 17);
clock.tick(1000);
group.zoomToShowLayer(marker1, zoomCallbackSpy);
//Run the the animation
clock.tick(1000);
//Now the markers should all be visible (zoomed or spiderfied), and callback called once
expect(marker1._icon).to.not.be(undefined);
expect(marker1._icon).to.not.be(null);
expect(marker2._icon).to.not.be(undefined);
expect(marker2._icon).to.not.be(null);
expect(marker3._icon).to.not.be(undefined);
expect(marker3._icon).to.not.be(null);
sinon.assert.calledOnce(zoomCallbackSpy);
});
});
/////////////////////////////
// CLEAN UP CODE
/////////////////////////////
map.remove();
document.body.removeChild(div);
});