leaflet.freedraw
Version:
Zoopla inspired freehand polygon creation using Leaflet.js.
570 lines (397 loc) • 21.7 kB
JavaScript
describe('Leaflet FreeDraw', function() {
var freeDraw = {};
/**
* @method createMockPolygon
* @return {L.Polygon|Boolean}
*/
var createMockPolygon = function createMockPolygon() {
var latLngs = [new L.LatLng(10, 15), new L.LatLng(15, 18), new L.LatLng(18, 9), new L.LatLng(9, 14)],
polygon = freeDraw.createPolygon(latLngs);
if (!polygon) {
return false;
}
// Define some make-believe edges.
polygon._parts[0] = [new L.Point(100, 100), new L.Point(200, 200)];
return polygon;
};
beforeEach(function() {
var element = document.createElement('div'),
map = L.map(element).setView([51.505, -0.09], 14);
freeDraw = new L.FreeDraw({
mode: L.FreeDraw.MODES.ALL
});
map.addLayer(freeDraw);
// Required to be a valid Leaflet.js module!
expect(typeof freeDraw.initialize).toBe('function');
expect(typeof freeDraw.onAdd).toBe('function');
expect(typeof freeDraw.onRemove).toBe('function');
// Mock the `emitPolygonCount` since it is problematic in tests.
spyOn(freeDraw, 'emitPolygonCount');
});
it('Should be able to configure the various modes correctly;', function() {
expect(L.FreeDraw.MODES.VIEW).toEqual(2 >> 1);
expect(L.FreeDraw.MODES.CREATE).toEqual(4 >> 1);
expect(L.FreeDraw.MODES.EDIT).toEqual(8 >> 1);
expect(L.FreeDraw.MODES.DELETE).toEqual(16 >> 1);
expect(L.FreeDraw.MODES.APPEND).toEqual(32 >> 1);
expect(L.FreeDraw.MODES.EDIT_APPEND).toEqual(L.FreeDraw.MODES.EDIT | L.FreeDraw.MODES.APPEND);
expect(L.FreeDraw.MODES.ALL).toEqual
(
L.FreeDraw.MODES.CREATE | L.FreeDraw.MODES.EDIT |
L.FreeDraw.MODES.DELETE | L.FreeDraw.MODES.APPEND | L.FreeDraw.MODES.VIEW
);
});
it('Should be able to throw an exception;', function() {
expect(function throwException() {
L.FreeDraw.Throw('We threw an exception!');
}).toThrow('Leaflet.FreeDraw: We threw an exception!.');
});
it('Should be able to provide a unique list of latitude/longitude values;', function() {
var latLngs = [new L.LatLng(100, 100), new L.LatLng(120, 120), new L.LatLng(100, 100)];
expect(freeDraw.uniqueLatLngs(latLngs).length).toEqual(2);
});
it('Should be able to prevent the drawing of multiple polygons;', function() {
var firstPolygon = createMockPolygon(),
secondPolygon = createMockPolygon(),
thirdPolygon = createMockPolygon();
expect(freeDraw.getPolygons(true).length).toEqual(3);
freeDraw.clearPolygons();
freeDraw.destroyPolygon(firstPolygon);
freeDraw.destroyPolygon(secondPolygon);
freeDraw.destroyPolygon(thirdPolygon);
expect(freeDraw.getPolygons(true).length).toEqual(0);
// Prevent the user from drawing multiple polygons!
freeDraw.options.allowMultiplePolygons(false);
// Attempt to create two polygons.
createMockPolygon(); createMockPolygon();
expect(freeDraw.getPolygons(true).length).toEqual(1);
});
it('Should be able to cancel the current action;', function() {
freeDraw.creating = 'Creating';
freeDraw.movingEdge = 'Moving';
// Cancel the current action!
freeDraw.cancelAction();
expect(freeDraw.creating).toEqual(false);
expect(freeDraw.movingEdge).toBeNull();
});
it('Should be able to perform events in silent mode;', function() {
expect(freeDraw.silenced).toBeFalsy();
freeDraw.silently(function() {
expect(freeDraw.silenced).toBeTruthy();
});
expect(freeDraw.silenced).toBeFalsy();
});
it('Should be able to set/unset the mode accordingly;', function() {
freeDraw.setMode(L.FreeDraw.MODES.CREATE);
expect(freeDraw.mode).toEqual(L.FreeDraw.MODES.CREATE);
freeDraw.unsetMode(L.FreeDraw.MODES.CREATE);
expect(freeDraw.mode).toEqual(L.FreeDraw.MODES.VIEW);
freeDraw.setMode(L.FreeDraw.MODES.CREATE | L.FreeDraw.MODES.APPEND);
expect(freeDraw.mode).toEqual(L.FreeDraw.MODES.CREATE | L.FreeDraw.MODES.APPEND);
});
it('Should be able to convert lat/longs to ClipperJS points;', function() {
var latLngs = [new L.LatLng(10, 100), new L.LatLng(20, 210), new L.LatLng(185, 95)],
points = freeDraw.latLngsToClipperPoints(latLngs);
expect(points[0].X).toBeDefined();
expect(points[0].Y).toEqual(585290);
expect(points.length).toEqual(3);
});
it('Should be able to convert ClipperJS polygons to flattened lat/lngs;', function() {
var latLngs = [new L.LatLng(10, 100), new L.LatLng(20, 210), new L.LatLng(185, 95)],
firstPolygon = freeDraw.latLngsToClipperPoints(latLngs),
secondPolygon = freeDraw.latLngsToClipperPoints(latLngs),
clipperPolygons = [firstPolygon, secondPolygon],
transformed = freeDraw.clipperPolygonsToLatLngs(clipperPolygons);
expect(transformed.length).toEqual(6);
expect(transformed[2].lat).toEqual(85.0511287798066);
expect(transformed[2].lng).toEqual(94.99998092651367);
});
it('Should be able to create a triangle on the map and then remove it;', function() {
var latLngs = [new L.LatLng(10, 100), new L.LatLng(20, 210), new L.LatLng(185, 95), new L.LatLng(200, 200)],
polygon = freeDraw.createPolygon(latLngs);
freeDraw.on('markers', function markersReceived(eventData) {
expect(eventData.latLngs).toBeDefined();
});
expect(polygon instanceof L.Polygon).toBeTruthy();
expect(polygon._latlngs).toBeDefined();
expect(freeDraw.getPolygons(true).length).toEqual(1);
expect(freeDraw.polygonCount).toEqual(1);
freeDraw.destroyPolygon(polygon);
expect(freeDraw.getPolygons(true).length).toEqual(0);
expect(freeDraw.polygonCount).toEqual(0);
});
it('Should be able to create and remove the edges belonging to a polygon;', function() {
var firstLatLngs = [new L.LatLng(10, 15), new L.LatLng(15, 18), new L.LatLng(18, 9), new L.LatLng(9, 14)],
secondLatLngs = [new L.LatLng(12, 15), new L.LatLng(15, 13), new L.LatLng(18, 9), new L.LatLng(28, 14)],
firstPolygon = freeDraw.createPolygon(firstLatLngs),
secondPolygon = freeDraw.createPolygon(secondLatLngs);
expect(firstPolygon instanceof L.Polygon).toBeTruthy();
expect(secondPolygon instanceof L.Polygon).toBeTruthy();
expect(freeDraw.getPolygons(true).length).toEqual(2);
// Define some make-believe edges.
firstPolygon._parts[0] = [new L.Point(100, 100), new L.Point(200, 200)];
secondPolygon._parts[0] = [new L.Point(300, 300), new L.Point(400, 400)];
// Add some manual edges.
freeDraw.createEdges(firstPolygon);
expect(freeDraw.edges.length).toEqual(4);
freeDraw.createEdges(secondPolygon);
expect(freeDraw.edges.length).toEqual(8);
freeDraw.destroyEdges(firstPolygon);
expect(freeDraw.edges.length).toEqual(4);
freeDraw.clearPolygons();
expect(freeDraw.edges.length).toEqual(0);
});
it('Should be able to emit an event of the markers upon events;', function() {
freeDraw.silently(function silently() {
var latLngs = [new L.LatLng(10, 100), new L.LatLng(20, 210), new L.LatLng(185, 95), new L.LatLng(200, 200)],
polygon = freeDraw.createPolygon(latLngs);
// Define some make-believe edges.
polygon._parts[0] = [new L.Point(300, 300), new L.Point(400, 400)];
});
freeDraw.on('markers', function markersReceived(eventData) {
var latLngs = eventData.latLngs;
expect(latLngs.length).toEqual(1);
expect(latLngs[0].length).toEqual(7);
var firstLatLng = latLngs[0][0],
lastLatLng = latLngs[0][latLngs[0].length - 1];
// Should be a closed polygon when sharing!
expect(firstLatLng.lat).toEqual(lastLatLng.lat);
expect(firstLatLng.lng).toEqual(lastLatLng.lng);
});
// Force the notification.
freeDraw.notifyBoundaries();
});
it('Should be able to re-create edges for a given polygon;', function() {
var polygon = createMockPolygon();
// Add some manual edges, and then attempt to re-create them.
freeDraw.createEdges(polygon);
spyOn(freeDraw, 'createEdges').and.callThrough();
var edgeCount = freeDraw.recreateEdges(polygon);
expect(edgeCount).toEqual(4);
expect(freeDraw.createEdges).toHaveBeenCalled();
});
it('Should be able to resurrect any possible orphans', function(done) {
var polygon = createMockPolygon();
freeDraw.resurrectOrphans();
spyOn(freeDraw, 'recreateEdges').and.callThrough();
setTimeout(function setTimeout() {
expect(freeDraw.recreateEdges).toHaveBeenCalled();
done();
}, 1);
});
it('Should be able to decline creating a polygon using the right mouse button;', function() {
var event = document.createEvent('MouseEvents'),
mouseX = 100,
mouseY = 200,
RIGHT_CLICK = 2;
event.initMouseEvent('mousedown', true, true, window, 1, 12, 345, mouseX, mouseY, false, false, true, false, RIGHT_CLICK, null);
spyOn(event, 'stopPropagation');
spyOn(event, 'preventDefault');
freeDraw.map._container.dispatchEvent(event);
expect(freeDraw.fromPoint.x).not.toEqual(mouseX);
expect(freeDraw.fromPoint.y).not.toEqual(mouseY);
expect(event.stopPropagation).not.toHaveBeenCalled();
expect(event.preventDefault).not.toHaveBeenCalled();
});
it('Should be able to place the map in create mode and make a polygon;', function() {
// Disable the simplification of polygons for this example otherwise we're at the mercy of the
// JSClipper algorithm.
freeDraw.options.simplifyPolygon = false;
expect(freeDraw.getPolygons(true).length).toEqual(0);
var event = document.createEvent('MouseEvents'),
mouseX = 0,
mouseY = 0;
// Begin the creation of a polygon.
expect(freeDraw.creating).toBeFalsy();
event.initMouseEvent('mousedown', true, true, window, 1, 12, 345, mouseX, mouseY, false, false, true, false, 0, null);
freeDraw.map._container.dispatchEvent(event);
expect(freeDraw.creating).toBeTruthy();
expect(freeDraw.fromPoint.x).toEqual(mouseX);
expect(freeDraw.fromPoint.y).toEqual(mouseY);
expect(freeDraw.latLngs.length).toEqual(0);
// Create three more points for the polygon.
mouseX = 100;
mouseY = 0;
event = document.createEvent('MouseEvents');
event.initMouseEvent('mousemove', true, true, window, 1, 12, 345, mouseX, mouseY, false, false, true, false, 0, null);
freeDraw.map._container.dispatchEvent(event);
expect(freeDraw.latLngs.length).toEqual(1);
mouseX = 100;
mouseY = 100;
event = document.createEvent('MouseEvents');
event.initMouseEvent('mousemove', true, true, window, 1, 12, 345, mouseX, mouseY, false, false, true, false, 0, null);
freeDraw.map._container.dispatchEvent(event);
expect(freeDraw.latLngs.length).toEqual(2);
mouseX = 0;
mouseY = 100;
event = document.createEvent('MouseEvents');
event.initMouseEvent('mousemove', true, true, window, 1, 12, 345, mouseX, mouseY, false, false, true, false, 0, null);
freeDraw.map._container.dispatchEvent(event);
expect(freeDraw.latLngs.length).toEqual(3);
// And finish the polygon creation!
mouseX = 0;
mouseY = 50;
spyOn(freeDraw, '_createMouseUp').and.callThrough();
event = document.createEvent('MouseEvents');
event.initMouseEvent('mouseup', true, true, window, 1, 12, 345, mouseX, mouseY, false, false, true, false, 0, null);
freeDraw.map._container.dispatchEvent(event);
expect(freeDraw._createMouseUp).toHaveBeenCalled();
expect(freeDraw.creating).toBeFalsy();
expect(freeDraw.getPolygons(true).length).toEqual(1);
});
it('Should be able to report the current mode;', function() {
freeDraw.on('mode', function modeReceived(eventData) {
expect(eventData.mode).toEqual(L.FreeDraw.MODES.DELETE);
});
freeDraw.setMode(L.FreeDraw.MODES.DELETE);
freeDraw.off('mode');
freeDraw.on('mode', function modeReceived(eventData) {
expect(eventData.mode).toEqual(L.FreeDraw.MODES.DELETE | L.FreeDraw.MODES.APPEND);
});
freeDraw.setMode(L.FreeDraw.MODES.DELETE | L.FreeDraw.MODES.APPEND);
freeDraw.off('mode');
freeDraw.on('mode', function modeReceived(eventData) {
expect(eventData.mode).toEqual(L.FreeDraw.MODES.VIEW);
});
freeDraw.unsetMode(L.FreeDraw.MODES.DELETE | L.FreeDraw.MODES.APPEND);
freeDraw.off('mode');
});
it('Should be able to create and destroy the D3 layer;', function() {
freeDraw.destroyD3();
expect(typeof freeDraw.svg).toEqual('object');
expect(freeDraw.svg.toString()).toEqual('[object Object]');
freeDraw.createD3();
expect(freeDraw.svg.toString()).toEqual('[object SVGSVGElement]');
});
it('Should be able to define the necessary classes on the map element;', function() {
var element = freeDraw.map._container;
// Every class should be added to the map element.
freeDraw.setMode(L.FreeDraw.MODES.ALL);
expect(element.classList.contains('mode-create')).toBeTruthy();
expect(element.classList.contains('mode-edit')).toBeTruthy();
expect(element.classList.contains('mode-view')).toBeTruthy();
expect(element.classList.contains('mode-delete')).toBeTruthy();
expect(element.classList.contains('mode-append')).toBeTruthy();
freeDraw.unsetMode(L.FreeDraw.MODES.CREATE);
expect(element.classList.contains('mode-create')).toBeFalsy();
expect(element.classList.contains('mode-edit')).toBeTruthy();
expect(element.classList.contains('mode-view')).toBeTruthy();
expect(element.classList.contains('mode-delete')).toBeTruthy();
expect(element.classList.contains('mode-append')).toBeTruthy();
freeDraw.unsetMode(L.FreeDraw.MODES.EDIT);
expect(element.classList.contains('mode-create')).toBeFalsy();
expect(element.classList.contains('mode-edit')).toBeFalsy();
expect(element.classList.contains('mode-view')).toBeTruthy();
expect(element.classList.contains('mode-delete')).toBeTruthy();
expect(element.classList.contains('mode-append')).toBeTruthy();
freeDraw.unsetMode(L.FreeDraw.MODES.VIEW);
expect(element.classList.contains('mode-create')).toBeFalsy();
expect(element.classList.contains('mode-edit')).toBeFalsy();
expect(element.classList.contains('mode-view')).toBeFalsy();
expect(element.classList.contains('mode-delete')).toBeTruthy();
expect(element.classList.contains('mode-append')).toBeTruthy();
freeDraw.setMode(L.FreeDraw.MODES.APPEND);
expect(element.classList.contains('mode-create')).toBeFalsy();
expect(element.classList.contains('mode-edit')).toBeFalsy();
expect(element.classList.contains('mode-view')).toBeFalsy();
expect(element.classList.contains('mode-delete')).toBeFalsy();
expect(element.classList.contains('mode-append')).toBeTruthy();
freeDraw.setMode(0);
expect(element.classList.contains('mode-create')).toBeFalsy();
expect(element.classList.contains('mode-edit')).toBeFalsy();
expect(element.classList.contains('mode-view')).toBeTruthy();
expect(element.classList.contains('mode-delete')).toBeFalsy();
expect(element.classList.contains('mode-append')).toBeFalsy();
});
it('Should be able to handle the clicking of a polygon;', function() {
var polygon = createMockPolygon(),
fakeEvent = { originalEvent: { clientX: 12, clientY: 17 } };
spyOn(freeDraw, 'destroyPolygon').and.callThrough();
spyOn(freeDraw.map, 'latLngToContainerPoint').and.callThrough();
spyOn(L.LineUtil, 'pointToSegmentDistance').and.callThrough();
// Simulate the click on the polygon.
freeDraw.handlePolygonClick(polygon, fakeEvent);
// Ensure the necessary methods were invoked
expect(freeDraw.map.latLngToContainerPoint).toHaveBeenCalled();
expect(L.LineUtil.pointToSegmentDistance).toHaveBeenCalled();
expect(freeDraw.destroyPolygon).toHaveBeenCalled();
// ...And now let's try to add a polygon elbow instead of deleting.
polygon = createMockPolygon();
fakeEvent = { originalEvent: { clientX: 12, clientY: 17 } };
expect(polygon._latlngs.length).toEqual(5);
spyOn(freeDraw, 'createEdges').and.callThrough();
spyOn(freeDraw, 'destroyEdges').and.callThrough();
freeDraw.setMode(L.FreeDraw.MODES.APPEND);
freeDraw.handlePolygonClick(polygon, fakeEvent);
expect(freeDraw.createEdges).toHaveBeenCalled();
expect(freeDraw.destroyEdges).toHaveBeenCalled();
expect(polygon._latlngs.length).toEqual(6);
});
describe('Hull Algorithms:', function() {
it('Should be able to specify the map instance;', function() {
expect(freeDraw.hull.map).toBeNull();
freeDraw.hull.setMap({ map: true });
expect(typeof freeDraw.hull.map).toBe('object');
expect('map' in freeDraw.hull.map).toBeTruthy();
});
});
describe('Utility Methods:', function() {
it('Should be able to transform lat/long groups into MULTIPOLYGON;', function() {
var latLngs = [new L.LatLng(100, 100), new L.LatLng(200, 200), new L.LatLng(300, 300)],
multiPolygon = L.FreeDraw.Utilities.getMySQLMultiPolygon([latLngs]);
expect(typeof multiPolygon).toBe('string');
expect(multiPolygon).toEqual('MULTIPOLYGON(((100 100,200 200,300 300)))');
});
it('Should be able to transform lat/long groups into multiple POLYGON;', function() {
var latLngs = [new L.LatLng(100, 100), new L.LatLng(200, 200), new L.LatLng(300, 300)],
polygons = L.FreeDraw.Utilities.getMySQLPolygons([latLngs]);
expect(typeof polygons).toBe('object');
expect(polygons.length).toEqual(1);
expect(polygons[0]).toEqual('POLYGON((100 100,200 200,300 300))');
});
});
describe('State Memorisation:', function() {
it('Should be able to define the first state as an empty array;', function() {
expect(freeDraw.memory instanceof L.FreeDraw.Memory).toBeTruthy();
expect(Array.isArray(freeDraw.memory.states[0])).toBeTruthy();
expect(freeDraw.memory.states[0].length).toEqual(0);
});
it('Should be able to add a state to the state array;', function() {
var latLngs = [new L.LatLng(100, 100), new L.LatLng(200, 200), new L.LatLng(300, 300)];
freeDraw.memory.save([latLngs]);
expect(freeDraw.memory.states.length).toEqual(2);
});
it('Should be able to undo and redo the state;', function() {
var latLngs = [new L.LatLng(100, 100), new L.LatLng(200, 200), new L.LatLng(300, 300)];
freeDraw.memory.save([latLngs]);
expect(freeDraw.memory.current).toEqual(1);
freeDraw.memory.undo();
expect(freeDraw.memory.current).toEqual(0);
freeDraw.memory.redo();
expect(freeDraw.memory.current).toEqual(1);
});
it('Should be able to overwrite the redo state if the user desires;', function() {
var latLngs = [new L.LatLng(100, 100), new L.LatLng(200, 200), new L.LatLng(300, 300)];
freeDraw.memory.save([latLngs]);
expect(freeDraw.memory.current).toEqual(1);
freeDraw.memory.undo();
freeDraw.memory.save([latLngs]);
expect(freeDraw.memory.current).toEqual(1);
expect(freeDraw.memory.states.length).toEqual(2);
});
it('Should not be able to undo/redo more than is available;', function() {
var latLngs = [new L.LatLng(100, 100), new L.LatLng(200, 200), new L.LatLng(300, 300)];
freeDraw.memory.save([latLngs]);
expect(freeDraw.memory.canUndo()).toBeTruthy();
freeDraw.memory.undo();
expect(freeDraw.memory.canUndo()).toBeFalsy();
expect(freeDraw.memory.current).toEqual(0);
freeDraw.memory.undo(); freeDraw.memory.undo(); freeDraw.memory.undo(); freeDraw.memory.undo();
expect(freeDraw.memory.current).toEqual(0);
expect(freeDraw.memory.canRedo()).toBeTruthy();
freeDraw.memory.redo(); freeDraw.memory.redo(); freeDraw.memory.redo();
expect(freeDraw.memory.canRedo()).toBeFalsy();
expect(freeDraw.memory.current).toEqual(1);
});
});
});