Organize tests
This commit is contained in:
@@ -0,0 +1,301 @@
|
||||
import DragAndDrop from '../../../../../src/ol/interaction/DragAndDrop.js';
|
||||
import Event from '../../../../../src/ol/events/Event.js';
|
||||
import EventTarget from '../../../../../src/ol/events/Target.js';
|
||||
import GeoJSON from '../../../../../src/ol/format/GeoJSON.js';
|
||||
import MVT from '../../../../../src/ol/format/MVT.js';
|
||||
import VectorSource from '../../../../../src/ol/source/Vector.js';
|
||||
import View from '../../../../../src/ol/View.js';
|
||||
|
||||
where('FileReader').describe('ol.interaction.DragAndDrop', function () {
|
||||
let viewport, map, interaction;
|
||||
|
||||
beforeEach(function () {
|
||||
viewport = new EventTarget();
|
||||
map = {
|
||||
getViewport: function () {
|
||||
return viewport;
|
||||
},
|
||||
getView: function () {
|
||||
return new View();
|
||||
},
|
||||
};
|
||||
interaction = new DragAndDrop({
|
||||
formatConstructors: [GeoJSON],
|
||||
});
|
||||
});
|
||||
|
||||
describe('constructor', function () {
|
||||
it('can be constructed without arguments', function () {
|
||||
const interaction = new DragAndDrop();
|
||||
expect(interaction).to.be.an(DragAndDrop);
|
||||
});
|
||||
|
||||
it('sets formats on the instance', function () {
|
||||
expect(interaction.formats_).to.have.length(1);
|
||||
});
|
||||
|
||||
it('accepts a source option', function () {
|
||||
const source = new VectorSource();
|
||||
const drop = new DragAndDrop({
|
||||
formatConstructors: [GeoJSON],
|
||||
source: source,
|
||||
});
|
||||
expect(drop.source_).to.equal(source);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#setActive()', function () {
|
||||
it('registers and unregisters listeners', function () {
|
||||
interaction.setMap(map);
|
||||
interaction.setActive(true);
|
||||
expect(viewport.hasListener('dragenter')).to.be(true);
|
||||
expect(viewport.hasListener('dragover')).to.be(true);
|
||||
expect(viewport.hasListener('drop')).to.be(true);
|
||||
interaction.setActive(false);
|
||||
expect(viewport.hasListener('dragenter')).to.be(false);
|
||||
expect(viewport.hasListener('dragover')).to.be(false);
|
||||
expect(viewport.hasListener('drop')).to.be(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#setMap()', function () {
|
||||
it('registers and unregisters listeners', function () {
|
||||
interaction.setMap(map);
|
||||
expect(viewport.hasListener('dragenter')).to.be(true);
|
||||
expect(viewport.hasListener('dragover')).to.be(true);
|
||||
expect(viewport.hasListener('drop')).to.be(true);
|
||||
interaction.setMap(null);
|
||||
expect(viewport.hasListener('dragenter')).to.be(false);
|
||||
expect(viewport.hasListener('dragover')).to.be(false);
|
||||
expect(viewport.hasListener('drop')).to.be(false);
|
||||
});
|
||||
|
||||
it('registers and unregisters listeners on a custom target', function () {
|
||||
const customTarget = new EventTarget();
|
||||
interaction = new DragAndDrop({
|
||||
formatConstructors: [GeoJSON],
|
||||
target: customTarget,
|
||||
});
|
||||
interaction.setMap(map);
|
||||
expect(customTarget.hasListener('dragenter')).to.be(true);
|
||||
expect(customTarget.hasListener('dragover')).to.be(true);
|
||||
expect(customTarget.hasListener('drop')).to.be(true);
|
||||
interaction.setMap(null);
|
||||
expect(customTarget.hasListener('dragenter')).to.be(false);
|
||||
expect(customTarget.hasListener('dragover')).to.be(false);
|
||||
expect(customTarget.hasListener('drop')).to.be(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#handleDrop_', function () {
|
||||
let OrigFileReader;
|
||||
let mockReadAsText;
|
||||
let mockReadAsArrayBuffer;
|
||||
|
||||
beforeEach(function () {
|
||||
OrigFileReader = FileReader;
|
||||
mockReadAsText = false;
|
||||
mockReadAsArrayBuffer = false;
|
||||
|
||||
class MockFileReader extends EventTarget {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
}
|
||||
readAsText(file) {
|
||||
mockReadAsText = true;
|
||||
this.result = file;
|
||||
this.dispatchEvent('load');
|
||||
}
|
||||
readAsArrayBuffer(file) {
|
||||
mockReadAsArrayBuffer = true;
|
||||
this.result = new TextEncoder().encode(file).buffer;
|
||||
this.dispatchEvent('load');
|
||||
}
|
||||
}
|
||||
FileReader = MockFileReader;
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
FileReader = OrigFileReader;
|
||||
});
|
||||
|
||||
it('reads dropped files as text', function (done) {
|
||||
interaction.on('addfeatures', function (evt) {
|
||||
expect(evt.features.length).to.be(1);
|
||||
expect(mockReadAsText).to.be(true);
|
||||
expect(mockReadAsArrayBuffer).to.be(false);
|
||||
done();
|
||||
});
|
||||
interaction.setMap(map);
|
||||
const event = new Event();
|
||||
event.dataTransfer = {};
|
||||
event.type = 'dragenter';
|
||||
viewport.dispatchEvent(event);
|
||||
event.type = 'dragover';
|
||||
viewport.dispatchEvent(event);
|
||||
event.type = 'drop';
|
||||
event.dataTransfer.files = {
|
||||
length: 1,
|
||||
item: function () {
|
||||
return JSON.stringify({
|
||||
type: 'FeatureCollection',
|
||||
features: [{type: 'Feature', id: '1'}],
|
||||
});
|
||||
},
|
||||
};
|
||||
viewport.dispatchEvent(event);
|
||||
expect(event.dataTransfer.dropEffect).to.be('copy');
|
||||
expect(event.propagationStopped).to.be(true);
|
||||
});
|
||||
|
||||
it('reads dropped files as arraybuffer', function (done) {
|
||||
const drop = new DragAndDrop({
|
||||
formatConstructors: [GeoJSON, MVT],
|
||||
});
|
||||
drop.setMap(map);
|
||||
|
||||
drop.on('addfeatures', function (evt) {
|
||||
expect(evt.features.length).to.be(1);
|
||||
expect(mockReadAsText).to.be(false);
|
||||
expect(mockReadAsArrayBuffer).to.be(true);
|
||||
done();
|
||||
});
|
||||
|
||||
const event = new Event();
|
||||
event.dataTransfer = {};
|
||||
event.type = 'dragenter';
|
||||
viewport.dispatchEvent(event);
|
||||
event.type = 'dragover';
|
||||
viewport.dispatchEvent(event);
|
||||
event.type = 'drop';
|
||||
event.dataTransfer.files = {
|
||||
length: 1,
|
||||
item: function () {
|
||||
return JSON.stringify({
|
||||
type: 'FeatureCollection',
|
||||
features: [{type: 'Feature', id: '1'}],
|
||||
});
|
||||
},
|
||||
};
|
||||
viewport.dispatchEvent(event);
|
||||
expect(event.dataTransfer.dropEffect).to.be('copy');
|
||||
expect(event.propagationStopped).to.be(true);
|
||||
});
|
||||
|
||||
it('reads using constructed formats', function (done) {
|
||||
const drop = new DragAndDrop({
|
||||
formatConstructors: [new GeoJSON()],
|
||||
});
|
||||
drop.setMap(map);
|
||||
|
||||
drop.on('addfeatures', function (evt) {
|
||||
expect(evt.features.length).to.be(1);
|
||||
expect(mockReadAsText).to.be(true);
|
||||
expect(mockReadAsArrayBuffer).to.be(false);
|
||||
done();
|
||||
});
|
||||
|
||||
const event = new Event();
|
||||
event.dataTransfer = {};
|
||||
event.type = 'dragenter';
|
||||
viewport.dispatchEvent(event);
|
||||
event.type = 'dragover';
|
||||
viewport.dispatchEvent(event);
|
||||
event.type = 'drop';
|
||||
event.dataTransfer.files = {
|
||||
length: 1,
|
||||
item: function () {
|
||||
return JSON.stringify({
|
||||
type: 'FeatureCollection',
|
||||
features: [{type: 'Feature', id: '1'}],
|
||||
});
|
||||
},
|
||||
};
|
||||
viewport.dispatchEvent(event);
|
||||
expect(event.dataTransfer.dropEffect).to.be('copy');
|
||||
expect(event.propagationStopped).to.be(true);
|
||||
});
|
||||
|
||||
it('reads using arraybuffer formats', function (done) {
|
||||
class binaryGeoJSON extends GeoJSON {
|
||||
constructor(options) {
|
||||
super(options);
|
||||
}
|
||||
getType() {
|
||||
return 'arraybuffer';
|
||||
}
|
||||
readFeatures(source, options) {
|
||||
const data = new TextDecoder().decode(source);
|
||||
return super.readFeatures(data, options);
|
||||
}
|
||||
}
|
||||
|
||||
const drop = new DragAndDrop({
|
||||
formatConstructors: [binaryGeoJSON],
|
||||
});
|
||||
drop.setMap(map);
|
||||
|
||||
drop.on('addfeatures', function (evt) {
|
||||
expect(evt.features.length).to.be(1);
|
||||
expect(mockReadAsText).to.be(false);
|
||||
expect(mockReadAsArrayBuffer).to.be(true);
|
||||
done();
|
||||
});
|
||||
|
||||
const event = new Event();
|
||||
event.dataTransfer = {};
|
||||
event.type = 'dragenter';
|
||||
viewport.dispatchEvent(event);
|
||||
event.type = 'dragover';
|
||||
viewport.dispatchEvent(event);
|
||||
event.type = 'drop';
|
||||
event.dataTransfer.files = {
|
||||
length: 1,
|
||||
item: function () {
|
||||
return JSON.stringify({
|
||||
type: 'FeatureCollection',
|
||||
features: [{type: 'Feature', id: '1'}],
|
||||
});
|
||||
},
|
||||
};
|
||||
viewport.dispatchEvent(event);
|
||||
expect(event.dataTransfer.dropEffect).to.be('copy');
|
||||
expect(event.propagationStopped).to.be(true);
|
||||
});
|
||||
|
||||
it('adds dropped features to a source', function (done) {
|
||||
const source = new VectorSource();
|
||||
const drop = new DragAndDrop({
|
||||
formatConstructors: [GeoJSON],
|
||||
source: source,
|
||||
});
|
||||
drop.setMap(map);
|
||||
|
||||
drop.on('addfeatures', function (evt) {
|
||||
const features = source.getFeatures();
|
||||
expect(features.length).to.be(1);
|
||||
done();
|
||||
});
|
||||
|
||||
const event = new Event();
|
||||
event.dataTransfer = {};
|
||||
event.type = 'dragenter';
|
||||
viewport.dispatchEvent(event);
|
||||
event.type = 'dragover';
|
||||
viewport.dispatchEvent(event);
|
||||
event.type = 'drop';
|
||||
event.dataTransfer.files = {
|
||||
length: 1,
|
||||
item: function () {
|
||||
return JSON.stringify({
|
||||
type: 'FeatureCollection',
|
||||
features: [{type: 'Feature', id: '1'}],
|
||||
});
|
||||
},
|
||||
};
|
||||
viewport.dispatchEvent(event);
|
||||
expect(event.dataTransfer.dropEffect).to.be('copy');
|
||||
expect(event.propagationStopped).to.be(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,97 @@
|
||||
import DragRotateAndZoom from '../../../../../src/ol/interaction/DragRotateAndZoom.js';
|
||||
import Event from '../../../../../src/ol/events/Event.js';
|
||||
import Map from '../../../../../src/ol/Map.js';
|
||||
import MapBrowserEvent from '../../../../../src/ol/MapBrowserEvent.js';
|
||||
import VectorLayer from '../../../../../src/ol/layer/Vector.js';
|
||||
import VectorSource from '../../../../../src/ol/source/Vector.js';
|
||||
import View from '../../../../../src/ol/View.js';
|
||||
|
||||
describe('ol.interaction.DragRotateAndZoom', function () {
|
||||
describe('constructor', function () {
|
||||
it('can be constructed without arguments', function () {
|
||||
const instance = new DragRotateAndZoom();
|
||||
expect(instance).to.be.an(DragRotateAndZoom);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#handleDragEvent()', function () {
|
||||
let target, map, interaction;
|
||||
|
||||
const width = 360;
|
||||
const height = 180;
|
||||
|
||||
beforeEach(function (done) {
|
||||
target = document.createElement('div');
|
||||
const style = target.style;
|
||||
style.position = 'absolute';
|
||||
style.left = '-1000px';
|
||||
style.top = '-1000px';
|
||||
style.width = width + 'px';
|
||||
style.height = height + 'px';
|
||||
document.body.appendChild(target);
|
||||
const source = new VectorSource();
|
||||
const layer = new VectorLayer({source: source});
|
||||
interaction = new DragRotateAndZoom();
|
||||
map = new Map({
|
||||
target: target,
|
||||
layers: [layer],
|
||||
interactions: [interaction],
|
||||
view: new View({
|
||||
projection: 'EPSG:4326',
|
||||
center: [0, 0],
|
||||
resolution: 1,
|
||||
}),
|
||||
});
|
||||
map.once('postrender', function () {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
map.dispose();
|
||||
document.body.removeChild(target);
|
||||
});
|
||||
|
||||
it('does not rotate when rotation is disabled on the view', function () {
|
||||
const pointerEvent = new Event();
|
||||
pointerEvent.type = 'pointermove';
|
||||
pointerEvent.clientX = 20;
|
||||
pointerEvent.clientY = 10;
|
||||
pointerEvent.pointerType = 'mouse';
|
||||
let event = new MapBrowserEvent('pointermove', map, pointerEvent, true);
|
||||
interaction.lastAngle_ = Math.PI;
|
||||
|
||||
let callCount = 0;
|
||||
|
||||
let view = map.getView();
|
||||
view.on('change:rotation', function () {
|
||||
callCount++;
|
||||
});
|
||||
|
||||
interaction.handleDragEvent(event);
|
||||
expect(callCount).to.be(1);
|
||||
expect(interaction.lastAngle_).to.be(-0.8308214428190254);
|
||||
|
||||
callCount = 0;
|
||||
view = new View({
|
||||
projection: 'EPSG:4326',
|
||||
center: [0, 0],
|
||||
resolution: 1,
|
||||
enableRotation: false,
|
||||
});
|
||||
map.setView(view);
|
||||
view.on('change:rotation', function () {
|
||||
callCount++;
|
||||
});
|
||||
|
||||
pointerEvent.type = 'pointermove';
|
||||
pointerEvent.clientX = 24;
|
||||
pointerEvent.clientY = 16;
|
||||
pointerEvent.pointerType = 'mouse';
|
||||
event = new MapBrowserEvent('pointermove', map, pointerEvent, true);
|
||||
|
||||
interaction.handleDragEvent(event);
|
||||
expect(callCount).to.be(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,106 @@
|
||||
import DragZoom from '../../../../../src/ol/interaction/DragZoom.js';
|
||||
import Map from '../../../../../src/ol/Map.js';
|
||||
import RenderBox from '../../../../../src/ol/render/Box.js';
|
||||
import VectorLayer from '../../../../../src/ol/layer/Vector.js';
|
||||
import VectorSource from '../../../../../src/ol/source/Vector.js';
|
||||
import View from '../../../../../src/ol/View.js';
|
||||
import {getCenter} from '../../../../../src/ol/extent.js';
|
||||
import {fromExtent as polygonFromExtent} from '../../../../../src/ol/geom/Polygon.js';
|
||||
|
||||
describe('ol.interaction.DragZoom', function () {
|
||||
let target, map, source;
|
||||
|
||||
const width = 360;
|
||||
const height = 180;
|
||||
|
||||
beforeEach(function (done) {
|
||||
target = document.createElement('div');
|
||||
const style = target.style;
|
||||
style.position = 'absolute';
|
||||
style.left = '-1000px';
|
||||
style.top = '-1000px';
|
||||
style.width = width + 'px';
|
||||
style.height = height + 'px';
|
||||
document.body.appendChild(target);
|
||||
source = new VectorSource();
|
||||
const layer = new VectorLayer({source: source});
|
||||
map = new Map({
|
||||
target: target,
|
||||
layers: [layer],
|
||||
view: new View({
|
||||
projection: 'EPSG:4326',
|
||||
center: [0, 0],
|
||||
resolution: 1,
|
||||
}),
|
||||
});
|
||||
map.once('postrender', function () {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
map.dispose();
|
||||
document.body.removeChild(target);
|
||||
});
|
||||
|
||||
describe('constructor', function () {
|
||||
it('can be constructed without arguments', function () {
|
||||
const instance = new DragZoom();
|
||||
expect(instance).to.be.an(DragZoom);
|
||||
});
|
||||
it('sets "ol-dragzoom" as box className', function () {
|
||||
const instance = new DragZoom();
|
||||
expect(instance.box_.element_.className).to.be('ol-box ol-dragzoom');
|
||||
});
|
||||
it('sets a custom box className', function () {
|
||||
const instance = new DragZoom({className: 'test-dragzoom'});
|
||||
expect(instance.box_.element_.className).to.be('ol-box test-dragzoom');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#onBoxEnd()', function () {
|
||||
it('centers the view on the box geometry', function (done) {
|
||||
const interaction = new DragZoom({
|
||||
duration: 10,
|
||||
});
|
||||
map.addInteraction(interaction);
|
||||
|
||||
const box = new RenderBox();
|
||||
const extent = [-110, 40, -90, 60];
|
||||
box.geometry_ = polygonFromExtent(extent);
|
||||
interaction.box_ = box;
|
||||
|
||||
interaction.onBoxEnd();
|
||||
setTimeout(function () {
|
||||
const view = map.getView();
|
||||
const center = view.getCenterInternal();
|
||||
expect(center).to.eql(getCenter(extent));
|
||||
done();
|
||||
}, 50);
|
||||
});
|
||||
|
||||
it('sets new resolution while zooming out', function (done) {
|
||||
const interaction = new DragZoom({
|
||||
duration: 10,
|
||||
out: true,
|
||||
});
|
||||
map.addInteraction(interaction);
|
||||
|
||||
const box = new RenderBox();
|
||||
const extent = [-11.25, -11.25, 11.25, 11.25];
|
||||
box.geometry_ = polygonFromExtent(extent);
|
||||
interaction.box_ = box;
|
||||
|
||||
map.getView().setResolution(0.25);
|
||||
setTimeout(function () {
|
||||
interaction.onBoxEnd();
|
||||
setTimeout(function () {
|
||||
const view = map.getView();
|
||||
const resolution = view.getResolution();
|
||||
expect(resolution).to.eql(view.getConstrainedResolution(0.5));
|
||||
done();
|
||||
}, 50);
|
||||
}, 50);
|
||||
});
|
||||
});
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,140 @@
|
||||
import ExtentInteraction from '../../../../../src/ol/interaction/Extent.js';
|
||||
import Map from '../../../../../src/ol/Map.js';
|
||||
import MapBrowserEvent from '../../../../../src/ol/MapBrowserEvent.js';
|
||||
import View from '../../../../../src/ol/View.js';
|
||||
|
||||
describe('ol.interaction.Extent', function () {
|
||||
let map, interaction;
|
||||
|
||||
const width = 360;
|
||||
const height = 180;
|
||||
|
||||
beforeEach(function () {
|
||||
const target = createMapDiv(width, height);
|
||||
|
||||
map = new Map({
|
||||
target: target,
|
||||
layers: [],
|
||||
view: new View({
|
||||
projection: 'EPSG:4326',
|
||||
center: [0, 0],
|
||||
resolution: 1,
|
||||
}),
|
||||
});
|
||||
map.renderSync();
|
||||
|
||||
interaction = new ExtentInteraction();
|
||||
map.addInteraction(interaction);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
if (map) {
|
||||
disposeMap(map);
|
||||
}
|
||||
map = null;
|
||||
interaction = null;
|
||||
});
|
||||
|
||||
/**
|
||||
* Simulates a browser event on the map viewport. The client x/y location
|
||||
* will be adjusted as if the map were centered at 0,0.
|
||||
* @param {string} type Event type.
|
||||
* @param {number} x Horizontal offset from map center.
|
||||
* @param {number} y Vertical offset from map center.
|
||||
* @param {boolean} [opt_shiftKey] Shift key is pressed.
|
||||
* @param {number} button The mouse button.
|
||||
*/
|
||||
function simulateEvent(type, x, y, opt_shiftKey, button) {
|
||||
const viewport = map.getViewport();
|
||||
// calculated in case body has top < 0 (test runner with small window)
|
||||
const position = viewport.getBoundingClientRect();
|
||||
const shiftKey = opt_shiftKey !== undefined ? opt_shiftKey : false;
|
||||
const pointerEvent = {};
|
||||
pointerEvent.type = type;
|
||||
pointerEvent.target = viewport.firstChild;
|
||||
pointerEvent.button = button;
|
||||
pointerEvent.clientX = position.left + x + width / 2;
|
||||
pointerEvent.clientY = position.top - y + height / 2;
|
||||
pointerEvent.shiftKey = shiftKey;
|
||||
pointerEvent.pointerId = 0;
|
||||
pointerEvent.preventDefault = function () {};
|
||||
const event = new MapBrowserEvent(type, map, pointerEvent);
|
||||
event.originalEvent.pointerId = 1;
|
||||
map.handleMapBrowserEvent(event);
|
||||
}
|
||||
|
||||
describe('Constructor', function () {
|
||||
it('can be configured with an extent', function () {
|
||||
expect(function () {
|
||||
new ExtentInteraction({
|
||||
extent: [-10, -10, 10, 10],
|
||||
});
|
||||
}).to.not.throwException();
|
||||
});
|
||||
});
|
||||
|
||||
describe('snap to vertex', function () {
|
||||
it('snap to vertex works', function () {
|
||||
interaction.setExtent([-50, -50, 50, 50]);
|
||||
|
||||
expect(interaction.snapToVertex_([230, 40], map)).to.eql([50, 50]);
|
||||
expect(interaction.snapToVertex_([231, 41], map)).to.eql([50, 50]);
|
||||
});
|
||||
|
||||
it('snap to edge works', function () {
|
||||
interaction.setExtent([-50, -50, 50, 50]);
|
||||
|
||||
expect(interaction.snapToVertex_([230, 90], map)).to.eql([50, 0]);
|
||||
expect(interaction.snapToVertex_([230, 89], map)).to.eql([50, 1]);
|
||||
expect(interaction.snapToVertex_([231, 90], map)).to.eql([50, 0]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('draw extent', function () {
|
||||
it('drawing extent works', function () {
|
||||
simulateEvent('pointerdown', -50, -50, false, 0);
|
||||
simulateEvent('pointerdrag', 50, 50, false, 0);
|
||||
simulateEvent('pointerup', 50, 50, false, 0);
|
||||
|
||||
expect(interaction.getExtent()).to.eql([-50, -50, 50, 50]);
|
||||
});
|
||||
|
||||
it('clicking off extent nulls extent', function () {
|
||||
interaction.setExtent([-50, -50, 50, 50]);
|
||||
|
||||
simulateEvent('pointerdown', -10, -10, false, 0);
|
||||
simulateEvent('pointerup', -10, -10, false, 0);
|
||||
|
||||
expect(interaction.getExtent()).to.equal(null);
|
||||
});
|
||||
|
||||
it('clicking on extent does not null extent', function () {
|
||||
interaction.setExtent([-50, -50, 50, 50]);
|
||||
|
||||
simulateEvent('pointerdown', 50, 50, false, 0);
|
||||
simulateEvent('pointerup', 50, 50, false, 0);
|
||||
|
||||
expect(interaction.getExtent()).to.eql([-50, -50, 50, 50]);
|
||||
});
|
||||
|
||||
it('snap and drag vertex works', function () {
|
||||
interaction.setExtent([-50, -50, 50, 50]);
|
||||
|
||||
simulateEvent('pointerdown', 51, 49, false, 0);
|
||||
simulateEvent('pointerdrag', -70, -40, false, 0);
|
||||
simulateEvent('pointerup', -70, -40, false, 0);
|
||||
|
||||
expect(interaction.getExtent()).to.eql([-70, -50, -50, -40]);
|
||||
});
|
||||
|
||||
it('snap and drag edge works', function () {
|
||||
interaction.setExtent([-50, -50, 50, 50]);
|
||||
|
||||
simulateEvent('pointerdown', 51, 5, false, 0);
|
||||
simulateEvent('pointerdrag', 20, -30, false, 0);
|
||||
simulateEvent('pointerup', 20, -30, false, 0);
|
||||
|
||||
expect(interaction.getExtent()).to.eql([-50, -50, 20, 50]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,108 @@
|
||||
import EventTarget from '../../../../../src/ol/events/Target.js';
|
||||
import Interaction, {
|
||||
zoomByDelta,
|
||||
} from '../../../../../src/ol/interaction/Interaction.js';
|
||||
import {FALSE} from '../../../../../src/ol/functions.js';
|
||||
import {Map, View} from '../../../../../src/ol/index.js';
|
||||
import {
|
||||
clearUserProjection,
|
||||
useGeographic,
|
||||
} from '../../../../../src/ol/proj.js';
|
||||
|
||||
describe('ol.interaction.Interaction', function () {
|
||||
describe('constructor', function () {
|
||||
let interaction;
|
||||
|
||||
beforeEach(function () {
|
||||
interaction = new Interaction({});
|
||||
});
|
||||
|
||||
it('creates a new interaction', function () {
|
||||
expect(interaction).to.be.a(Interaction);
|
||||
expect(interaction).to.be.a(EventTarget);
|
||||
});
|
||||
|
||||
it('creates an active interaction', function () {
|
||||
expect(interaction.getActive()).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getMap()', function () {
|
||||
it('retrieves the associated map', function () {
|
||||
const map = new Map({});
|
||||
const interaction = new Interaction({});
|
||||
interaction.setMap(map);
|
||||
expect(interaction.getMap()).to.be(map);
|
||||
});
|
||||
|
||||
it('returns null if no map', function () {
|
||||
const interaction = new Interaction({});
|
||||
expect(interaction.getMap()).to.be(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#setMap()', function () {
|
||||
it('allows a map to be set', function () {
|
||||
const map = new Map({});
|
||||
const interaction = new Interaction({});
|
||||
interaction.setMap(map);
|
||||
expect(interaction.getMap()).to.be(map);
|
||||
});
|
||||
|
||||
it('accepts null', function () {
|
||||
const interaction = new Interaction({});
|
||||
interaction.setMap(null);
|
||||
expect(interaction.getMap()).to.be(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#handleEvent()', function () {
|
||||
class MockInteraction extends Interaction {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
}
|
||||
handleEvent(mapBrowserEvent) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
it('has a default event handler', function () {
|
||||
const interaction = new Interaction({});
|
||||
expect(interaction.handleEvent()).to.be(true);
|
||||
});
|
||||
|
||||
it('allows event handler overrides via options', function () {
|
||||
const interaction = new Interaction({
|
||||
handleEvent: FALSE,
|
||||
});
|
||||
expect(interaction.handleEvent()).to.be(false);
|
||||
});
|
||||
|
||||
it('allows event handler overrides via class extension', function () {
|
||||
const interaction = new MockInteraction({});
|
||||
expect(interaction.handleEvent()).to.be(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('zoomByDelta - useGeographic', () => {
|
||||
beforeEach(useGeographic);
|
||||
afterEach(clearUserProjection);
|
||||
|
||||
it('works with a user projection set', () => {
|
||||
const view = new View({
|
||||
center: [0, 0],
|
||||
zoom: 0,
|
||||
});
|
||||
|
||||
const spy = sinon.spy(view, 'animate');
|
||||
|
||||
const anchor = [90, 45];
|
||||
const duration = 10;
|
||||
zoomByDelta(view, 1, anchor, duration);
|
||||
|
||||
expect(spy.callCount).to.be(1);
|
||||
const options = spy.getCall(0).args[0];
|
||||
expect(options.anchor).to.be(anchor);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,57 @@
|
||||
import Event from '../../../../../src/ol/events/Event.js';
|
||||
import Map from '../../../../../src/ol/Map.js';
|
||||
import MapBrowserEvent from '../../../../../src/ol/MapBrowserEvent.js';
|
||||
import View from '../../../../../src/ol/View.js';
|
||||
|
||||
describe('ol.interaction.KeyboardPan', function () {
|
||||
let map;
|
||||
|
||||
beforeEach(function () {
|
||||
map = new Map({
|
||||
target: createMapDiv(100, 100),
|
||||
view: new View({
|
||||
center: [0, 0],
|
||||
resolutions: [1],
|
||||
zoom: 0,
|
||||
}),
|
||||
});
|
||||
map.renderSync();
|
||||
});
|
||||
afterEach(function () {
|
||||
disposeMap(map);
|
||||
});
|
||||
|
||||
describe('handleEvent()', function () {
|
||||
it('pans on arrow keys', function () {
|
||||
const view = map.getView();
|
||||
const spy = sinon.spy(view, 'animateInternal');
|
||||
const event = new MapBrowserEvent('keydown', map, {
|
||||
type: 'keydown',
|
||||
target: map.getTargetElement(),
|
||||
preventDefault: Event.prototype.preventDefault,
|
||||
});
|
||||
|
||||
event.originalEvent.keyCode = 40; // DOWN
|
||||
map.handleMapBrowserEvent(event);
|
||||
expect(spy.getCall(0).args[0].center).to.eql([0, -128]);
|
||||
view.setCenter([0, 0]);
|
||||
|
||||
event.originalEvent.keyCode = 38; // UP
|
||||
map.handleMapBrowserEvent(event);
|
||||
expect(spy.getCall(1).args[0].center).to.eql([0, 128]);
|
||||
view.setCenter([0, 0]);
|
||||
|
||||
event.originalEvent.keyCode = 37; // LEFT
|
||||
map.handleMapBrowserEvent(event);
|
||||
expect(spy.getCall(2).args[0].center).to.eql([-128, 0]);
|
||||
view.setCenter([0, 0]);
|
||||
|
||||
event.originalEvent.keyCode = 39; // RIGHT
|
||||
map.handleMapBrowserEvent(event);
|
||||
expect(spy.getCall(3).args[0].center).to.eql([128, 0]);
|
||||
view.setCenter([0, 0]);
|
||||
|
||||
view.animateInternal.restore();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,61 @@
|
||||
import Event from '../../../../../src/ol/events/Event.js';
|
||||
import Map from '../../../../../src/ol/Map.js';
|
||||
import MapBrowserEvent from '../../../../../src/ol/MapBrowserEvent.js';
|
||||
import View from '../../../../../src/ol/View.js';
|
||||
|
||||
describe('ol.interaction.KeyboardZoom', function () {
|
||||
let map;
|
||||
|
||||
beforeEach(function () {
|
||||
map = new Map({
|
||||
target: createMapDiv(100, 100),
|
||||
view: new View({
|
||||
center: [0, 0],
|
||||
resolutions: [4, 2, 1],
|
||||
zoom: 1,
|
||||
}),
|
||||
});
|
||||
map.renderSync();
|
||||
});
|
||||
afterEach(function () {
|
||||
disposeMap(map);
|
||||
});
|
||||
|
||||
describe('handleEvent()', function () {
|
||||
it('zooms on + and - keys', function () {
|
||||
const view = map.getView();
|
||||
const spy = sinon.spy(view, 'animateInternal');
|
||||
const event = new MapBrowserEvent('keydown', map, {
|
||||
type: 'keydown',
|
||||
target: map.getTargetElement(),
|
||||
preventDefault: Event.prototype.preventDefault,
|
||||
});
|
||||
|
||||
event.originalEvent.charCode = '+'.charCodeAt(0);
|
||||
map.handleMapBrowserEvent(event);
|
||||
expect(spy.getCall(0).args[0].resolution).to.eql(1);
|
||||
view.setResolution(2);
|
||||
|
||||
event.originalEvent.charCode = '-'.charCodeAt(0);
|
||||
map.handleMapBrowserEvent(event);
|
||||
expect(spy.getCall(1).args[0].resolution).to.eql(4);
|
||||
view.setResolution(2);
|
||||
|
||||
view.animateInternal.restore();
|
||||
});
|
||||
|
||||
it('does nothing if the target is editable', function () {
|
||||
const view = map.getView();
|
||||
const spy = sinon.spy(view, 'animateInternal');
|
||||
const event = new MapBrowserEvent('keydown', map, {
|
||||
type: 'keydown',
|
||||
target: document.createElement('input'),
|
||||
preventDefault: Event.prototype.preventDefault,
|
||||
});
|
||||
|
||||
event.originalEvent.charCode = '+'.charCodeAt(0);
|
||||
map.handleMapBrowserEvent(event);
|
||||
expect(spy.called).to.be(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,152 @@
|
||||
import Event from '../../../../../src/ol/events/Event.js';
|
||||
import Map from '../../../../../src/ol/Map.js';
|
||||
import MapBrowserEvent from '../../../../../src/ol/MapBrowserEvent.js';
|
||||
import MouseWheelZoom, {
|
||||
Mode,
|
||||
} from '../../../../../src/ol/interaction/MouseWheelZoom.js';
|
||||
import View from '../../../../../src/ol/View.js';
|
||||
import {DEVICE_PIXEL_RATIO, FIREFOX} from '../../../../../src/ol/has.js';
|
||||
|
||||
describe('ol.interaction.MouseWheelZoom', function () {
|
||||
let map, interaction;
|
||||
|
||||
beforeEach(function () {
|
||||
interaction = new MouseWheelZoom();
|
||||
map = new Map({
|
||||
target: createMapDiv(100, 100),
|
||||
interactions: [interaction],
|
||||
view: new View({
|
||||
center: [0, 0],
|
||||
resolutions: [2, 1, 0.5],
|
||||
zoom: 1,
|
||||
}),
|
||||
});
|
||||
map.renderSync();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
disposeMap(map);
|
||||
map = null;
|
||||
interaction = null;
|
||||
});
|
||||
|
||||
describe('timeout duration', function () {
|
||||
let clock;
|
||||
beforeEach(function () {
|
||||
sinon.spy(interaction, 'handleWheelZoom_');
|
||||
clock = sinon.useFakeTimers();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
clock.restore();
|
||||
interaction.handleWheelZoom_.restore();
|
||||
});
|
||||
|
||||
it('works with the default value', function (done) {
|
||||
const event = new MapBrowserEvent('wheel', map, {
|
||||
type: 'wheel',
|
||||
target: map.getViewport(),
|
||||
preventDefault: Event.prototype.preventDefault,
|
||||
});
|
||||
|
||||
map.handleMapBrowserEvent(event);
|
||||
clock.tick(50);
|
||||
// default timeout is 80 ms, not called yet
|
||||
expect(interaction.handleWheelZoom_.called).to.be(false);
|
||||
|
||||
clock.tick(30);
|
||||
expect(interaction.handleWheelZoom_.called).to.be(true);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('handleEvent()', function () {
|
||||
if (FIREFOX) {
|
||||
it('works on Firefox in DOM_DELTA_PIXEL mode (trackpad)', function (done) {
|
||||
map.once('postrender', function () {
|
||||
expect(interaction.mode_).to.be(Mode.TRACKPAD);
|
||||
done();
|
||||
});
|
||||
const event = new MapBrowserEvent('wheel', map, {
|
||||
type: 'wheel',
|
||||
deltaMode: WheelEvent.DOM_DELTA_PIXEL,
|
||||
deltaY: DEVICE_PIXEL_RATIO,
|
||||
target: map.getViewport(),
|
||||
preventDefault: Event.prototype.preventDefault,
|
||||
});
|
||||
event.coordinate = [0, 0];
|
||||
map.handleMapBrowserEvent(event);
|
||||
});
|
||||
}
|
||||
|
||||
if (!FIREFOX) {
|
||||
it('works in DOM_DELTA_PIXEL mode (trackpad)', function (done) {
|
||||
map.once('postrender', function () {
|
||||
expect(interaction.mode_).to.be(Mode.TRACKPAD);
|
||||
done();
|
||||
});
|
||||
const event = new MapBrowserEvent('wheel', map, {
|
||||
type: 'wheel',
|
||||
deltaMode: WheelEvent.DOM_DELTA_PIXEL,
|
||||
deltaY: 1,
|
||||
target: map.getViewport(),
|
||||
preventDefault: Event.prototype.preventDefault,
|
||||
});
|
||||
event.coordinate = [0, 0];
|
||||
map.handleMapBrowserEvent(event);
|
||||
});
|
||||
}
|
||||
|
||||
describe('spying on view.animateInternal()', function () {
|
||||
let view;
|
||||
beforeEach(function () {
|
||||
view = map.getView();
|
||||
sinon.spy(view, 'animateInternal');
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
view.animateInternal.restore();
|
||||
});
|
||||
|
||||
it('works in DOM_DELTA_LINE mode (wheel)', function (done) {
|
||||
map.once('postrender', function () {
|
||||
const call = view.animateInternal.getCall(0);
|
||||
expect(call.args[0].resolution).to.be(2);
|
||||
expect(call.args[0].anchor).to.eql([0, 0]);
|
||||
done();
|
||||
});
|
||||
|
||||
const event = new MapBrowserEvent('wheel', map, {
|
||||
type: 'wheel',
|
||||
deltaMode: WheelEvent.DOM_DELTA_LINE,
|
||||
deltaY: 20,
|
||||
target: map.getViewport(),
|
||||
preventDefault: Event.prototype.preventDefault,
|
||||
});
|
||||
event.coordinate = [0, 0];
|
||||
|
||||
map.handleMapBrowserEvent(event);
|
||||
});
|
||||
|
||||
it('works on all browsers (wheel)', function (done) {
|
||||
map.once('postrender', function () {
|
||||
const call = view.animateInternal.getCall(0);
|
||||
expect(call.args[0].resolution).to.be(2);
|
||||
expect(call.args[0].anchor).to.eql([0, 0]);
|
||||
done();
|
||||
});
|
||||
|
||||
const event = new MapBrowserEvent('wheel', map, {
|
||||
type: 'wheel',
|
||||
deltaY: 300,
|
||||
target: map.getViewport(),
|
||||
preventDefault: Event.prototype.preventDefault,
|
||||
});
|
||||
event.coordinate = [0, 0];
|
||||
|
||||
map.handleMapBrowserEvent(event);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,133 @@
|
||||
import Event from '../../../../../src/ol/events/Event.js';
|
||||
import Map from '../../../../../src/ol/Map.js';
|
||||
import MapBrowserEvent from '../../../../../src/ol/MapBrowserEvent.js';
|
||||
import PointerInteraction from '../../../../../src/ol/interaction/Pointer.js';
|
||||
|
||||
describe('ol.interaction.Pointer', function () {
|
||||
describe('#handleEvent', function () {
|
||||
let event;
|
||||
let defaultPrevented;
|
||||
|
||||
beforeEach(function () {
|
||||
const type = 'pointerdown';
|
||||
const pointerEvent = new Event();
|
||||
pointerEvent.type = type;
|
||||
pointerEvent.pointerId = 0;
|
||||
pointerEvent.preventDefault = function () {
|
||||
defaultPrevented = true;
|
||||
};
|
||||
event = new MapBrowserEvent(type, new Map(), pointerEvent);
|
||||
defaultPrevented = false;
|
||||
});
|
||||
|
||||
it('does not prevent default on handled down event', function () {
|
||||
const interaction = new PointerInteraction({
|
||||
handleDownEvent: function () {
|
||||
return true;
|
||||
},
|
||||
});
|
||||
interaction.handleEvent(event);
|
||||
expect(defaultPrevented).to.be(false);
|
||||
});
|
||||
|
||||
it('does not prevent default on unhandled down event', function () {
|
||||
const interaction = new PointerInteraction({
|
||||
handleDownEvent: function () {
|
||||
return false;
|
||||
},
|
||||
});
|
||||
interaction.handleEvent(event);
|
||||
expect(defaultPrevented).to.be(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('event handlers', function () {
|
||||
let handleDownCalled, handleDragCalled, handleMoveCalled, handleUpCalled;
|
||||
|
||||
const flagHandleDown = function () {
|
||||
handleDownCalled = true;
|
||||
};
|
||||
|
||||
const flagHandleDrag = function () {
|
||||
handleDragCalled = true;
|
||||
};
|
||||
|
||||
const flagHandleMove = function () {
|
||||
handleMoveCalled = true;
|
||||
};
|
||||
|
||||
const flagHandleUp = function () {
|
||||
handleUpCalled = true;
|
||||
};
|
||||
|
||||
class MockPointerInteraction extends PointerInteraction {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
}
|
||||
handleDownEvent(mapBrowserEvent) {
|
||||
flagHandleDown();
|
||||
return super.handleDownEvent(mapBrowserEvent);
|
||||
}
|
||||
handleDragEvent(mapBrowserEvent) {
|
||||
flagHandleDrag();
|
||||
}
|
||||
handleMoveEvent(mapBrowserEvent) {
|
||||
flagHandleMove();
|
||||
}
|
||||
handleUpEvent(mapBrowserEvent) {
|
||||
flagHandleUp();
|
||||
return super.handleUpEvent(mapBrowserEvent);
|
||||
}
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
handleDownCalled = false;
|
||||
handleDragCalled = false;
|
||||
handleMoveCalled = false;
|
||||
handleUpCalled = false;
|
||||
});
|
||||
|
||||
it('has default event handlers', function () {
|
||||
const interaction = new PointerInteraction({});
|
||||
expect(interaction.handleDownEvent()).to.be(false);
|
||||
expect(interaction.handleUpEvent()).to.be(false);
|
||||
});
|
||||
|
||||
it('allows event handler overrides via options', function () {
|
||||
const interaction = new PointerInteraction({
|
||||
handleDownEvent: flagHandleDown,
|
||||
handleDragEvent: flagHandleDrag,
|
||||
handleMoveEvent: flagHandleMove,
|
||||
handleUpEvent: flagHandleUp,
|
||||
});
|
||||
|
||||
interaction.handleDownEvent();
|
||||
expect(handleDownCalled).to.be(true);
|
||||
|
||||
interaction.handleDragEvent();
|
||||
expect(handleDragCalled).to.be(true);
|
||||
|
||||
interaction.handleMoveEvent();
|
||||
expect(handleMoveCalled).to.be(true);
|
||||
|
||||
interaction.handleUpEvent();
|
||||
expect(handleUpCalled).to.be(true);
|
||||
});
|
||||
|
||||
it('allows event handler overrides via class extension', function () {
|
||||
const interaction = new MockPointerInteraction({});
|
||||
|
||||
interaction.handleDownEvent();
|
||||
expect(handleDownCalled).to.be(true);
|
||||
|
||||
interaction.handleDragEvent();
|
||||
expect(handleDragCalled).to.be(true);
|
||||
|
||||
interaction.handleMoveEvent();
|
||||
expect(handleMoveCalled).to.be(true);
|
||||
|
||||
interaction.handleUpEvent();
|
||||
expect(handleUpCalled).to.be(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,516 @@
|
||||
import Collection from '../../../../../src/ol/Collection.js';
|
||||
import Feature from '../../../../../src/ol/Feature.js';
|
||||
import Interaction from '../../../../../src/ol/interaction/Interaction.js';
|
||||
import Map from '../../../../../src/ol/Map.js';
|
||||
import MapBrowserEvent from '../../../../../src/ol/MapBrowserEvent.js';
|
||||
import MapBrowserEventType from '../../../../../src/ol/MapBrowserEventType.js';
|
||||
import Polygon from '../../../../../src/ol/geom/Polygon.js';
|
||||
import Select from '../../../../../src/ol/interaction/Select.js';
|
||||
import Style from '../../../../../src/ol/style/Style.js';
|
||||
import VectorLayer from '../../../../../src/ol/layer/Vector.js';
|
||||
import VectorSource from '../../../../../src/ol/source/Vector.js';
|
||||
import View from '../../../../../src/ol/View.js';
|
||||
|
||||
describe('ol.interaction.Select', function () {
|
||||
let target, map, layer, source;
|
||||
|
||||
const width = 360;
|
||||
const height = 180;
|
||||
|
||||
beforeEach(function (done) {
|
||||
target = document.createElement('div');
|
||||
|
||||
const style = target.style;
|
||||
style.position = 'absolute';
|
||||
style.left = '-1000px';
|
||||
style.top = '-1000px';
|
||||
style.width = width + 'px';
|
||||
style.height = height + 'px';
|
||||
document.body.appendChild(target);
|
||||
|
||||
const geometry = new Polygon([
|
||||
[
|
||||
[0, 0],
|
||||
[0, 40],
|
||||
[40, 40],
|
||||
[40, 0],
|
||||
],
|
||||
]);
|
||||
|
||||
// Four overlapping features, two features of type "foo" and two features
|
||||
// of type "bar". The rendering order is, from top to bottom, foo -> bar
|
||||
// -> foo -> bar.
|
||||
const features = [];
|
||||
features.push(
|
||||
new Feature({
|
||||
geometry: geometry,
|
||||
type: 'bar',
|
||||
}),
|
||||
new Feature({
|
||||
geometry: geometry,
|
||||
type: 'foo',
|
||||
}),
|
||||
new Feature({
|
||||
geometry: geometry,
|
||||
type: 'bar',
|
||||
}),
|
||||
new Feature({
|
||||
geometry: geometry,
|
||||
type: 'foo',
|
||||
})
|
||||
);
|
||||
|
||||
source = new VectorSource({
|
||||
features: features,
|
||||
});
|
||||
|
||||
layer = new VectorLayer({source: source});
|
||||
|
||||
map = new Map({
|
||||
target: target,
|
||||
layers: [layer],
|
||||
view: new View({
|
||||
projection: 'EPSG:4326',
|
||||
center: [0, 0],
|
||||
resolution: 1,
|
||||
}),
|
||||
});
|
||||
|
||||
map.once('postrender', function () {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
map.dispose();
|
||||
document.body.removeChild(target);
|
||||
});
|
||||
|
||||
/**
|
||||
* Simulates a browser event on the map viewport. The client x/y location
|
||||
* will be adjusted as if the map were centered at 0,0.
|
||||
* @param {string} type Event type.
|
||||
* @param {number} x Horizontal offset from map center.
|
||||
* @param {number} y Vertical offset from map center.
|
||||
* @param {boolean} [opt_shiftKey] Shift key is pressed.
|
||||
*/
|
||||
function simulateEvent(type, x, y, opt_shiftKey) {
|
||||
const viewport = map.getViewport();
|
||||
// calculated in case body has top < 0 (test runner with small window)
|
||||
const position = viewport.getBoundingClientRect();
|
||||
const shiftKey = opt_shiftKey !== undefined ? opt_shiftKey : false;
|
||||
const event = {
|
||||
type: type,
|
||||
target: viewport.firstChild,
|
||||
clientX: position.left + x + width / 2,
|
||||
clientY: position.top + y + height / 2,
|
||||
shiftKey: shiftKey,
|
||||
stopPropagation: () => {
|
||||
event.propagationStopped = true;
|
||||
},
|
||||
};
|
||||
map.handleMapBrowserEvent(new MapBrowserEvent(type, map, event));
|
||||
}
|
||||
|
||||
describe('constructor', function () {
|
||||
it('creates a new interaction', function () {
|
||||
const select = new Select();
|
||||
expect(select).to.be.a(Select);
|
||||
expect(select).to.be.a(Interaction);
|
||||
});
|
||||
|
||||
describe('user-provided collection', function () {
|
||||
it('uses the user-provided collection', function () {
|
||||
const features = new Collection();
|
||||
const select = new Select({features: features});
|
||||
expect(select.getFeatures()).to.be(features);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('selecting a polygon', function () {
|
||||
let select;
|
||||
|
||||
beforeEach(function () {
|
||||
select = new Select();
|
||||
map.addInteraction(select);
|
||||
});
|
||||
|
||||
it('select with single-click', function () {
|
||||
const listenerSpy = sinon.spy(function (e) {
|
||||
expect(e.selected).to.have.length(1);
|
||||
});
|
||||
select.on('select', listenerSpy);
|
||||
|
||||
simulateEvent('singleclick', 10, -20);
|
||||
|
||||
expect(listenerSpy.callCount).to.be(1);
|
||||
|
||||
const features = select.getFeatures();
|
||||
expect(features.getLength()).to.equal(1);
|
||||
});
|
||||
|
||||
it('single-click outside the geometry', function () {
|
||||
const listenerSpy = sinon.spy(function (e) {
|
||||
expect(e.selected).to.have.length(1);
|
||||
});
|
||||
select.on('select', listenerSpy);
|
||||
|
||||
simulateEvent(MapBrowserEventType.SINGLECLICK, -10, -10);
|
||||
|
||||
expect(listenerSpy.callCount).to.be(0);
|
||||
|
||||
const features = select.getFeatures();
|
||||
expect(features.getLength()).to.equal(0);
|
||||
});
|
||||
|
||||
it('select twice with single-click', function () {
|
||||
const listenerSpy = sinon.spy(function (e) {
|
||||
expect(e.selected).to.have.length(1);
|
||||
});
|
||||
select.on('select', listenerSpy);
|
||||
|
||||
simulateEvent(MapBrowserEventType.SINGLECLICK, 10, -20);
|
||||
simulateEvent(MapBrowserEventType.SINGLECLICK, 9, -21);
|
||||
|
||||
expect(listenerSpy.callCount).to.be(1);
|
||||
|
||||
const features = select.getFeatures();
|
||||
expect(features.getLength()).to.equal(1);
|
||||
});
|
||||
|
||||
it('select with shift single-click', function () {
|
||||
const listenerSpy = sinon.spy(function (e) {
|
||||
expect(e.selected).to.have.length(1);
|
||||
});
|
||||
select.on('select', listenerSpy);
|
||||
|
||||
simulateEvent('singleclick', 10, -20, true);
|
||||
|
||||
expect(listenerSpy.callCount).to.be(1);
|
||||
|
||||
const features = select.getFeatures();
|
||||
expect(features.getLength()).to.equal(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('multiselecting polygons', function () {
|
||||
let select;
|
||||
|
||||
beforeEach(function () {
|
||||
select = new Select({
|
||||
multi: true,
|
||||
});
|
||||
map.addInteraction(select);
|
||||
});
|
||||
|
||||
it('select with single-click', function () {
|
||||
const listenerSpy = sinon.spy(function (e) {
|
||||
expect(e.selected).to.have.length(4);
|
||||
});
|
||||
select.on('select', listenerSpy);
|
||||
|
||||
simulateEvent('singleclick', 10, -20);
|
||||
|
||||
expect(listenerSpy.callCount).to.be(1);
|
||||
|
||||
const features = select.getFeatures();
|
||||
expect(features.getLength()).to.equal(4);
|
||||
});
|
||||
|
||||
it('select with shift single-click', function () {
|
||||
const listenerSpy = sinon.spy(function (e) {
|
||||
expect(e.selected).to.have.length(4);
|
||||
});
|
||||
select.on('select', listenerSpy);
|
||||
|
||||
simulateEvent('singleclick', 10, -20, true);
|
||||
|
||||
expect(listenerSpy.callCount).to.be(1);
|
||||
|
||||
let features = select.getFeatures();
|
||||
expect(features.getLength()).to.equal(4);
|
||||
expect(select.getLayer(features.item(0))).to.equal(layer);
|
||||
|
||||
// Select again to make sure the style change does not break selection
|
||||
simulateEvent('singleclick', 10, -20);
|
||||
|
||||
expect(listenerSpy.callCount).to.be(1);
|
||||
|
||||
features = select.getFeatures();
|
||||
expect(features.getLength()).to.equal(4);
|
||||
expect(select.getLayer(features.item(0))).to.equal(layer);
|
||||
});
|
||||
});
|
||||
|
||||
describe('toggle selecting polygons', function () {
|
||||
let select;
|
||||
|
||||
beforeEach(function () {
|
||||
select = new Select({
|
||||
multi: true,
|
||||
});
|
||||
map.addInteraction(select);
|
||||
});
|
||||
|
||||
it('with SHIFT + single-click', function () {
|
||||
const listenerSpy = sinon.spy();
|
||||
select.on('select', listenerSpy);
|
||||
|
||||
simulateEvent('singleclick', 10, -20, true);
|
||||
|
||||
expect(listenerSpy.callCount).to.be(1);
|
||||
|
||||
let features = select.getFeatures();
|
||||
expect(features.getLength()).to.equal(4);
|
||||
|
||||
map.renderSync();
|
||||
|
||||
simulateEvent('singleclick', 10, -20, true);
|
||||
|
||||
expect(listenerSpy.callCount).to.be(1);
|
||||
|
||||
features = select.getFeatures();
|
||||
expect(features.getLength()).to.equal(4);
|
||||
});
|
||||
});
|
||||
|
||||
describe('filter features using the filter option', function () {
|
||||
describe('with multi set to true', function () {
|
||||
it('only selects features that pass the filter', function () {
|
||||
const select = new Select({
|
||||
multi: true,
|
||||
filter: function (feature, layer) {
|
||||
return feature.get('type') === 'bar';
|
||||
},
|
||||
});
|
||||
map.addInteraction(select);
|
||||
|
||||
simulateEvent('singleclick', 10, -20);
|
||||
const features = select.getFeatures();
|
||||
expect(features.getLength()).to.equal(2);
|
||||
expect(features.item(0).get('type')).to.be('bar');
|
||||
expect(features.item(1).get('type')).to.be('bar');
|
||||
});
|
||||
|
||||
it(
|
||||
'only selects features that pass the filter ' +
|
||||
'using shift single-click',
|
||||
function () {
|
||||
const select = new Select({
|
||||
multi: true,
|
||||
filter: function (feature, layer) {
|
||||
return feature.get('type') === 'bar';
|
||||
},
|
||||
});
|
||||
map.addInteraction(select);
|
||||
|
||||
simulateEvent('singleclick', 10, -20, true);
|
||||
const features = select.getFeatures();
|
||||
expect(features.getLength()).to.equal(2);
|
||||
expect(features.item(0).get('type')).to.be('bar');
|
||||
expect(features.item(1).get('type')).to.be('bar');
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
describe('with multi set to false', function () {
|
||||
it('only selects the first feature that passes the filter', function () {
|
||||
const select = new Select({
|
||||
multi: false,
|
||||
filter: function (feature, layer) {
|
||||
return feature.get('type') === 'bar';
|
||||
},
|
||||
});
|
||||
map.addInteraction(select);
|
||||
simulateEvent('singleclick', 10, -20);
|
||||
const features = select.getFeatures();
|
||||
expect(features.getLength()).to.equal(1);
|
||||
expect(features.item(0).get('type')).to.be('bar');
|
||||
});
|
||||
|
||||
it(
|
||||
'only selects the first feature that passes the filter ' +
|
||||
'using shift single-click',
|
||||
function () {
|
||||
const select = new Select({
|
||||
multi: false,
|
||||
filter: function (feature, layer) {
|
||||
return feature.get('type') === 'bar';
|
||||
},
|
||||
});
|
||||
map.addInteraction(select);
|
||||
simulateEvent('singleclick', 10, -20, true);
|
||||
const features = select.getFeatures();
|
||||
expect(features.getLength()).to.equal(1);
|
||||
expect(features.item(0).get('type')).to.be('bar');
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getLayer(feature)', function () {
|
||||
let interaction;
|
||||
|
||||
beforeEach(function () {
|
||||
interaction = new Select();
|
||||
map.addInteraction(interaction);
|
||||
});
|
||||
afterEach(function () {
|
||||
map.removeInteraction(interaction);
|
||||
});
|
||||
|
||||
it('returns a layer from a selected feature', function () {
|
||||
const listenerSpy = sinon.spy(function (e) {
|
||||
const feature = e.selected[0];
|
||||
const layer_ = interaction.getLayer(feature);
|
||||
expect(e.selected).to.have.length(1);
|
||||
expect(feature).to.be.a(Feature);
|
||||
expect(layer_).to.be.a(VectorLayer);
|
||||
expect(layer_).to.equal(layer);
|
||||
});
|
||||
interaction.on('select', listenerSpy);
|
||||
|
||||
simulateEvent('singleclick', 10, -20);
|
||||
// Select again to make sure the style change does not break selection
|
||||
simulateEvent('singleclick', 10, -20);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#setActive()', function () {
|
||||
let interaction;
|
||||
|
||||
beforeEach(function () {
|
||||
interaction = new Select();
|
||||
|
||||
expect(interaction.getActive()).to.be(true);
|
||||
|
||||
map.addInteraction(interaction);
|
||||
|
||||
simulateEvent('singleclick', 10, -20);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
map.removeInteraction(interaction);
|
||||
});
|
||||
|
||||
describe('#setActive(false)', function () {
|
||||
it('keeps the the selection', function () {
|
||||
interaction.setActive(false);
|
||||
expect(interaction.getFeatures().getLength()).to.equal(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#setActive(true)', function () {
|
||||
beforeEach(function () {
|
||||
interaction.setActive(false);
|
||||
});
|
||||
it('fires change:active', function () {
|
||||
const listenerSpy = sinon.spy();
|
||||
interaction.on('change:active', listenerSpy);
|
||||
interaction.setActive(true);
|
||||
expect(listenerSpy.callCount).to.be(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('clear event listeners on interaction removal', function () {
|
||||
let firstInteraction, secondInteraction, feature;
|
||||
|
||||
beforeEach(function () {
|
||||
feature = source.getFeatures()[3]; // top feature is selected
|
||||
|
||||
const style = new Style({});
|
||||
const features = new Collection();
|
||||
|
||||
firstInteraction = new Select({style, features});
|
||||
secondInteraction = new Select({style, features});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
map.removeInteraction(secondInteraction);
|
||||
map.removeInteraction(firstInteraction);
|
||||
});
|
||||
|
||||
// The base case
|
||||
describe('with a single interaction added', function () {
|
||||
it('changes the selected feature once', function () {
|
||||
map.addInteraction(firstInteraction);
|
||||
|
||||
const listenerSpy = sinon.spy();
|
||||
feature.on('change', listenerSpy);
|
||||
|
||||
simulateEvent('singleclick', 10, -20, false);
|
||||
|
||||
expect(listenerSpy.callCount).to.be(1);
|
||||
});
|
||||
});
|
||||
|
||||
// The "difficult" case. To prevent regression
|
||||
describe('with a replaced interaction', function () {
|
||||
it('changes the selected feature once', function () {
|
||||
map.addInteraction(firstInteraction);
|
||||
map.removeInteraction(firstInteraction);
|
||||
map.addInteraction(secondInteraction);
|
||||
|
||||
const listenerSpy = sinon.spy();
|
||||
feature.on('change', listenerSpy);
|
||||
|
||||
simulateEvent('singleclick', 10, -20, false);
|
||||
|
||||
expect(listenerSpy.callCount).to.be(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('supports stop propagation', function () {
|
||||
let firstInteraction, secondInteraction;
|
||||
|
||||
beforeEach(function () {
|
||||
firstInteraction = new Select();
|
||||
secondInteraction = new Select();
|
||||
|
||||
map.addInteraction(firstInteraction);
|
||||
// note second interaction added to map last
|
||||
map.addInteraction(secondInteraction);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
map.removeInteraction(firstInteraction);
|
||||
map.removeInteraction(secondInteraction);
|
||||
});
|
||||
|
||||
//base case sanity check
|
||||
describe('without stop propagation', function () {
|
||||
it('both interactions dispatch select', function () {
|
||||
const firstSelectSpy = sinon.spy();
|
||||
firstInteraction.on('select', firstSelectSpy);
|
||||
|
||||
const secondSelectSpy = sinon.spy();
|
||||
secondInteraction.on('select', secondSelectSpy);
|
||||
|
||||
simulateEvent('singleclick', 10, -20);
|
||||
|
||||
expect(firstSelectSpy.callCount).to.be(1);
|
||||
expect(secondSelectSpy.callCount).to.be(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('calling stop propagation', function () {
|
||||
it('only "last" added interaction dispatches select', function () {
|
||||
const firstSelectSpy = sinon.spy();
|
||||
firstInteraction.on('select', firstSelectSpy);
|
||||
|
||||
const secondSelectSpy = sinon.spy(function (e) {
|
||||
e.mapBrowserEvent.stopPropagation();
|
||||
});
|
||||
secondInteraction.on('select', secondSelectSpy);
|
||||
|
||||
simulateEvent('singleclick', 10, -20);
|
||||
|
||||
expect(firstSelectSpy.callCount).to.be(0);
|
||||
expect(secondSelectSpy.callCount).to.be(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,336 @@
|
||||
import Circle from '../../../../../src/ol/geom/Circle.js';
|
||||
import Collection from '../../../../../src/ol/Collection.js';
|
||||
import Feature from '../../../../../src/ol/Feature.js';
|
||||
import LineString from '../../../../../src/ol/geom/LineString.js';
|
||||
import Map from '../../../../../src/ol/Map.js';
|
||||
import Point from '../../../../../src/ol/geom/Point.js';
|
||||
import Snap from '../../../../../src/ol/interaction/Snap.js';
|
||||
import View from '../../../../../src/ol/View.js';
|
||||
import {
|
||||
clearUserProjection,
|
||||
setUserProjection,
|
||||
transform,
|
||||
useGeographic,
|
||||
} from '../../../../../src/ol/proj.js';
|
||||
import {overrideRAF} from '../../util.js';
|
||||
|
||||
describe('ol.interaction.Snap', function () {
|
||||
describe('constructor', function () {
|
||||
it('can be constructed without arguments', function () {
|
||||
const instance = new Snap();
|
||||
expect(instance).to.be.an(Snap);
|
||||
});
|
||||
});
|
||||
|
||||
describe('handleEvent', function () {
|
||||
let target, map;
|
||||
|
||||
const width = 360;
|
||||
const height = 180;
|
||||
|
||||
beforeEach(function (done) {
|
||||
target = document.createElement('div');
|
||||
|
||||
const style = target.style;
|
||||
style.position = 'absolute';
|
||||
style.left = '-1000px';
|
||||
style.top = '-1000px';
|
||||
style.width = width + 'px';
|
||||
style.height = height + 'px';
|
||||
document.body.appendChild(target);
|
||||
|
||||
map = new Map({
|
||||
target: target,
|
||||
view: new View({
|
||||
projection: 'EPSG:4326',
|
||||
center: [0, 0],
|
||||
resolution: 1,
|
||||
}),
|
||||
});
|
||||
|
||||
map.once('postrender', function () {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
map.dispose();
|
||||
document.body.removeChild(target);
|
||||
clearUserProjection();
|
||||
});
|
||||
|
||||
it('can handle XYZ coordinates', function () {
|
||||
const point = new Feature(new Point([0, 0, 123]));
|
||||
const snapInteraction = new Snap({
|
||||
features: new Collection([point]),
|
||||
});
|
||||
snapInteraction.setMap(map);
|
||||
|
||||
const event = {
|
||||
pixel: [width / 2, height / 2],
|
||||
coordinate: [0, 0],
|
||||
map: map,
|
||||
};
|
||||
snapInteraction.handleEvent(event);
|
||||
// check that the coordinate is in XY and not XYZ
|
||||
expect(event.coordinate).to.eql([0, 0]);
|
||||
});
|
||||
|
||||
it('snaps to edges only', function () {
|
||||
const point = new Feature(
|
||||
new LineString([
|
||||
[-10, 0],
|
||||
[10, 0],
|
||||
])
|
||||
);
|
||||
const snapInteraction = new Snap({
|
||||
features: new Collection([point]),
|
||||
pixelTolerance: 5,
|
||||
vertex: false,
|
||||
});
|
||||
snapInteraction.setMap(map);
|
||||
|
||||
const event = {
|
||||
pixel: [7 + width / 2, height / 2 - 4],
|
||||
coordinate: [7, 4],
|
||||
map: map,
|
||||
};
|
||||
snapInteraction.handleEvent(event);
|
||||
expect(event.coordinate).to.eql([7, 0]);
|
||||
});
|
||||
|
||||
it('snaps to vertices only', function () {
|
||||
const point = new Feature(
|
||||
new LineString([
|
||||
[-10, 0],
|
||||
[10, 0],
|
||||
])
|
||||
);
|
||||
const snapInteraction = new Snap({
|
||||
features: new Collection([point]),
|
||||
pixelTolerance: 5,
|
||||
edge: false,
|
||||
});
|
||||
snapInteraction.setMap(map);
|
||||
|
||||
const event = {
|
||||
pixel: [7 + width / 2, height / 2 - 4],
|
||||
coordinate: [7, 4],
|
||||
map: map,
|
||||
};
|
||||
snapInteraction.handleEvent(event);
|
||||
expect(event.coordinate).to.eql([10, 0]);
|
||||
});
|
||||
|
||||
it('snaps to circle', function () {
|
||||
const circle = new Feature(new Circle([0, 0], 10));
|
||||
const snapInteraction = new Snap({
|
||||
features: new Collection([circle]),
|
||||
pixelTolerance: 5,
|
||||
});
|
||||
snapInteraction.setMap(map);
|
||||
|
||||
const event = {
|
||||
pixel: [5 + width / 2, height / 2 - 5],
|
||||
coordinate: [5, 5],
|
||||
map: map,
|
||||
};
|
||||
snapInteraction.handleEvent(event);
|
||||
|
||||
expect(event.coordinate[0]).to.roughlyEqual(
|
||||
Math.sin(Math.PI / 4) * 10,
|
||||
1e-10
|
||||
);
|
||||
expect(event.coordinate[1]).to.roughlyEqual(
|
||||
Math.sin(Math.PI / 4) * 10,
|
||||
1e-10
|
||||
);
|
||||
});
|
||||
|
||||
it('snaps to circle in a user projection', function () {
|
||||
const userProjection = 'EPSG:3857';
|
||||
setUserProjection(userProjection);
|
||||
const viewProjection = map.getView().getProjection();
|
||||
|
||||
const circle = new Feature(
|
||||
new Circle([0, 0], 10).transform(viewProjection, userProjection)
|
||||
);
|
||||
const snapInteraction = new Snap({
|
||||
features: new Collection([circle]),
|
||||
pixelTolerance: 5,
|
||||
});
|
||||
snapInteraction.setMap(map);
|
||||
|
||||
const event = {
|
||||
pixel: [5 + width / 2, height / 2 - 5],
|
||||
coordinate: transform([5, 5], viewProjection, userProjection),
|
||||
map: map,
|
||||
};
|
||||
snapInteraction.handleEvent(event);
|
||||
|
||||
const coordinate = transform(
|
||||
[Math.sin(Math.PI / 4) * 10, Math.sin(Math.PI / 4) * 10],
|
||||
viewProjection,
|
||||
userProjection
|
||||
);
|
||||
expect(event.coordinate[0]).to.roughlyEqual(coordinate[0], 1e-10);
|
||||
expect(event.coordinate[1]).to.roughlyEqual(coordinate[1], 1e-10);
|
||||
});
|
||||
|
||||
it('handle feature without geometry', function () {
|
||||
const feature = new Feature();
|
||||
const snapInteraction = new Snap({
|
||||
features: new Collection([feature]),
|
||||
pixelTolerance: 5,
|
||||
edge: false,
|
||||
});
|
||||
snapInteraction.setMap(map);
|
||||
|
||||
feature.setGeometry(
|
||||
new LineString([
|
||||
[-10, 0],
|
||||
[10, 0],
|
||||
])
|
||||
);
|
||||
|
||||
const event = {
|
||||
pixel: [7 + width / 2, height / 2 - 4],
|
||||
coordinate: [7, 4],
|
||||
map: map,
|
||||
};
|
||||
snapInteraction.handleEvent(event);
|
||||
expect(event.coordinate).to.eql([10, 0]);
|
||||
});
|
||||
|
||||
it('handle geometry changes', function () {
|
||||
const line = new Feature(
|
||||
new LineString([
|
||||
[-10, 0],
|
||||
[0, 0],
|
||||
])
|
||||
);
|
||||
const snapInteraction = new Snap({
|
||||
features: new Collection([line]),
|
||||
pixelTolerance: 5,
|
||||
edge: false,
|
||||
});
|
||||
snapInteraction.setMap(map);
|
||||
|
||||
line.getGeometry().setCoordinates([
|
||||
[-10, 0],
|
||||
[10, 0],
|
||||
]);
|
||||
|
||||
const event = {
|
||||
pixel: [7 + width / 2, height / 2 - 4],
|
||||
coordinate: [7, 4],
|
||||
map: map,
|
||||
};
|
||||
snapInteraction.handleEvent(event);
|
||||
expect(event.coordinate).to.eql([10, 0]);
|
||||
});
|
||||
|
||||
it('handle geometry name changes', function () {
|
||||
const line = new Feature({
|
||||
geometry: new LineString([
|
||||
[-10, 0],
|
||||
[0, 0],
|
||||
]),
|
||||
alt_geometry: new LineString([
|
||||
[-10, 0],
|
||||
[10, 0],
|
||||
]),
|
||||
});
|
||||
const snapInteraction = new Snap({
|
||||
features: new Collection([line]),
|
||||
pixelTolerance: 5,
|
||||
edge: false,
|
||||
});
|
||||
snapInteraction.setMap(map);
|
||||
|
||||
line.setGeometryName('alt_geometry');
|
||||
|
||||
const event = {
|
||||
pixel: [7 + width / 2, height / 2 - 4],
|
||||
coordinate: [7, 4],
|
||||
map: map,
|
||||
};
|
||||
snapInteraction.handleEvent(event);
|
||||
expect(event.coordinate).to.eql([10, 0]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('handleEvent - useGeographic', () => {
|
||||
let target, map;
|
||||
const size = 256;
|
||||
|
||||
let restoreRAF;
|
||||
|
||||
beforeEach((done) => {
|
||||
restoreRAF = overrideRAF();
|
||||
|
||||
useGeographic();
|
||||
target = document.createElement('div');
|
||||
|
||||
Object.assign(target.style, {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: `${size}px`,
|
||||
height: `${size}px`,
|
||||
});
|
||||
document.body.appendChild(target);
|
||||
|
||||
map = new Map({
|
||||
target: target,
|
||||
view: new View({
|
||||
center: [0, 0],
|
||||
zoom: 0,
|
||||
}),
|
||||
});
|
||||
|
||||
map.once('postrender', () => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
map.dispose();
|
||||
document.body.removeChild(target);
|
||||
clearUserProjection();
|
||||
restoreRAF();
|
||||
});
|
||||
|
||||
it('snaps to user coordinates', () => {
|
||||
const lon = -90;
|
||||
const lat = 45;
|
||||
const point = new Feature(new Point([lon, lat]));
|
||||
|
||||
const snap = new Snap({
|
||||
features: new Collection([point]),
|
||||
});
|
||||
snap.setMap(map);
|
||||
|
||||
const expectedPixel = map
|
||||
.getPixelFromCoordinate([lon, lat])
|
||||
.map((value) => Math.round(value));
|
||||
|
||||
const delta = 5;
|
||||
const pixel = expectedPixel.slice();
|
||||
pixel[0] += delta;
|
||||
pixel[1] += delta;
|
||||
|
||||
const coordinate = map.getCoordinateFromPixel(pixel);
|
||||
|
||||
const event = {
|
||||
pixel: pixel,
|
||||
coordinate: coordinate,
|
||||
map: map,
|
||||
};
|
||||
snap.handleEvent(event);
|
||||
|
||||
expect(event.coordinate).to.eql([lon, lat]);
|
||||
expect(event.pixel).to.eql(expectedPixel);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,327 @@
|
||||
import Collection from '../../../../../src/ol/Collection.js';
|
||||
import Feature from '../../../../../src/ol/Feature.js';
|
||||
import Interaction from '../../../../../src/ol/interaction/Interaction.js';
|
||||
import Map from '../../../../../src/ol/Map.js';
|
||||
import MapBrowserEvent from '../../../../../src/ol/MapBrowserEvent.js';
|
||||
import Point from '../../../../../src/ol/geom/Point.js';
|
||||
import Translate, {
|
||||
TranslateEvent,
|
||||
} from '../../../../../src/ol/interaction/Translate.js';
|
||||
import VectorLayer from '../../../../../src/ol/layer/Vector.js';
|
||||
import VectorSource from '../../../../../src/ol/source/Vector.js';
|
||||
import View from '../../../../../src/ol/View.js';
|
||||
|
||||
describe('ol.interaction.Translate', function () {
|
||||
let target, map, source, features;
|
||||
|
||||
const width = 360;
|
||||
const height = 180;
|
||||
|
||||
beforeEach(function (done) {
|
||||
target = document.createElement('div');
|
||||
const style = target.style;
|
||||
style.position = 'absolute';
|
||||
style.left = '-1000px';
|
||||
style.top = '-1000px';
|
||||
style.width = width + 'px';
|
||||
style.height = height + 'px';
|
||||
document.body.appendChild(target);
|
||||
source = new VectorSource();
|
||||
features = [
|
||||
new Feature({
|
||||
geometry: new Point([10, -20]),
|
||||
}),
|
||||
new Feature({
|
||||
geometry: new Point([20, -30]),
|
||||
}),
|
||||
];
|
||||
source.addFeatures(features);
|
||||
const layer = new VectorLayer({source: source});
|
||||
map = new Map({
|
||||
target: target,
|
||||
layers: [layer],
|
||||
view: new View({
|
||||
projection: 'EPSG:4326',
|
||||
center: [0, 0],
|
||||
resolution: 1,
|
||||
}),
|
||||
});
|
||||
map.once('postrender', function () {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
map.dispose();
|
||||
document.body.removeChild(target);
|
||||
});
|
||||
|
||||
/**
|
||||
* Simulates a browser event on the map viewport. The client x/y location
|
||||
* will be adjusted as if the map were centered at 0,0.
|
||||
* @param {string} type Event type.
|
||||
* @param {number} x Horizontal offset from map center.
|
||||
* @param {number} y Vertical offset from map center.
|
||||
* @param {boolean} [opt_shiftKey] Shift key is pressed.
|
||||
*/
|
||||
function simulateEvent(type, x, y, opt_shiftKey) {
|
||||
const viewport = map.getViewport();
|
||||
// calculated in case body has top < 0 (test runner with small window)
|
||||
const position = viewport.getBoundingClientRect();
|
||||
const shiftKey = opt_shiftKey !== undefined ? opt_shiftKey : false;
|
||||
const event = new MapBrowserEvent(type, map, {
|
||||
type: type,
|
||||
target: viewport.firstChild,
|
||||
pointerId: 0,
|
||||
clientX: position.left + x + width / 2,
|
||||
clientY: position.top + y + height / 2,
|
||||
shiftKey: shiftKey,
|
||||
preventDefault: function () {},
|
||||
});
|
||||
map.handleMapBrowserEvent(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tracks events triggered by the interaction as well as feature
|
||||
* modifications. Helper function to
|
||||
* @param {ol.Feature} feature Translated feature.
|
||||
* @param {ol.interaction.Translate} interaction The interaction.
|
||||
* @return {Array<TranslateEvent|string>} events
|
||||
*/
|
||||
function trackEvents(feature, interaction) {
|
||||
const events = [];
|
||||
feature.on('change', function (event) {
|
||||
events.push('change');
|
||||
});
|
||||
interaction.on('translatestart', function (event) {
|
||||
events.push(event);
|
||||
});
|
||||
interaction.on('translateend', function (event) {
|
||||
events.push(event);
|
||||
});
|
||||
return events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the event array to verify proper event sequence. Checks
|
||||
* that first and last event are correct TranslateEvents and that feature
|
||||
* modifications event are in between.
|
||||
* @param {Array<TranslateEvent|string>} events The events.
|
||||
* @param {Array<ol.Feature>} features The features.
|
||||
*/
|
||||
function validateEvents(events, features) {
|
||||
const startevent = events[0];
|
||||
const endevent = events[events.length - 1];
|
||||
|
||||
// first event should be translatestart
|
||||
expect(startevent).to.be.an(TranslateEvent);
|
||||
expect(startevent.type).to.eql('translatestart');
|
||||
|
||||
// last event should be translateend
|
||||
expect(endevent).to.be.an(TranslateEvent);
|
||||
expect(endevent.type).to.eql('translateend');
|
||||
|
||||
// make sure we get change events to events array
|
||||
expect(events.length > 2).to.be(true);
|
||||
// middle events should be feature modification events
|
||||
for (let i = 1; i < events.length - 1; i++) {
|
||||
expect(events[i]).to.equal('change');
|
||||
}
|
||||
|
||||
// TranslateEvents should include the expected features
|
||||
expect(startevent.features.getArray()).to.eql(features);
|
||||
expect(endevent.features.getArray()).to.eql(features);
|
||||
}
|
||||
|
||||
describe('constructor', function () {
|
||||
it('creates a new interaction', function () {
|
||||
const translate = new Translate({
|
||||
features: features,
|
||||
});
|
||||
expect(translate).to.be.a(Translate);
|
||||
expect(translate).to.be.a(Interaction);
|
||||
});
|
||||
});
|
||||
|
||||
describe('setActive', function () {
|
||||
it('works when the map is not set', function () {
|
||||
const translate = new Translate({
|
||||
features: features,
|
||||
});
|
||||
expect(translate.getActive()).to.be(true);
|
||||
translate.setActive(false);
|
||||
expect(translate.getActive()).to.be(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('moving features, with features option', function () {
|
||||
let translate;
|
||||
|
||||
beforeEach(function () {
|
||||
translate = new Translate({
|
||||
features: new Collection([features[0]]),
|
||||
});
|
||||
map.addInteraction(translate);
|
||||
});
|
||||
|
||||
it('moves a selected feature', function () {
|
||||
const events = trackEvents(features[0], translate);
|
||||
|
||||
simulateEvent('pointermove', 10, 20);
|
||||
simulateEvent('pointerdown', 10, 20);
|
||||
simulateEvent('pointerdrag', 50, -40);
|
||||
simulateEvent('pointerup', 50, -40);
|
||||
const geometry = features[0].getGeometry();
|
||||
expect(geometry).to.be.a(Point);
|
||||
expect(geometry.getCoordinates()).to.eql([50, 40]);
|
||||
|
||||
validateEvents(events, [features[0]]);
|
||||
});
|
||||
|
||||
it('does not move an unselected feature', function () {
|
||||
const events = trackEvents(features[0], translate);
|
||||
|
||||
simulateEvent('pointermove', 20, 30);
|
||||
simulateEvent('pointerdown', 20, 30);
|
||||
simulateEvent('pointerdrag', 50, -40);
|
||||
simulateEvent('pointerup', 50, -40);
|
||||
const geometry = features[1].getGeometry();
|
||||
expect(geometry).to.be.a(Point);
|
||||
expect(geometry.getCoordinates()).to.eql([20, -30]);
|
||||
|
||||
expect(events).to.be.empty();
|
||||
});
|
||||
});
|
||||
|
||||
describe('moving features, without features option', function () {
|
||||
let translate;
|
||||
|
||||
beforeEach(function () {
|
||||
translate = new Translate();
|
||||
map.addInteraction(translate);
|
||||
});
|
||||
|
||||
it('moves only targeted feature', function () {
|
||||
const events = trackEvents(features[0], translate);
|
||||
|
||||
simulateEvent('pointermove', 10, 20);
|
||||
simulateEvent('pointerdown', 10, 20);
|
||||
simulateEvent('pointerdrag', 50, -40);
|
||||
simulateEvent('pointerup', 50, -40);
|
||||
expect(features[0].getGeometry().getCoordinates()).to.eql([50, 40]);
|
||||
expect(features[1].getGeometry().getCoordinates()).to.eql([20, -30]);
|
||||
|
||||
validateEvents(events, [features[0]]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('moving features, with filter option', function () {
|
||||
let translate;
|
||||
|
||||
beforeEach(function () {
|
||||
translate = new Translate({
|
||||
filter: function (feature, layer) {
|
||||
return feature == features[0];
|
||||
},
|
||||
});
|
||||
map.addInteraction(translate);
|
||||
});
|
||||
|
||||
it('moves a filter-passing feature', function () {
|
||||
const events = trackEvents(features[0], translate);
|
||||
|
||||
simulateEvent('pointermove', 10, 20);
|
||||
simulateEvent('pointerdown', 10, 20);
|
||||
simulateEvent('pointerdrag', 50, -40);
|
||||
simulateEvent('pointerup', 50, -40);
|
||||
const geometry = features[0].getGeometry();
|
||||
expect(geometry).to.be.a(Point);
|
||||
expect(geometry.getCoordinates()).to.eql([50, 40]);
|
||||
|
||||
validateEvents(events, [features[0]]);
|
||||
});
|
||||
|
||||
it('does not move a filter-discarded feature', function () {
|
||||
const events = trackEvents(features[0], translate);
|
||||
|
||||
simulateEvent('pointermove', 20, 30);
|
||||
simulateEvent('pointerdown', 20, 30);
|
||||
simulateEvent('pointerdrag', 50, -40);
|
||||
simulateEvent('pointerup', 50, -40);
|
||||
const geometry = features[1].getGeometry();
|
||||
expect(geometry).to.be.a(Point);
|
||||
expect(geometry.getCoordinates()).to.eql([20, -30]);
|
||||
|
||||
expect(events).to.be.empty();
|
||||
});
|
||||
});
|
||||
|
||||
describe('changes css cursor', function () {
|
||||
let element, translate;
|
||||
|
||||
beforeEach(function () {
|
||||
translate = new Translate();
|
||||
map.addInteraction(translate);
|
||||
element = map.getViewport();
|
||||
});
|
||||
|
||||
it('changes css cursor', function () {
|
||||
expect(element.classList.contains('ol-grabbing')).to.be(false);
|
||||
expect(element.classList.contains('ol-grab')).to.be(false);
|
||||
|
||||
simulateEvent('pointermove', 10, 20);
|
||||
expect(element.classList.contains('ol-grabbing')).to.be(false);
|
||||
expect(element.classList.contains('ol-grab')).to.be(true);
|
||||
|
||||
simulateEvent('pointerdown', 10, 20);
|
||||
expect(element.classList.contains('ol-grabbing')).to.be(true);
|
||||
expect(element.classList.contains('ol-grab')).to.be(false);
|
||||
|
||||
simulateEvent('pointerup', 10, 20);
|
||||
expect(element.classList.contains('ol-grabbing')).to.be(false);
|
||||
expect(element.classList.contains('ol-grab')).to.be(true);
|
||||
|
||||
simulateEvent('pointermove', 0, 0);
|
||||
expect(element.classList.contains('ol-grabbing')).to.be(false);
|
||||
expect(element.classList.contains('ol-grab')).to.be(false);
|
||||
});
|
||||
|
||||
it('resets css cursor when interaction is deactivated while pointer is on feature', function () {
|
||||
simulateEvent('pointermove', 10, 20);
|
||||
expect(element.classList.contains('ol-grabbing')).to.be(false);
|
||||
expect(element.classList.contains('ol-grab')).to.be(true);
|
||||
|
||||
translate.setActive(false);
|
||||
|
||||
simulateEvent('pointermove', 0, 0);
|
||||
expect(element.classList.contains('ol-grabbing')).to.be(false);
|
||||
expect(element.classList.contains('ol-grab')).to.be(false);
|
||||
});
|
||||
|
||||
it('resets css cursor interaction is removed while pointer is on feature', function () {
|
||||
simulateEvent('pointermove', 10, 20);
|
||||
expect(element.classList.contains('ol-grabbing')).to.be(false);
|
||||
expect(element.classList.contains('ol-grab')).to.be(true);
|
||||
|
||||
map.removeInteraction(translate);
|
||||
|
||||
simulateEvent('pointermove', 0, 0);
|
||||
expect(element.classList.contains('ol-grabbing')).to.be(false);
|
||||
expect(element.classList.contains('ol-grab')).to.be(false);
|
||||
});
|
||||
|
||||
it('resets css cursor to existing cursor interaction is removed while pointer is on feature', function () {
|
||||
element.style.cursor = 'pointer';
|
||||
|
||||
simulateEvent('pointermove', 10, 20);
|
||||
expect(element.classList.contains('ol-grabbing')).to.be(false);
|
||||
expect(element.classList.contains('ol-grab')).to.be(true);
|
||||
|
||||
map.removeInteraction(translate);
|
||||
|
||||
simulateEvent('pointermove', 0, 0);
|
||||
expect(element.classList.contains('ol-grabbing')).to.be(false);
|
||||
expect(element.classList.contains('ol-grab')).to.be(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user