import Map from '../../../src/ol/Map.js'; import View, { createCenterConstraint, createResolutionConstraint, createRotationConstraint, isNoopAnimation } from '../../../src/ol/View.js'; import ViewHint from '../../../src/ol/ViewHint.js'; import {createEmpty} from '../../../src/ol/extent.js'; import Circle from '../../../src/ol/geom/Circle.js'; import LineString from '../../../src/ol/geom/LineString.js'; import Point from '../../../src/ol/geom/Point.js'; import {useGeographic, clearUserProjection} from '../../../src/ol/proj.js'; describe('ol.View', function() { describe('constructor (defaults)', function() { let view; beforeEach(function() { view = new View(); }); it('creates an instance', function() { expect(view).to.be.a(View); }); it('provides default rotation', function() { expect(view.getRotation()).to.be(0); }); }); describe('parameter initialization with resolution/zoom constraints', function() { it('correctly handles max resolution constraint', function() { const view = new View({ maxResolution: 1000, resolution: 1200 }); expect(view.getResolution()).to.eql(1000); expect(view.targetResolution_).to.eql(1000); }); it('correctly handles min resolution constraint', function() { const view = new View({ maxResolution: 1024, minResolution: 128, resolution: 50 }); expect(view.getResolution()).to.eql(128); expect(view.targetResolution_).to.eql(128); }); it('correctly handles resolutions array constraint', function() { let view = new View({ resolutions: [1024, 512, 256, 128, 64, 32], resolution: 1200 }); expect(view.getResolution()).to.eql(1024); expect(view.targetResolution_).to.eql(1024); view = new View({ resolutions: [1024, 512, 256, 128, 64, 32], resolution: 10 }); expect(view.getResolution()).to.eql(32); expect(view.targetResolution_).to.eql(32); }); it('correctly handles min zoom constraint', function() { const view = new View({ minZoom: 3, zoom: 2 }); expect(view.getZoom()).to.eql(3); expect(view.targetResolution_).to.eql(view.getMaxResolution()); }); it('correctly handles max zoom constraint', function() { const view = new View({ maxZoom: 4, zoom: 5 }); expect(view.getZoom()).to.eql(4); expect(view.targetResolution_).to.eql(view.getMaxResolution() / Math.pow(2, 4)); }); it('correctly handles extent constraint', function() { // default viewport size is 100x100 const view = new View({ extent: [0, 0, 50, 50], resolution: 1 }); expect(view.getResolution()).to.eql(0.5); expect(view.targetResolution_).to.eql(0.5); }); }); describe('create constraints', function() { describe('create center constraint', function() { describe('with no options', function() { it('gives a correct center constraint function', function() { const options = {}; const size = [512, 256]; const resolution = 1e5; const fn = createCenterConstraint(options); expect(fn([0, 0], resolution, size)).to.eql([0, 0]); expect(fn([42, -100], resolution, size)).to.eql([42, -100]); }); }); describe('panning off the edge of the world', function() { it('disallows going north off the world', function() { const options = { projection: 'EPSG:4326' }; const size = [360, 180]; const resolution = 0.5; const fn = createCenterConstraint(options); expect(fn([0, 0], resolution, size)).to.eql([0, 0]); expect(fn([0, 60], resolution, size)).to.eql([0, 45]); expect(fn([180, 60], resolution, size)).to.eql([180, 45]); expect(fn([-180, 60], resolution, size)).to.eql([-180, 45]); }); it('disallows going south off the world', function() { const options = { projection: 'EPSG:4326' }; const size = [360, 180]; const resolution = 0.5; const fn = createCenterConstraint(options); expect(fn([0, 0], resolution, size)).to.eql([0, 0]); expect(fn([0, -60], resolution, size)).to.eql([0, -45]); expect(fn([180, -60], resolution, size)).to.eql([180, -45]); expect(fn([-180, -60], resolution, size)).to.eql([-180, -45]); }); }); describe('with multiWorld: true', function() { it('gives a correct center constraint function', function() { const options = {multiWorld: true}; const size = [512, 256]; const resolution = 1e5; const fn = createCenterConstraint(options); expect(fn([0, 0], resolution, size)).to.eql([0, 0]); expect(fn([42, -100], resolution, size)).to.eql([42, -100]); }); }); describe('with extent option and center only', function() { it('gives a correct center constraint function', function() { const options = { extent: [0, 0, 1, 1], constrainOnlyCenter: true }; const fn = createCenterConstraint(options); expect(fn([0, 0])).to.eql([0, 0]); expect(fn([-10, 0])).to.eql([0, 0]); expect(fn([100, 100])).to.eql([1, 1]); }); }); describe('with extent option', function() { it('gives a correct center constraint function', function() { const options = { extent: [0, 0, 1, 1] }; const fn = createCenterConstraint(options); const res = 1; const size = [0.15, 0.1]; expect(fn([0, 0], res, size)).to.eql([0.075, 0.05]); expect(fn([0.5, 0.5], res, size)).to.eql([0.5, 0.5]); expect(fn([10, 10], res, size)).to.eql([0.925, 0.95]); const overshootCenter = fn([10, 10], res, size, true); expect(overshootCenter[0] > 0.925).to.eql(true); expect(overshootCenter[1] > 0.95).to.eql(true); expect(overshootCenter[0] < 9).to.eql(true); expect(overshootCenter[1] < 9).to.eql(true); }); }); }); describe('create resolution constraint', function() { describe('with no options', function() { const size = [200, 200]; it('gives a correct resolution constraint function', function() { const options = {}; const fn = createResolutionConstraint(options).constraint; expect(fn(156543.03392804097, 0, size)) .to.roughlyEqual(156543.03392804097, 1e-9); expect(fn(78271.51696402048, 0, size)) .to.roughlyEqual(78271.51696402048, 1e-10); }); }); describe('with maxResolution, maxZoom, and zoomFactor options', function() { const size = [200, 200]; it('gives a correct resolution constraint function', function() { const options = { maxResolution: 81, maxZoom: 3, zoomFactor: 3 }; const info = createResolutionConstraint(options); const maxResolution = info.maxResolution; expect(maxResolution).to.eql(81); const minResolution = info.minResolution; expect(minResolution).to.eql(3); const fn = info.constraint; expect(fn(82, 0, size)).to.eql(81); expect(fn(81, 0, size)).to.eql(81); expect(fn(27, 0, size)).to.eql(27); expect(fn(9, 0, size)).to.eql(9); expect(fn(3, 0, size)).to.eql(3); expect(fn(2, 0, size)).to.eql(3); }); }); describe('with resolutions', function() { const size = [200, 200]; it('gives a correct resolution constraint function', function() { const options = { resolutions: [97, 76, 65, 54, 0.45] }; const info = createResolutionConstraint(options); const maxResolution = info.maxResolution; expect(maxResolution).to.eql(97); const minResolution = info.minResolution; expect(minResolution).to.eql(0.45); const fn = info.constraint; expect(fn(97, 0, size)).to.eql(97); expect(fn(76, 0, size)).to.eql(76); expect(fn(65, 0, size)).to.eql(65); expect(fn(54, 0, size)).to.eql(54); expect(fn(0.45, 0, size)).to.eql(0.45); }); }); describe('with zoom related options', function() { const defaultMaxRes = 156543.03392804097; const size = [200, 200]; function getConstraint(options) { return createResolutionConstraint(options).constraint; } it('works with only maxZoom', function() { const maxZoom = 10; const constraint = getConstraint({ maxZoom: maxZoom }); expect(constraint(defaultMaxRes, 0, size)).to.roughlyEqual( defaultMaxRes, 1e-9); expect(constraint(0, 0, size)).to.roughlyEqual( defaultMaxRes / Math.pow(2, maxZoom), 1e-9); }); it('works with only minZoom', function() { const minZoom = 5; const constraint = getConstraint({ minZoom: minZoom }); expect(constraint(defaultMaxRes, 0, size)).to.roughlyEqual( defaultMaxRes / Math.pow(2, minZoom), 1e-9); expect(constraint(0, 0, size)).to.roughlyEqual( defaultMaxRes / Math.pow(2, 28), 1e-9); }); it('works with maxZoom and minZoom', function() { const minZoom = 2; const maxZoom = 11; const constraint = getConstraint({ minZoom: minZoom, maxZoom: maxZoom }); expect(constraint(defaultMaxRes, 0, size)).to.roughlyEqual( defaultMaxRes / Math.pow(2, minZoom), 1e-9); expect(constraint(0, 0, size)).to.roughlyEqual( defaultMaxRes / Math.pow(2, maxZoom), 1e-9); }); it('works with maxZoom, minZoom, and zoomFactor', function() { const minZoom = 4; const maxZoom = 8; const zoomFactor = 3; const constraint = getConstraint({ minZoom: minZoom, maxZoom: maxZoom, zoomFactor: zoomFactor }); expect(constraint(defaultMaxRes, 0, size)).to.roughlyEqual( defaultMaxRes / Math.pow(zoomFactor, minZoom), 1e-9); expect(constraint(0, 0, size)).to.roughlyEqual( defaultMaxRes / Math.pow(zoomFactor, maxZoom), 1e-9); }); }); describe('with resolution related options', function() { const defaultMaxRes = 156543.03392804097; const size = [200, 200]; function getConstraint(options) { return createResolutionConstraint(options).constraint; } it('works with only maxResolution', function() { const maxResolution = 10e6; const constraint = getConstraint({ multiWorld: true, maxResolution: maxResolution }); expect(constraint(maxResolution * 3, 0, size)).to.roughlyEqual( maxResolution, 1e-9); const minResolution = constraint(0, 0, size); const defaultMinRes = defaultMaxRes / Math.pow(2, 28); expect(minResolution).to.be.greaterThan(defaultMinRes); expect(minResolution / defaultMinRes).to.be.lessThan(2); }); it('works with only minResolution', function() { const minResolution = 100; const constraint = getConstraint({ minResolution: minResolution }); expect(constraint(defaultMaxRes, 0, size)).to.roughlyEqual( defaultMaxRes, 1e-9); const constrainedMinRes = constraint(0, 0, size); expect(constrainedMinRes).to.be.greaterThan(minResolution); expect(constrainedMinRes / minResolution).to.be.lessThan(2); }); it('works with minResolution and maxResolution', function() { const constraint = getConstraint({ maxResolution: 500, minResolution: 100, constrainResolution: true }); expect(constraint(600, 0, size)).to.be(500); expect(constraint(500, 0, size)).to.be(500); expect(constraint(400, 0, size)).to.be(500); expect(constraint(300, 0, size)).to.be(250); expect(constraint(200, 0, size)).to.be(250); expect(constraint(100, 0, size)).to.be(125); expect(constraint(0, 0, size)).to.be(125); }); it('accepts minResolution, maxResolution, and zoomFactor', function() { const constraint = getConstraint({ maxResolution: 500, minResolution: 1, zoomFactor: 10, constrainResolution: true }); expect(constraint(1000, 0, size)).to.be(500); expect(constraint(500, 0, size)).to.be(500); expect(constraint(100, 0, size)).to.be(50); expect(constraint(50, 0, size)).to.be(50); expect(constraint(10, 0, size)).to.be(5); expect(constraint(1, 0, size)).to.be(5); }); }); describe('overspecified options (prefers resolution)', function() { const defaultMaxRes = 156543.03392804097; const size = [200, 200]; function getConstraint(options) { return createResolutionConstraint(options).constraint; } it('respects maxResolution over minZoom', function() { const maxResolution = 10e6; const minZoom = 8; const constraint = getConstraint({ multiWorld: true, maxResolution: maxResolution, minZoom: minZoom }); expect(constraint(maxResolution * 3, 0, size)).to.roughlyEqual( maxResolution, 1e-9); const minResolution = constraint(0, 0, size); const defaultMinRes = defaultMaxRes / Math.pow(2, 28); expect(minResolution).to.be.greaterThan(defaultMinRes); expect(minResolution / defaultMinRes).to.be.lessThan(2); }); it('respects minResolution over maxZoom', function() { const minResolution = 100; const maxZoom = 50; const constraint = getConstraint({ minResolution: minResolution, maxZoom: maxZoom }); expect(constraint(defaultMaxRes, 0, size)).to.roughlyEqual( defaultMaxRes, 1e-9); const constrainedMinRes = constraint(0, 0, size); expect(constrainedMinRes).to.be.greaterThan(minResolution); expect(constrainedMinRes / minResolution).to.be.lessThan(2); }); }); describe('Map views that show more than one world', function() { const defaultMaxRes = 156543.03392804097; const size = [512, 512]; const maxResolution = 160000; const resolutions = [160000, 80000, 40000, 20000, 10000, 5000]; function getConstraint(options) { return createResolutionConstraint(options).constraint; } it('are disabled by default', function() { const fn = getConstraint({}); expect(fn(defaultMaxRes, 0, size)).to.be(defaultMaxRes / 2); }); it('can be enabled by setting multiWorld to true', function() { const fn = getConstraint({ multiWorld: true }); expect(fn(defaultMaxRes, 0, size)).to.be(defaultMaxRes); }); it('disabled, with constrainResolution', function() { const fn = getConstraint({ maxResolution: maxResolution, constrainResolution: true }); expect(fn(defaultMaxRes, 0, size)).to.be(maxResolution / 4); }); it('enabled, with constrainResolution', function() { const fn = getConstraint({ maxResolution: maxResolution, constrainResolution: true, multiWorld: true }); expect(fn(defaultMaxRes, 0, size)).to.be(maxResolution); }); it('disabled, with resolutions array', function() { const fn = getConstraint({ resolutions: resolutions }); expect(fn(defaultMaxRes, 0, size)).to.be(defaultMaxRes / 2); }); it('enabled, with resolutions array', function() { const fn = getConstraint({ resolutions: resolutions, multiWorld: true }); expect(fn(defaultMaxRes, 0, size)).to.be(defaultMaxRes); }); it('disabled, with resolutions array and constrainResolution', function() { const fn = getConstraint({ resolutions: resolutions, constrainResolution: true }); expect(fn(defaultMaxRes, 0, size)).to.be(resolutions[2]); }); it('enabled, with resolutions array and constrainResolution', function() { const fn = getConstraint({ resolutions: resolutions, constrainResolution: true, multiWorld: true }); expect(fn(defaultMaxRes, 0, size)).to.be(resolutions[0]); }); }); }); describe('create rotation constraint', function() { it('gives a correct rotation constraint function', function() { const options = {}; const fn = createRotationConstraint(options); expect(fn(0.01, 0)).to.eql(0); expect(fn(0.15, 0)).to.eql(0.15); }); }); }); describe('#setHint()', function() { it('changes a view hint', function() { const view = new View({ center: [0, 0], zoom: 0 }); expect(view.getHints()).to.eql([0, 0]); expect(view.getInteracting()).to.eql(false); view.setHint(ViewHint.INTERACTING, 1); expect(view.getHints()).to.eql([0, 1]); expect(view.getInteracting()).to.eql(true); }); it('triggers the change event', function(done) { const view = new View({ center: [0, 0], zoom: 0 }); view.on('change', function() { expect(view.getHints()).to.eql([0, 1]); expect(view.getInteracting()).to.eql(true); done(); }); view.setHint(ViewHint.INTERACTING, 1); }); }); describe('#getUpdatedOptions_()', function() { it('applies minZoom to constructor options', function() { const view = new View({ center: [0, 0], minZoom: 2, zoom: 10 }); const options = view.getUpdatedOptions_({minZoom: 3}); expect(options.center).to.eql([0, 0]); expect(options.minZoom).to.eql(3); expect(options.zoom).to.eql(10); }); it('applies the current zoom', function() { const view = new View({ center: [0, 0], zoom: 10 }); view.setZoom(8); const options = view.getUpdatedOptions_(); expect(options.center).to.eql([0, 0]); expect(options.zoom).to.eql(8); }); it('applies the current resolution if resolution was originally supplied', function() { const view = new View({ center: [0, 0], maxResolution: 2000, resolution: 1000 }); view.setResolution(500); const options = view.getUpdatedOptions_(); expect(options.center).to.eql([0, 0]); expect(options.resolution).to.eql(500); }); it('applies the current center', function() { const view = new View({ center: [0, 0], zoom: 10 }); view.setCenter([1, 2]); const options = view.getUpdatedOptions_(); expect(options.center).to.eql([1, 2]); expect(options.zoom).to.eql(10); }); it('applies the current rotation', function() { const view = new View({ center: [0, 0], zoom: 10 }); view.setRotation(Math.PI / 6); const options = view.getUpdatedOptions_(); expect(options.center).to.eql([0, 0]); expect(options.zoom).to.eql(10); expect(options.rotation).to.eql(Math.PI / 6); }); }); describe('#animate()', function() { const originalRequestAnimationFrame = window.requestAnimationFrame; const originalCancelAnimationFrame = window.cancelAnimationFrame; beforeEach(function() { window.requestAnimationFrame = function(callback) { return setTimeout(callback, 1); }; window.cancelAnimationFrame = function(key) { return clearTimeout(key); }; }); afterEach(function() { window.requestAnimationFrame = originalRequestAnimationFrame; window.cancelAnimationFrame = originalCancelAnimationFrame; }); it('can be called to animate view properties', function(done) { const view = new View({ center: [0, 0], zoom: 5 }); view.animate({ zoom: 4, duration: 25 }, function(complete) { expect(complete).to.be(true); expect(view.getCenter()).to.eql([0, 0]); expect(view.getZoom()).to.eql(4); expect(view.getAnimating()).to.eql(false); done(); }); expect(view.getAnimating()).to.eql(true); }); it('allows duration to be zero', function(done) { const view = new View({ center: [0, 0], zoom: 5 }); view.animate({ zoom: 4, duration: 0 }, function(complete) { expect(complete).to.be(true); expect(view.getCenter()).to.eql([0, 0]); expect(view.getZoom()).to.eql(4); expect(view.getAnimating()).to.eql(false); done(); }); }); it('immediately completes for no-op animations', function() { const view = new View({ center: [0, 0], zoom: 5 }); view.animate({ zoom: 5, center: [0, 0], duration: 25 }); expect(view.getAnimating()).to.eql(false); }); it('immediately completes if view is not defined before', function() { const view = new View(); const center = [1, 2]; const zoom = 3; const rotation = 0.4; view.animate({ zoom: zoom, center: center, rotation: rotation, duration: 25 }); expect(view.getAnimating()).to.eql(false); expect(view.getCenter()).to.eql(center); expect(view.getZoom()).to.eql(zoom); expect(view.getRotation()).to.eql(rotation); }); it('sets final animation state if view is not defined before', function() { const view = new View(); const center = [1, 2]; const zoom = 3; const rotation = 0.4; view.animate( {zoom: 1}, {center: [2, 3]}, {rotation: 4}, { zoom: zoom, center: center, rotation: rotation, duration: 25 } ); expect(view.getAnimating()).to.eql(false); expect(view.getCenter()).to.eql(center); expect(view.getZoom()).to.eql(zoom); expect(view.getRotation()).to.eql(rotation); }); it('prefers zoom over resolution', function(done) { const view = new View({ center: [0, 0], zoom: 5 }); view.animate({ zoom: 4, resolution: view.getResolution() * 3, duration: 25 }, function(complete) { expect(complete).to.be(true); expect(view.getZoom()).to.be(4); done(); }); }); it('avoids going under minResolution', function(done) { const maxZoom = 14; const view = new View({ center: [0, 0], zoom: 0, maxZoom: maxZoom }); const minResolution = view.getMinResolution(); view.animate({ resolution: minResolution, duration: 10 }, function(complete) { expect(complete).to.be(true); expect(view.getResolution()).to.be(minResolution); expect(view.getZoom()).to.be(maxZoom); done(); }); }); it('takes the shortest arc to the target rotation', function(done) { const view = new View({ center: [0, 0], zoom: 0, rotation: Math.PI / 180 * 1 }); view.animate({ rotation: Math.PI / 180 * 359, duration: 0 }, function(complete) { expect(complete).to.be(true); expect(view.getRotation()).to.roughlyEqual(Math.PI / 180 * -1, 1e-12); done(); }); }); it('normalizes rotation to angles between -180 and 180 degrees after the anmiation', function(done) { const view = new View({ center: [0, 0], zoom: 0, rotation: Math.PI / 180 * 1 }); view.animate({ rotation: Math.PI / 180 * -181, duration: 0 }, function(complete) { expect(complete).to.be(true); expect(view.getRotation()).to.roughlyEqual(Math.PI / 180 * 179, 1e-12); done(); }); }); it('calls a callback when animation completes', function(done) { const view = new View({ center: [0, 0], zoom: 0 }); view.animate({ zoom: 1, duration: 25 }, function(complete) { expect(complete).to.be(true); done(); }); }); it('allows the callback to trigger another animation', function(done) { const view = new View({ center: [0, 0], zoom: 0 }); function firstCallback(complete) { expect(complete).to.be(true); view.animate({ zoom: 2, duration: 10 }, secondCallback); } function secondCallback(complete) { expect(complete).to.be(true); done(); } view.animate({ zoom: 1, duration: 25 }, firstCallback); }); it('calls callback with false when animation is interrupted', function(done) { const view = new View({ center: [0, 0], zoom: 0 }); view.animate({ zoom: 1, duration: 25 }, function(complete) { expect(complete).to.be(false); done(); }); view.setCenter([1, 2]); // interrupt the animation }); it('calls a callback even if animation is a no-op', function(done) { const view = new View({ center: [0, 0], zoom: 0 }); view.animate({ zoom: 0, duration: 25 }, function(complete) { expect(complete).to.be(true); done(); }); }); it('calls a callback if view is not defined before', function(done) { const view = new View(); view.animate({ zoom: 10, duration: 25 }, function(complete) { expect(view.getZoom()).to.be(10); expect(complete).to.be(true); done(); }); }); it('can run multiple animations in series', function(done) { const view = new View({ center: [0, 0], zoom: 0 }); let checked = false; view.animate({ zoom: 2, duration: 25 }, { center: [10, 10], duration: 25 }, function(complete) { expect(checked).to.be(true); expect(view.getZoom()).to.roughlyEqual(2, 1e-5); expect(view.getCenter()).to.eql([10, 10]); expect(complete).to.be(true); done(); }); setTimeout(function() { expect(view.getCenter()).to.eql([0, 0]); checked = true; }, 10); }); it('properly sets the ANIMATING hint', function(done) { const view = new View({ center: [0, 0], zoom: 0, rotation: 0 }); let count = 3; function decrement() { --count; if (count === 0) { expect(view.getHints()[ViewHint.ANIMATING]).to.be(0); done(); } } view.animate({ center: [1, 2], duration: 25 }, decrement); expect(view.getHints()[ViewHint.ANIMATING]).to.be(1); view.animate({ zoom: 1, duration: 25 }, decrement); expect(view.getHints()[ViewHint.ANIMATING]).to.be(2); view.animate({ rotation: Math.PI, duration: 25 }, decrement); expect(view.getHints()[ViewHint.ANIMATING]).to.be(3); }); it('clears the ANIMATING hint when animations are cancelled', function() { const view = new View({ center: [0, 0], zoom: 0, rotation: 0 }); view.animate({ center: [1, 2], duration: 25 }); expect(view.getHints()[ViewHint.ANIMATING]).to.be(1); view.animate({ zoom: 1, duration: 25 }); expect(view.getHints()[ViewHint.ANIMATING]).to.be(2); view.animate({ rotation: Math.PI, duration: 25 }); expect(view.getHints()[ViewHint.ANIMATING]).to.be(3); // cancel animations view.setCenter([10, 20]); expect(view.getHints()[ViewHint.ANIMATING]).to.be(0); }); it('completes multiple staggered animations run in parallel', function(done) { const view = new View({ center: [0, 0], zoom: 0 }); let calls = 0; view.animate({ zoom: 1, duration: 25 }, function() { ++calls; }); setTimeout(function() { expect(view.getZoom() > 0).to.be(true); expect(view.getZoom() < 1).to.be(true); expect(view.getAnimating()).to.be(true); view.animate({ zoom: 2, duration: 50 }, function() { expect(calls).to.be(1); expect(view.getZoom()).to.be(2); expect(view.getAnimating()).to.be(false); done(); }); }, 10); }); it('completes complex animation using resolution', function(done) { const view = new View({ center: [0, 0], resolution: 2 }); let calls = 0; function onAnimateEnd() { if (calls == 2) { expect(view.getAnimating()).to.be(false); done(); } } view.animate({ center: [100, 100], duration: 50 }, function() { ++calls; expect(view.getCenter()).to.eql([100, 100]); onAnimateEnd(); }); view.animate({ resolution: 2000, duration: 25 }, { resolution: 2, duration: 25 }, function() { ++calls; expect(view.getResolution()).to.be(2); onAnimateEnd(); }); setTimeout(function() { expect(view.getResolution() > 2).to.be(true); expect(view.getResolution() < 2000).to.be(true); expect(view.getAnimating()).to.be(true); }, 10); setTimeout(function() { expect(view.getResolution() > 2).to.be(true); expect(view.getResolution() < 2000).to.be(true); expect(view.getAnimating()).to.be(true); }, 40); }); }); describe('#cancelAnimations()', function() { const originalRequestAnimationFrame = window.requestAnimationFrame; const originalCancelAnimationFrame = window.cancelAnimationFrame; beforeEach(function() { window.requestAnimationFrame = function(callback) { return setTimeout(callback, 1); }; window.cancelAnimationFrame = function(key) { return clearTimeout(key); }; }); afterEach(function() { window.requestAnimationFrame = originalRequestAnimationFrame; window.cancelAnimationFrame = originalCancelAnimationFrame; }); it('cancels a currently running animation', function(done) { const view = new View({ center: [0, 0], zoom: 0, rotation: 0 }); view.animate({ rotation: 10, duration: 50 }); setTimeout(function() { expect(view.getAnimating()).to.be(true); view.once('change', function() { expect(view.getAnimating()).to.be(false); done(); }); view.cancelAnimations(); }, 10); }); it('cancels a multiple animations', function(done) { const view = new View({ center: [0, 0], zoom: 0, rotation: 0 }); view.animate({ rotation: 10, duration: 50 }, { zoom: 10, duration: 50 }); view.animate({ center: [10, 30], duration: 100 }); setTimeout(function() { expect(view.getAnimating()).to.be(true); view.once('change', function() { expect(view.getAnimating()).to.be(false); done(); }); view.cancelAnimations(); }, 10); }); it('calls callbacks with false to indicate animations did not complete', function(done) { const view = new View({ center: [0, 0], zoom: 0 }); view.animate({ zoom: 10, duration: 50 }, function(complete) { expect(view.getAnimating()).to.be(false); expect(complete).to.be(false); done(); }); setTimeout(function() { expect(view.getAnimating()).to.be(true); view.cancelAnimations(); }, 10); }); }); describe('#getResolutions', function() { let view; const resolutions = [512, 256, 128, 64, 32, 16]; it('returns correct resolutions', function() { view = new View({ resolutions: resolutions }); expect(view.getResolutions()).to.be(resolutions); }); it('returns resolutions as undefined', function() { view = new View(); expect(view.getResolutions()).to.be(undefined); }); }); describe('#getZoom', function() { let view; beforeEach(function() { view = new View({ resolutions: [1024, 512, 256, 128, 64, 32, 16, 8] }); }); it('returns correct zoom levels (with resolutions array)', function() { view.setResolution(undefined); expect(view.getZoom()).to.be(undefined); view.setResolution(513); expect(view.getZoom()).to.roughlyEqual(Math.log(1024 / 513) / Math.LN2, 1e-9); view.setResolution(512); expect(view.getZoom()).to.be(1); view.setResolution(100); expect(view.getZoom()).to.roughlyEqual(3.35614, 1e-5); view.setResolution(65); expect(view.getZoom()).to.roughlyEqual(3.97763, 1e-5); view.setResolution(64); expect(view.getZoom()).to.be(4); view.setResolution(16); expect(view.getZoom()).to.be(6); view.setResolution(15); expect(view.getZoom()).to.roughlyEqual(Math.log(1024 / 15) / Math.LN2, 1e-9); }); it('works for resolution arrays with variable zoom factors', function() { const view = new View({ resolutions: [10, 5, 2, 1] }); view.setZoom(1); expect(view.getZoom()).to.be(1); view.setZoom(1.3); expect(view.getZoom()).to.be(1.3); view.setZoom(2); expect(view.getZoom()).to.be(2); view.setZoom(2.7); expect(view.getZoom()).to.be(2.7); view.setZoom(3); expect(view.getZoom()).to.be(3); }); }); describe('#getZoom() - constrained', function() { it('returns correct zoom levels', function() { const view = new View({ minZoom: 10, maxZoom: 20 }); view.setZoom(5); expect(view.getZoom()).to.be(10); view.setZoom(10); expect(view.getZoom()).to.be(10); view.setZoom(15); expect(view.getZoom()).to.be(15); view.setZoom(15.3); expect(view.getZoom()).to.be(15.3); view.setZoom(20); expect(view.getZoom()).to.be(20); view.setZoom(25); expect(view.getZoom()).to.be(20); }); }); describe('#getZoom() - overspecified', function() { it('gives maxResolution precedence over minZoom', function() { const view = new View({ maxResolution: 100, minZoom: 2 // this should get ignored }); view.setResolution(100); expect(view.getZoom()).to.be(0); view.setZoom(0); expect(view.getResolution()).to.be(100); }); }); describe('#getZoomForResolution', function() { it('returns correct zoom levels', function() { const view = new View(); const max = view.getMaxResolution(); expect(view.getZoomForResolution(max)).to.be(0); expect(view.getZoomForResolution(max / 2)).to.be(1); expect(view.getZoomForResolution(max / 4)).to.be(2); expect(view.getZoomForResolution(2 * max)).to.be(-1); }); it('returns correct zoom levels for specifically configured resolutions', function() { const view = new View({ resolutions: [10, 8, 6, 4, 2] }); expect(view.getZoomForResolution(10)).to.be(0); expect(view.getZoomForResolution(8)).to.be(1); expect(view.getZoomForResolution(6)).to.be(2); expect(view.getZoomForResolution(4)).to.be(3); expect(view.getZoomForResolution(2)).to.be(4); }); }); describe('#getResolutionForZoom', function() { it('returns correct zoom resolution', function() { const view = new View(); const max = view.getMaxZoom(); const min = view.getMinZoom(); expect(view.getResolutionForZoom(max)).to.be(view.getMinResolution()); expect(view.getResolutionForZoom(max + 1)).to.be(view.getMinResolution() / 2); expect(view.getResolutionForZoom(min)).to.be(view.getMaxResolution()); expect(view.getResolutionForZoom(min - 1)).to.be(view.getMaxResolution() * 2); }); it('returns correct zoom levels for specifically configured resolutions', function() { const view = new View({ resolutions: [10, 8, 6, 4, 2] }); expect(view.getResolutionForZoom(-1)).to.be(10); expect(view.getResolutionForZoom(0)).to.be(10); expect(view.getResolutionForZoom(1)).to.be(8); expect(view.getResolutionForZoom(2)).to.be(6); expect(view.getResolutionForZoom(3)).to.be(4); expect(view.getResolutionForZoom(4)).to.be(2); expect(view.getResolutionForZoom(5)).to.be(2); }); it('returns correct zoom levels for resolutions with variable zoom levels', function() { const view = new View({ resolutions: [50, 10, 5, 2.5, 1.25, 0.625] }); expect(view.getResolutionForZoom(-1)).to.be(50); expect(view.getResolutionForZoom(0)).to.be(50); expect(view.getResolutionForZoom(0.5)).to.be(50 / Math.pow(5, 0.5)); expect(view.getResolutionForZoom(1)).to.be(10); expect(view.getResolutionForZoom(2)).to.be(5); expect(view.getResolutionForZoom(2.75)).to.be(5 / Math.pow(2, 0.75)); expect(view.getResolutionForZoom(3)).to.be(2.5); expect(view.getResolutionForZoom(4)).to.be(1.25); expect(view.getResolutionForZoom(5)).to.be(0.625); expect(view.getResolutionForZoom(6)).to.be(0.625); }); }); describe('#getMaxZoom', function() { it('returns the zoom level for the min resolution', function() { const view = new View(); expect(view.getMaxZoom()).to.be(view.getZoomForResolution(view.getMinResolution())); }); it('works for a view configured with a maxZoom', function() { const view = new View({ maxZoom: 10 }); expect(view.getMaxZoom()).to.be(10); }); }); describe('#getMinZoom', function() { it('returns the zoom level for the max resolution', function() { const view = new View(); expect(view.getMinZoom()).to.be(view.getZoomForResolution(view.getMaxResolution())); }); it('works for views configured with a minZoom', function() { const view = new View({ minZoom: 3 }); expect(view.getMinZoom()).to.be(3); }); }); describe('#setMaxZoom', function() { describe('with resolutions property in view', function() { it('changes the zoom level when the level is over max zoom', function() { const view = new View({ resolutions: [100000, 50000, 25000, 12500, 6250, 3125], zoom: 4 }); view.setMaxZoom(2); expect(view.getZoom()).to.be(2); }); }); describe('with no resolutions property in view', function() { it('changes the zoom level when the level is over max zoom', function() { const view = new View({ zoom: 4 }); view.setMaxZoom(2); expect(view.getZoom()).to.be(2); }); }); }); describe('#setMinZoom', function() { describe('with resolutions property in view', function() { it('changes the zoom level when the level is under min zoom', function() { const view = new View({ resolutions: [100000, 50000, 25000, 12500, 6250, 3125], zoom: 4 }); view.setMinZoom(5); expect(view.getZoom()).to.be(5); }); }); describe('with no resolutions property in view', function() { it('changes the zoom level when the level is under min zoom', function() { const view = new View({ zoom: 4 }); view.setMinZoom(5); expect(view.getZoom()).to.be(5); }); }); }); describe('#calculateExtent', function() { it('returns the expected extent', function() { const view = new View({ resolutions: [512], zoom: 0, center: [0, 0] }); const extent = view.calculateExtent([100, 200]); expect(extent[0]).to.be(-25600); expect(extent[1]).to.be(-51200); expect(extent[2]).to.be(25600); expect(extent[3]).to.be(51200); }); it('returns the expected extent with rotation', function() { const view = new View({ resolutions: [512], zoom: 0, center: [0, 0], rotation: Math.PI / 2 }); const extent = view.calculateExtent([100, 200]); expect(extent[0]).to.roughlyEqual(-51200, 1e-9); expect(extent[1]).to.roughlyEqual(-25600, 1e-9); expect(extent[2]).to.roughlyEqual(51200, 1e-9); expect(extent[3]).to.roughlyEqual(25600, 1e-9); }); }); describe('#getViewportSize_()', function() { let map, target; beforeEach(function() { target = document.createElement('div'); target.style.width = '200px'; target.style.height = '150px'; document.body.appendChild(target); map = new Map({ target: target }); }); afterEach(function() { map.setTarget(null); document.body.removeChild(target); }); it('correctly initializes the viewport size', function() { const size = map.getView().getViewportSize_(); expect(size).to.eql([200, 150]); }); it('correctly updates the viewport size', function() { target.style.width = '300px'; target.style.height = '200px'; map.updateSize(); const size = map.getView().getViewportSize_(); expect(size).to.eql([300, 200]); }); it('calculates the size correctly', function() { let size = map.getView().getViewportSize_(Math.PI / 2); expect(size[0]).to.roughlyEqual(150, 1e-9); expect(size[1]).to.roughlyEqual(200, 1e-9); size = map.getView().getViewportSize_(Math.PI); expect(size[0]).to.roughlyEqual(200, 1e-9); expect(size[1]).to.roughlyEqual(150, 1e-9); }); }); describe('fit', function() { const originalRequestAnimationFrame = window.requestAnimationFrame; const originalCancelAnimationFrame = window.cancelAnimationFrame; beforeEach(function() { window.requestAnimationFrame = function(callback) { return setTimeout(callback, 1); }; window.cancelAnimationFrame = function(key) { return clearTimeout(key); }; }); afterEach(function() { window.requestAnimationFrame = originalRequestAnimationFrame; window.cancelAnimationFrame = originalCancelAnimationFrame; }); let view; beforeEach(function() { view = new View({ center: [0, 0], resolutions: [200, 100, 50, 20, 10, 5, 2, 1], zoom: 5 }); }); it('fits correctly to the geometry (with unconstrained resolution)', function() { view.fit( new LineString([[6000, 46000], [6000, 47100], [7000, 46000]]), {size: [200, 200], padding: [100, 0, 0, 100]}); expect(view.getResolution()).to.be(11); expect(view.getCenter()[0]).to.be(5950); expect(view.getCenter()[1]).to.be(47100); view.fit( new Circle([6000, 46000], 1000), {size: [200, 200]}); expect(view.getResolution()).to.be(10); expect(view.getCenter()[0]).to.be(6000); expect(view.getCenter()[1]).to.be(46000); view.setRotation(Math.PI / 8); view.fit( new Circle([6000, 46000], 1000), {size: [200, 200]}); expect(view.getResolution()).to.roughlyEqual(10, 1e-9); expect(view.getCenter()[0]).to.roughlyEqual(6000, 1e-9); expect(view.getCenter()[1]).to.roughlyEqual(46000, 1e-9); view.setRotation(Math.PI / 4); view.fit( new LineString([[6000, 46000], [6000, 47100], [7000, 46000]]), {size: [200, 200], padding: [100, 0, 0, 100]}); expect(view.getResolution()).to.roughlyEqual(14.849242404917458, 1e-9); expect(view.getCenter()[0]).to.roughlyEqual(5200, 1e-9); expect(view.getCenter()[1]).to.roughlyEqual(46300, 1e-9); }); it('fits correctly to the geometry', function() { view.setConstrainResolution(true); view.fit( new LineString([[6000, 46000], [6000, 47100], [7000, 46000]]), {size: [200, 200], padding: [100, 0, 0, 100]}); expect(view.getResolution()).to.be(20); expect(view.getCenter()[0]).to.be(5500); expect(view.getCenter()[1]).to.be(47550); view.fit( new LineString([[6000, 46000], [6000, 47100], [7000, 46000]]), {size: [200, 200], padding: [100, 0, 0, 100], nearest: true}); expect(view.getResolution()).to.be(10); expect(view.getCenter()[0]).to.be(6000); expect(view.getCenter()[1]).to.be(47050); view.fit( new Point([6000, 46000]), {size: [200, 200], padding: [100, 0, 0, 100], minResolution: 2}); expect(view.getResolution()).to.be(2); expect(view.getCenter()[0]).to.be(5900); expect(view.getCenter()[1]).to.be(46100); view.fit( new Point([6000, 46000]), {size: [200, 200], padding: [100, 0, 0, 100], maxZoom: 6}); expect(view.getResolution()).to.be(2); expect(view.getZoom()).to.be(6); expect(view.getCenter()[0]).to.be(5900); expect(view.getCenter()[1]).to.be(46100); }); it('fits correctly to the extent', function() { view.fit([1000, 1000, 2000, 2000], {size: [200, 200]}); expect(view.getResolution()).to.be(5); expect(view.getCenter()[0]).to.be(1500); expect(view.getCenter()[1]).to.be(1500); }); it('throws on invalid geometry/extent value', function() { expect(function() { view.fit(true, [200, 200]); }).to.throwException(); }); it('throws on empty extent', function() { expect(function() { view.fit(createEmpty()); }).to.throwException(); }); it('animates when duration is defined', function(done) { view.fit( new LineString([[6000, 46000], [6000, 47100], [7000, 46000]]), { size: [200, 200], padding: [100, 0, 0, 100], duration: 25 }); expect(view.getAnimating()).to.eql(true); setTimeout(function() { expect(view.getResolution()).to.be(11); expect(view.getCenter()[0]).to.be(5950); expect(view.getCenter()[1]).to.be(47100); expect(view.getAnimating()).to.eql(false); done(); }, 50); }); it('calls a callback when duration is not defined', function(done) { view.fit(new LineString([[6000, 46000], [6000, 47100], [7000, 46000]]), { callback: function(complete) { expect(complete).to.be(true); done(); } }); }); it('calls a callback when animation completes', function(done) { view.fit(new LineString([[6000, 46000], [6000, 47100], [7000, 46000]]), { duration: 25, callback: function(complete) { expect(complete).to.be(true); done(); } }); }); }); describe('centerOn', function() { let view; beforeEach(function() { view = new View({ resolutions: [200, 100, 50, 20, 10, 5, 2, 1] }); }); it('fit correctly to the coordinates', function() { view.setResolution(10); view.centerOn( [6000, 46000], [400, 400], [300, 300] ); expect(view.getCenter()[0]).to.be(5000); expect(view.getCenter()[1]).to.be(47000); view.setRotation(Math.PI / 4); view.centerOn( [6000, 46000], [400, 400], [300, 300] ); expect(view.getCenter()[0]).to.roughlyEqual(4585.78643762691, 1e-9); expect(view.getCenter()[1]).to.roughlyEqual(46000, 1e-9); }); }); describe('#beginInteraction() and endInteraction()', function() { let view; beforeEach(function() { view = new View(); }); it('correctly changes the view hint', function() { view.beginInteraction(); expect(view.getHints()[1]).to.be(1); view.beginInteraction(); expect(view.getHints()[1]).to.be(2); view.endInteraction(); view.endInteraction(); expect(view.getHints()[1]).to.be(0); }); }); describe('#getConstrainedZoom()', function() { let view; it('works correctly without constraint', function() { view = new View({ zoom: 0 }); expect(view.getConstrainedZoom(3)).to.be(3); }); it('works correctly with resolution constraints', function() { view = new View({ zoom: 4, minZoom: 4, maxZoom: 8 }); expect(view.getConstrainedZoom(3)).to.be(4); expect(view.getConstrainedZoom(10)).to.be(8); }); it('works correctly with a specific resolution set', function() { view = new View({ zoom: 0, resolutions: [512, 256, 128, 64, 32, 16, 8] }); expect(view.getConstrainedZoom(0)).to.be(0); expect(view.getConstrainedZoom(4)).to.be(4); expect(view.getConstrainedZoom(8)).to.be(6); }); }); describe('#getConstrainedResolution()', function() { let view; const defaultMaxRes = 156543.03392804097; it('works correctly by snapping to power of 2', function() { view = new View(); expect(view.getConstrainedResolution(1000000)).to.be(defaultMaxRes); expect(view.getConstrainedResolution(defaultMaxRes / 8)).to.be(defaultMaxRes / 8); }); it('works correctly by snapping to a custom zoom factor', function() { view = new View({ maxResolution: 2500, zoomFactor: 5, maxZoom: 4, constrainResolution: true }); expect(view.getConstrainedResolution(90, 1)).to.be(100); expect(view.getConstrainedResolution(90, -1)).to.be(20); expect(view.getConstrainedResolution(20)).to.be(20); expect(view.getConstrainedResolution(5)).to.be(4); expect(view.getConstrainedResolution(1)).to.be(4); }); it('works correctly with a specific resolution set', function() { view = new View({ zoom: 0, resolutions: [512, 256, 128, 64, 32, 16, 8], constrainResolution: true }); expect(view.getConstrainedResolution(1000, 1)).to.be(512); expect(view.getConstrainedResolution(260, 1)).to.be(512); expect(view.getConstrainedResolution(260)).to.be(256); expect(view.getConstrainedResolution(30)).to.be(32); expect(view.getConstrainedResolution(30, -1)).to.be(16); expect(view.getConstrainedResolution(4, -1)).to.be(8); }); }); describe('#adjustRotation()', function() { it('changes view rotation with anchor', function() { const view = new View({ resolution: 1, center: [0, 0] }); view.adjustRotation(Math.PI / 2); expect(view.getRotation()).to.be(Math.PI / 2); expect(view.getCenter()).to.eql([0, 0]); view.adjustRotation(-Math.PI); expect(view.getRotation()).to.be(-Math.PI / 2); expect(view.getCenter()).to.eql([0, 0]); view.adjustRotation(Math.PI / 3, [50, 0]); expect(view.getRotation()).to.roughlyEqual(-Math.PI / 6, 1e-9); expect(view.getCenter()[0]).to.roughlyEqual(50 * (1 - Math.cos(Math.PI / 3)), 1e-9); expect(view.getCenter()[1]).to.roughlyEqual(-50 * Math.sin(Math.PI / 3), 1e-9); }); it('does not change view parameters if rotation is disabled', function() { const view = new View({ resolution: 1, enableRotation: false, center: [0, 0] }); view.adjustRotation(Math.PI / 2); expect(view.getRotation()).to.be(0); expect(view.getCenter()).to.eql([0, 0]); view.adjustRotation(-Math.PI * 3, [-50, 0]); expect(view.getRotation()).to.be(0); expect(view.getCenter()).to.eql([0, 0]); }); }); describe('#adjustZoom()', function() { it('changes view resolution', function() { const view = new View({ resolution: 1, resolutions: [4, 2, 1, 0.5, 0.25] }); view.adjustZoom(1); expect(view.getResolution()).to.be(0.5); view.adjustZoom(-1); expect(view.getResolution()).to.be(1); view.adjustZoom(2); expect(view.getResolution()).to.be(0.25); view.adjustZoom(-2); expect(view.getResolution()).to.be(1); }); it('changes view resolution and center relative to the anchor', function() { const view = new View({ center: [0, 0], resolution: 1, resolutions: [4, 2, 1, 0.5, 0.25] }); view.adjustZoom(1, [10, 10]); expect(view.getCenter()).to.eql([5, 5]); view.adjustZoom(-1, [0, 0]); expect(view.getCenter()).to.eql([10, 10]); view.adjustZoom(2, [0, 0]); expect(view.getCenter()).to.eql([2.5, 2.5]); view.adjustZoom(-2, [0, 0]); expect(view.getCenter()).to.eql([10, 10]); }); it('changes view resolution and center relative to the anchor, while respecting the extent (center only)', function() { const view = new View({ center: [0, 0], extent: [-2.5, -2.5, 2.5, 2.5], constrainOnlyCenter: true, resolution: 1, resolutions: [4, 2, 1, 0.5, 0.25] }); view.adjustZoom(1, [10, 10]); expect(view.getCenter()).to.eql([2.5, 2.5]); view.adjustZoom(-1, [0, 0]); expect(view.getCenter()).to.eql([2.5, 2.5]); view.adjustZoom(2, [10, 10]); expect(view.getCenter()).to.eql([2.5, 2.5]); view.adjustZoom(-2, [0, 0]); expect(view.getCenter()).to.eql([2.5, 2.5]); }); it('changes view resolution and center relative to the anchor, while respecting the extent', function() { const map = new Map({}); const view = new View({ center: [50, 50], extent: [0, 0, 100, 100], resolution: 1, resolutions: [4, 2, 1, 0.5, 0.25, 0.125] }); map.setView(view); view.adjustZoom(1, [100, 100]); expect(view.getCenter()).to.eql([75, 75]); view.adjustZoom(-1, [75, 75]); expect(view.getCenter()).to.eql([50, 50]); view.adjustZoom(2, [100, 100]); expect(view.getCenter()).to.eql([87.5, 87.5]); view.adjustZoom(-3, [0, 0]); expect(view.getCenter()).to.eql([50, 50]); expect(view.getResolution()).to.eql(1); }); it('changes view resolution and center relative to the anchor, while respecting the extent (rotated)', function() { const map = new Map({}); const view = new View({ center: [50, 50], extent: [-100, -100, 100, 100], resolution: 1, resolutions: [2, 1, 0.5, 0.25, 0.125], rotation: Math.PI / 4 }); map.setView(view); const halfSize = 100 * Math.SQRT1_2; view.adjustZoom(1, [100, 100]); expect(view.getCenter()).to.eql([100 - halfSize / 2, 100 - halfSize / 2]); view.setCenter([0, 50]); view.adjustZoom(-1, [0, 0]); expect(view.getCenter()).to.eql([0, 100 - halfSize]); }); }); describe('#adjustZoom() - useGeographic', () => { beforeEach(useGeographic); afterEach(clearUserProjection); it('changes view resolution', () => { const view = new View({ resolution: 1, resolutions: [4, 2, 1, 0.5, 0.25] }); view.adjustZoom(1); expect(view.getResolution()).to.be(0.5); view.adjustZoom(-1); expect(view.getResolution()).to.be(1); view.adjustZoom(2); expect(view.getResolution()).to.be(0.25); view.adjustZoom(-2); expect(view.getResolution()).to.be(1); }); it('changes view resolution and center relative to the anchor', function() { const view = new View({ center: [0, 0], zoom: 0 }); let center; view.adjustZoom(1, [90, 45]); center = view.getCenter(); expect(center[0]).to.be(45); expect(center[1]).to.roughlyEqual(24.4698, 1e-4); view.adjustZoom(-1, [90, 45]); center = view.getCenter(); expect(center[0]).to.roughlyEqual(0, 1e-10); expect(center[1]).to.roughlyEqual(0, 1e-10); view.adjustZoom(2, [-90, -45]); center = view.getCenter(); expect(center[0]).to.be(-67.5); expect(center[1]).to.roughlyEqual(-35.3836, 1e-4); view.adjustZoom(-2, [-90, -45]); center = view.getCenter(); expect(center[0]).to.roughlyEqual(0, 1e-10); expect(center[1]).to.roughlyEqual(0, 1e-10); }); }); }); describe('does not start unexpected animations during interaction', function() { let map; beforeEach(function() { map = new Map({ target: createMapDiv(512, 256) }); }); afterEach(function() { disposeMap(map); }); it('works when initialized with #setCenter() and #setZoom()', function(done) { const view = map.getView(); let callCount = 0; view.on('change:resolution', function() { ++callCount; }); view.setCenter([0, 0]); view.setZoom(0); view.beginInteraction(); view.endInteraction(); setTimeout(function() { expect(callCount).to.be(1); done(); }, 500); }); it('works when initialized with #animate()', function(done) { const view = map.getView(); let callCount = 0; view.on('change:resolution', function() { ++callCount; }); view.animate({ center: [0, 0], zoom: 0 }); view.beginInteraction(); view.endInteraction(); setTimeout(function() { expect(callCount).to.be(1); done(); }, 500); }); }); describe('ol.View.isNoopAnimation()', function() { const cases = [{ animation: { sourceCenter: [0, 0], targetCenter: [0, 0], sourceResolution: 1, targetResolution: 1, sourceRotation: 0, targetRotation: 0 }, noop: true }, { animation: { sourceCenter: [0, 0], targetCenter: [0, 1], sourceResolution: 1, targetResolution: 1, sourceRotation: 0, targetRotation: 0 }, noop: false }, { animation: { sourceCenter: [0, 0], targetCenter: [0, 0], sourceResolution: 1, targetResolution: 0, sourceRotation: 0, targetRotation: 0 }, noop: false }, { animation: { sourceCenter: [0, 0], targetCenter: [0, 0], sourceResolution: 1, targetResolution: 1, sourceRotation: 0, targetRotation: 1 }, noop: false }, { animation: { sourceCenter: [0, 0], targetCenter: [0, 0] }, noop: true }, { animation: { sourceCenter: [1, 0], targetCenter: [0, 0] }, noop: false }, { animation: { sourceResolution: 1, targetResolution: 1 }, noop: true }, { animation: { sourceResolution: 0, targetResolution: 1 }, noop: false }, { animation: { sourceRotation: 10, targetRotation: 10 }, noop: true }, { animation: { sourceRotation: 0, targetRotation: 10 }, noop: false }]; cases.forEach(function(c, i) { it('works for case ' + i, function() { const noop = isNoopAnimation(c.animation); expect(noop).to.equal(c.noop); }); }); });