UNPKG

63.4 kBPlain TextView Raw
1import Camera from '../ui/camera';
2import Transform from '../geo/transform';
3import TaskQueue, {TaskID} from '../util/task_queue';
4import browser from '../util/browser';
5import {fixedLngLat, fixedNum} from '../../test/unit/lib/fixed';
6import {setMatchMedia} from '../util/test/util';
7
8beforeEach(() => {
9 setMatchMedia();
10 Object.defineProperty(browser, 'prefersReducedMotion', {value: false});
11});
12
13class CameraMock extends Camera {
14 // eslint-disable-next-line
15 _requestRenderFrame(a: () => void): TaskID {
16 return undefined;
17 }
18
19 _cancelRenderFrame(_: TaskID): void {
20 return undefined;
21 }
22}
23
24function attachSimulateFrame(camera) {
25 const queue = new TaskQueue();
26 camera._requestRenderFrame = (cb) => queue.add(cb);
27 camera._cancelRenderFrame = (id) => queue.remove(id);
28 camera.simulateFrame = () => queue.run();
29 return camera;
30}
31
32function createCamera(options?) {
33 options = options || {};
34
35 const transform = new Transform(0, 20, 0, 60, options.renderWorldCopies);
36 transform.resize(512, 512);
37
38 const camera = attachSimulateFrame(new CameraMock(transform, {} as any))
39 .jumpTo(options);
40
41 camera._update = () => {};
42
43 return camera;
44}
45
46function assertTransitionTime(done, camera, min, max) {
47 let startTime;
48 camera
49 .on('movestart', () => { startTime = new Date(); })
50 .on('moveend', () => {
51 const endTime = new Date();
52 const timeDiff = endTime.getTime() - startTime.getTime();
53 expect(timeDiff >= min && timeDiff < max).toBeTruthy();
54 done();
55 });
56}
57
58describe('#jumpTo', () => {
59 // Choose initial zoom to avoid center being constrained by mercator latitude limits.
60 const camera = createCamera({zoom: 1});
61
62 test('sets center', () => {
63 camera.jumpTo({center: [1, 2]});
64 expect(camera.getCenter()).toEqual({lng: 1, lat: 2});
65 });
66
67 test('throws on invalid center argument', () => {
68 expect(() => {
69 camera.jumpTo({center: 1});
70 }).toThrow(Error);
71 });
72
73 test('keeps current center if not specified', () => {
74 camera.jumpTo({});
75 expect(camera.getCenter()).toEqual({lng: 1, lat: 2});
76 });
77
78 test('sets zoom', () => {
79 camera.jumpTo({zoom: 3});
80 expect(camera.getZoom()).toBe(3);
81 });
82
83 test('keeps current zoom if not specified', () => {
84 camera.jumpTo({});
85 expect(camera.getZoom()).toBe(3);
86 });
87
88 test('sets bearing', () => {
89 camera.jumpTo({bearing: 4});
90 expect(camera.getBearing()).toBe(4);
91 });
92
93 test('keeps current bearing if not specified', () => {
94 camera.jumpTo({});
95 expect(camera.getBearing()).toBe(4);
96 });
97
98 test('sets pitch', () => {
99 camera.jumpTo({pitch: 45});
100 expect(camera.getPitch()).toBe(45);
101 });
102
103 test('keeps current pitch if not specified', () => {
104 camera.jumpTo({});
105 expect(camera.getPitch()).toBe(45);
106 });
107
108 test('sets multiple properties', () => {
109 camera.jumpTo({
110 center: [10, 20],
111 zoom: 10,
112 bearing: 180,
113 pitch: 60
114 });
115 expect(camera.getCenter()).toEqual({lng: 10, lat: 20});
116 expect(camera.getZoom()).toBe(10);
117 expect(camera.getBearing()).toBe(180);
118 expect(camera.getPitch()).toBe(60);
119 });
120
121 test('emits move events, preserving eventData', done => {
122 let started, moved, ended;
123 const eventData = {data: 'ok'};
124
125 camera
126 .on('movestart', (d) => { started = d.data; })
127 .on('move', (d) => { moved = d.data; })
128 .on('moveend', (d) => { ended = d.data; });
129
130 camera.jumpTo({center: [1, 2]}, eventData);
131 expect(started).toBe('ok');
132 expect(moved).toBe('ok');
133 expect(ended).toBe('ok');
134 done();
135 });
136
137 test('emits zoom events, preserving eventData', done => {
138 let started, zoomed, ended;
139 const eventData = {data: 'ok'};
140
141 camera
142 .on('zoomstart', (d) => { started = d.data; })
143 .on('zoom', (d) => { zoomed = d.data; })
144 .on('zoomend', (d) => { ended = d.data; });
145
146 camera.jumpTo({zoom: 3}, eventData);
147 expect(started).toBe('ok');
148 expect(zoomed).toBe('ok');
149 expect(ended).toBe('ok');
150 done();
151 });
152
153 test('emits rotate events, preserving eventData', done => {
154 let started, rotated, ended;
155 const eventData = {data: 'ok'};
156
157 camera
158 .on('rotatestart', (d) => { started = d.data; })
159 .on('rotate', (d) => { rotated = d.data; })
160 .on('rotateend', (d) => { ended = d.data; });
161
162 camera.jumpTo({bearing: 90}, eventData);
163 expect(started).toBe('ok');
164 expect(rotated).toBe('ok');
165 expect(ended).toBe('ok');
166 done();
167 });
168
169 test('emits pitch events, preserving eventData', done => {
170 let started, pitched, ended;
171 const eventData = {data: 'ok'};
172
173 camera
174 .on('pitchstart', (d) => { started = d.data; })
175 .on('pitch', (d) => { pitched = d.data; })
176 .on('pitchend', (d) => { ended = d.data; });
177
178 camera.jumpTo({pitch: 10}, eventData);
179 expect(started).toBe('ok');
180 expect(pitched).toBe('ok');
181 expect(ended).toBe('ok');
182 done();
183 });
184
185 test('cancels in-progress easing', () => {
186 camera.panTo([3, 4]);
187 expect(camera.isEasing()).toBeTruthy();
188 camera.jumpTo({center: [1, 2]});
189 expect(!camera.isEasing()).toBeTruthy();
190 });
191});
192
193describe('#setCenter', () => {
194 // Choose initial zoom to avoid center being constrained by mercator latitude limits.
195 const camera = createCamera({zoom: 1});
196
197 test('sets center', () => {
198 camera.setCenter([1, 2]);
199 expect(camera.getCenter()).toEqual({lng: 1, lat: 2});
200 });
201
202 test('throws on invalid center argument', () => {
203 expect(() => {
204 camera.jumpTo({center: 1});
205 }).toThrow(Error);
206 });
207
208 test('emits move events, preserving eventData', done => {
209 let started, moved, ended;
210 const eventData = {data: 'ok'};
211
212 camera.on('movestart', (d) => { started = d.data; })
213 .on('move', (d) => { moved = d.data; })
214 .on('moveend', (d) => { ended = d.data; });
215
216 camera.setCenter([10, 20], eventData);
217 expect(started).toBe('ok');
218 expect(moved).toBe('ok');
219 expect(ended).toBe('ok');
220 done();
221 });
222
223 test('cancels in-progress easing', () => {
224 camera.panTo([3, 4]);
225 expect(camera.isEasing()).toBeTruthy();
226 camera.setCenter([1, 2]);
227 expect(!camera.isEasing()).toBeTruthy();
228 });
229});
230
231describe('#setZoom', () => {
232 const camera = createCamera();
233
234 test('sets zoom', () => {
235 camera.setZoom(3);
236 expect(camera.getZoom()).toBe(3);
237 });
238
239 test('emits move and zoom events, preserving eventData', done => {
240 let movestarted, moved, moveended, zoomstarted, zoomed, zoomended;
241 const eventData = {data: 'ok'};
242
243 camera
244 .on('movestart', (d) => { movestarted = d.data; })
245 .on('move', (d) => { moved = d.data; })
246 .on('moveend', (d) => { moveended = d.data; })
247 .on('zoomstart', (d) => { zoomstarted = d.data; })
248 .on('zoom', (d) => { zoomed = d.data; })
249 .on('zoomend', (d) => { zoomended = d.data; });
250
251 camera.setZoom(4, eventData);
252 expect(movestarted).toBe('ok');
253 expect(moved).toBe('ok');
254 expect(moveended).toBe('ok');
255 expect(zoomstarted).toBe('ok');
256 expect(zoomed).toBe('ok');
257 expect(zoomended).toBe('ok');
258 done();
259 });
260
261 test('cancels in-progress easing', () => {
262 camera.panTo([3, 4]);
263 expect(camera.isEasing()).toBeTruthy();
264 camera.setZoom(5);
265 expect(!camera.isEasing()).toBeTruthy();
266 });
267});
268
269describe('#setBearing', () => {
270 const camera = createCamera();
271
272 test('sets bearing', () => {
273 camera.setBearing(4);
274 expect(camera.getBearing()).toBe(4);
275 });
276
277 test('emits move and rotate events, preserving eventData', done => {
278 let movestarted, moved, moveended, rotatestarted, rotated, rotateended;
279 const eventData = {data: 'ok'};
280
281 camera
282 .on('movestart', (d) => { movestarted = d.data; })
283 .on('move', (d) => { moved = d.data; })
284 .on('moveend', (d) => { moveended = d.data; })
285 .on('rotatestart', (d) => { rotatestarted = d.data; })
286 .on('rotate', (d) => { rotated = d.data; })
287 .on('rotateend', (d) => { rotateended = d.data; });
288
289 camera.setBearing(5, eventData);
290 expect(movestarted).toBe('ok');
291 expect(moved).toBe('ok');
292 expect(moveended).toBe('ok');
293 expect(rotatestarted).toBe('ok');
294 expect(rotated).toBe('ok');
295 expect(rotateended).toBe('ok');
296 done();
297 });
298
299 test('cancels in-progress easing', () => {
300 camera.panTo([3, 4]);
301 expect(camera.isEasing()).toBeTruthy();
302 camera.setBearing(6);
303 expect(!camera.isEasing()).toBeTruthy();
304 });
305});
306
307describe('#setPadding', () => {
308 test('sets padding', () => {
309 const camera = createCamera();
310 const padding = {left: 300, top: 100, right: 50, bottom: 10};
311 camera.setPadding(padding);
312 expect(camera.getPadding()).toEqual(padding);
313 });
314
315 test('existing padding is retained if no new values are passed in', () => {
316 const camera = createCamera();
317 const padding = {left: 300, top: 100, right: 50, bottom: 10};
318 camera.setPadding(padding);
319 camera.setPadding({});
320
321 const currentPadding = camera.getPadding();
322 expect(currentPadding).toEqual(padding);
323 });
324
325 test('doesnt change padding thats already present if new value isnt passed in', () => {
326 const camera = createCamera();
327 const padding = {left: 300, top: 100, right: 50, bottom: 10};
328 camera.setPadding(padding);
329 const padding1 = {right: 100};
330 camera.setPadding(padding1);
331
332 const currentPadding = camera.getPadding();
333 expect(currentPadding.left).toBe(padding.left);
334 expect(currentPadding.top).toBe(padding.top);
335 // padding1 here
336 expect(currentPadding.right).toBe(padding1.right);
337 expect(currentPadding.bottom).toBe(padding.bottom);
338 });
339});
340
341describe('#panBy', () => {
342 test('pans by specified amount', () => {
343 const camera = createCamera();
344 camera.panBy([100, 0], {duration: 0});
345 expect(fixedLngLat(camera.getCenter())).toEqual({lng: 70.3125, lat: 0});
346 });
347
348 test('pans relative to viewport on a rotated camera', () => {
349 const camera = createCamera({bearing: 180});
350 camera.panBy([100, 0], {duration: 0});
351 expect(fixedLngLat(camera.getCenter())).toEqual({lng: -70.3125, lat: 0});
352 });
353
354 test('emits move events, preserving eventData', done => {
355 const camera = createCamera();
356 let started, moved;
357 const eventData = {data: 'ok'};
358
359 camera
360 .on('movestart', (d) => { started = d.data; })
361 .on('move', (d) => { moved = d.data; })
362 .on('moveend', (d) => {
363 expect(started).toBe('ok');
364 expect(moved).toBe('ok');
365 expect(d.data).toBe('ok');
366 done();
367 });
368
369 camera.panBy([100, 0], {duration: 0}, eventData);
370 });
371
372 test('supresses movestart if noMoveStart option is true', done => {
373 const camera = createCamera();
374 let started;
375
376 // fire once in advance to satisfy assertions that moveend only comes after movestart
377 camera.fire('movestart');
378
379 camera
380 .on('movestart', () => { started = true; })
381 .on('moveend', () => {
382 expect(!started).toBeTruthy();
383 done();
384 });
385
386 camera.panBy([100, 0], {duration: 0, noMoveStart: true});
387 });
388});
389
390describe('#panTo', () => {
391 test('pans to specified location', () => {
392 const camera = createCamera();
393 camera.panTo([100, 0], {duration: 0});
394 expect(camera.getCenter()).toEqual({lng: 100, lat: 0});
395 });
396
397 test('throws on invalid center argument', () => {
398 const camera = createCamera();
399 expect(() => {
400 camera.panTo({center: 1});
401 }).toThrow(Error);
402 });
403
404 test('pans with specified offset', () => {
405 const camera = createCamera();
406 camera.panTo([100, 0], {offset: [100, 0], duration: 0});
407 expect(fixedLngLat(camera.getCenter())).toEqual({lng: 29.6875, lat: 0});
408 });
409
410 test('pans with specified offset relative to viewport on a rotated camera', () => {
411 const camera = createCamera({bearing: 180});
412 camera.panTo([100, 0], {offset: [100, 0], duration: 0});
413 expect(fixedLngLat(camera.getCenter())).toEqual({lng: 170.3125, lat: 0});
414 });
415
416 test('emits move events, preserving eventData', done => {
417 const camera = createCamera();
418 let started, moved;
419 const eventData = {data: 'ok'};
420
421 camera
422 .on('movestart', (d) => { started = d.data; })
423 .on('move', (d) => { moved = d.data; })
424 .on('moveend', (d) => {
425 expect(started).toBe('ok');
426 expect(moved).toBe('ok');
427 expect(d.data).toBe('ok');
428 done();
429 });
430
431 camera.panTo([100, 0], {duration: 0}, eventData);
432 });
433
434 test('supresses movestart if noMoveStart option is true', done => {
435 const camera = createCamera();
436 let started;
437
438 // fire once in advance to satisfy assertions that moveend only comes after movestart
439 camera.fire('movestart');
440
441 camera
442 .on('movestart', () => { started = true; })
443 .on('moveend', () => {
444 expect(!started).toBeTruthy();
445 done();
446 });
447
448 camera.panTo([100, 0], {duration: 0, noMoveStart: true});
449 });
450});
451
452describe('#zoomTo', () => {
453 test('zooms to specified level', () => {
454 const camera = createCamera();
455 camera.zoomTo(3.2, {duration: 0});
456 expect(camera.getZoom()).toBe(3.2);
457 });
458
459 test('zooms around specified location', () => {
460 const camera = createCamera();
461 camera.zoomTo(3.2, {around: [5, 0], duration: 0});
462 expect(camera.getZoom()).toBe(3.2);
463 expect(fixedLngLat(camera.getCenter())).toEqual(fixedLngLat({lng: 4.455905897939886, lat: 0}));
464 });
465
466 test('zooms with specified offset', () => {
467 const camera = createCamera();
468 camera.zoomTo(3.2, {offset: [100, 0], duration: 0});
469 expect(camera.getZoom()).toBe(3.2);
470 expect(fixedLngLat(camera.getCenter())).toEqual(fixedLngLat({lng: 62.66117668978015, lat: 0}));
471 });
472
473 test('zooms with specified offset relative to viewport on a rotated camera', () => {
474 const camera = createCamera({bearing: 180});
475 camera.zoomTo(3.2, {offset: [100, 0], duration: 0});
476 expect(camera.getZoom()).toBe(3.2);
477 expect(fixedLngLat(camera.getCenter())).toEqual(fixedLngLat({lng: -62.66117668978012, lat: 0}));
478 });
479
480 test('emits move and zoom events, preserving eventData', done => {
481 const camera = createCamera();
482 let movestarted, moved, zoomstarted, zoomed;
483 const eventData = {data: 'ok'};
484
485 expect.assertions(6);
486
487 camera
488 .on('movestart', (d) => { movestarted = d.data; })
489 .on('move', (d) => { moved = d.data; })
490 .on('moveend', (d) => {
491 expect(movestarted).toBe('ok');
492 expect(moved).toBe('ok');
493 expect(d.data).toBe('ok');
494 });
495
496 camera
497 .on('zoomstart', (d) => { zoomstarted = d.data; })
498 .on('zoom', (d) => { zoomed = d.data; })
499 .on('zoomend', (d) => {
500 expect(zoomstarted).toBe('ok');
501 expect(zoomed).toBe('ok');
502 expect(d.data).toBe('ok');
503 });
504
505 camera.zoomTo(5, {duration: 0}, eventData);
506 done();
507 });
508});
509
510describe('#rotateTo', () => {
511 test('rotates to specified bearing', () => {
512 const camera = createCamera();
513 camera.rotateTo(90, {duration: 0});
514 expect(camera.getBearing()).toBe(90);
515 });
516
517 test('rotates around specified location', () => {
518 const camera = createCamera({zoom: 3});
519 camera.rotateTo(90, {around: [5, 0], duration: 0});
520 expect(camera.getBearing()).toBe(90);
521 expect(fixedLngLat(camera.getCenter())).toEqual(fixedLngLat({lng: 4.999999999999972, lat: 4.993665859353271}));
522 });
523
524 test('rotates around specified location, constrained to fit the view', () => {
525 const camera = createCamera({zoom: 0});
526 camera.rotateTo(90, {around: [5, 0], duration: 0});
527 expect(camera.getBearing()).toBe(90);
528 expect(fixedLngLat(camera.getCenter())).toEqual(fixedLngLat({lng: 4.999999999999972, lat: 0.000002552471840999715}));
529 });
530
531 test('rotates with specified offset', () => {
532 const camera = createCamera({zoom: 1});
533 camera.rotateTo(90, {offset: [200, 0], duration: 0});
534 expect(camera.getBearing()).toBe(90);
535 expect(fixedLngLat(camera.getCenter())).toEqual(fixedLngLat({lng: 70.3125, lat: 57.3265212252}));
536 });
537
538 test('rotates with specified offset, constrained to fit the view', () => {
539 const camera = createCamera({zoom: 0});
540 camera.rotateTo(90, {offset: [100, 0], duration: 0});
541 expect(camera.getBearing()).toBe(90);
542 expect(fixedLngLat(camera.getCenter())).toEqual(fixedLngLat({lng: 70.3125, lat: 0.000002552471840999715}));
543 });
544
545 test('rotates with specified offset relative to viewport on a rotated camera', () => {
546 const camera = createCamera({bearing: 180, zoom: 1});
547 camera.rotateTo(90, {offset: [200, 0], duration: 0});
548 expect(camera.getBearing()).toBe(90);
549 expect(fixedLngLat(camera.getCenter())).toEqual(fixedLngLat({lng: -70.3125, lat: 57.3265212252}));
550 });
551
552 test('emits move and rotate events, preserving eventData', done => {
553 const camera = createCamera();
554 let movestarted, moved, rotatestarted, rotated;
555 const eventData = {data: 'ok'};
556
557 expect.assertions(6);
558
559 camera
560 .on('movestart', (d) => { movestarted = d.data; })
561 .on('move', (d) => { moved = d.data; })
562 .on('moveend', (d) => {
563 expect(movestarted).toBe('ok');
564 expect(moved).toBe('ok');
565 expect(d.data).toBe('ok');
566 });
567
568 camera
569 .on('rotatestart', (d) => { rotatestarted = d.data; })
570 .on('rotate', (d) => { rotated = d.data; })
571 .on('rotateend', (d) => {
572 expect(rotatestarted).toBe('ok');
573 expect(rotated).toBe('ok');
574 expect(d.data).toBe('ok');
575 });
576
577 camera.rotateTo(90, {duration: 0}, eventData);
578 done();
579 });
580});
581
582describe('#easeTo', () => {
583 test('pans to specified location', () => {
584 const camera = createCamera();
585 camera.easeTo({center: [100, 0], duration: 0});
586 expect(camera.getCenter()).toEqual({lng: 100, lat: 0});
587 });
588
589 test('zooms to specified level', () => {
590 const camera = createCamera();
591 camera.easeTo({zoom: 3.2, duration: 0});
592 expect(camera.getZoom()).toBe(3.2);
593 });
594
595 test('rotates to specified bearing', () => {
596 const camera = createCamera();
597 camera.easeTo({bearing: 90, duration: 0});
598 expect(camera.getBearing()).toBe(90);
599 });
600
601 test('pitches to specified pitch', () => {
602 const camera = createCamera();
603 camera.easeTo({pitch: 45, duration: 0});
604 expect(camera.getPitch()).toBe(45);
605 });
606
607 test('pans and zooms', () => {
608 const camera = createCamera();
609 camera.easeTo({center: [100, 0], zoom: 3.2, duration: 0});
610 expect(fixedLngLat(camera.getCenter())).toEqual(fixedLngLat({lng: 100, lat: 0}));
611 expect(camera.getZoom()).toBe(3.2);
612 });
613
614 test('zooms around a point', () => {
615 const camera = createCamera();
616 camera.easeTo({around: [100, 0], zoom: 3, duration: 0});
617 expect(fixedLngLat(camera.getCenter())).toEqual(fixedLngLat({lng: 87.5, lat: 0}));
618 expect(camera.getZoom()).toBe(3);
619 });
620
621 test('pans and rotates', () => {
622 const camera = createCamera();
623 camera.easeTo({center: [100, 0], bearing: 90, duration: 0});
624 expect(camera.getCenter()).toEqual({lng: 100, lat: 0});
625 expect(camera.getBearing()).toBe(90);
626 });
627
628 test('zooms and rotates', () => {
629 const camera = createCamera();
630 camera.easeTo({zoom: 3.2, bearing: 90, duration: 0});
631 expect(camera.getZoom()).toBe(3.2);
632 expect(camera.getBearing()).toBe(90);
633 });
634
635 test('pans, zooms, and rotates', () => {
636 const camera = createCamera({bearing: -90});
637 camera.easeTo({center: [100, 0], zoom: 3.2, bearing: 90, duration: 0});
638 expect(fixedLngLat(camera.getCenter())).toEqual(fixedLngLat({lng: 100, lat: 0}));
639 expect(camera.getZoom()).toBe(3.2);
640 expect(camera.getBearing()).toBe(90);
641 });
642
643 test('noop', () => {
644 const camera = createCamera();
645 camera.easeTo({duration: 0});
646 expect(fixedLngLat(camera.getCenter())).toEqual({lng: 0, lat: 0});
647 expect(camera.getZoom()).toBe(0);
648 expect(camera.getBearing()).toBeCloseTo(0);
649 });
650
651 test('noop with offset', () => {
652 const camera = createCamera();
653 camera.easeTo({offset: [100, 0], duration: 0});
654 expect(fixedLngLat(camera.getCenter())).toEqual({lng: 0, lat: 0});
655 expect(camera.getZoom()).toBe(0);
656 expect(camera.getBearing()).toBeCloseTo(0);
657 });
658
659 test('pans with specified offset', () => {
660 const camera = createCamera();
661 camera.easeTo({center: [100, 0], offset: [100, 0], duration: 0});
662 expect(fixedLngLat(camera.getCenter())).toEqual({lng: 29.6875, lat: 0});
663 });
664
665 test('pans with specified offset relative to viewport on a rotated camera', () => {
666 const camera = createCamera({bearing: 180});
667 camera.easeTo({center: [100, 0], offset: [100, 0], duration: 0});
668 expect(fixedLngLat(camera.getCenter())).toEqual({lng: 170.3125, lat: 0});
669 });
670
671 test('zooms with specified offset', () => {
672 const camera = createCamera();
673 camera.easeTo({zoom: 3.2, offset: [100, 0], duration: 0});
674 expect(camera.getZoom()).toBe(3.2);
675 expect(fixedLngLat(camera.getCenter())).toEqual(fixedLngLat({lng: 62.66117668978015, lat: 0}));
676 });
677
678 test('zooms with specified offset relative to viewport on a rotated camera', () => {
679 const camera = createCamera({bearing: 180});
680 camera.easeTo({zoom: 3.2, offset: [100, 0], duration: 0});
681 expect(camera.getZoom()).toBe(3.2);
682 expect(fixedLngLat(camera.getCenter())).toEqual(fixedLngLat({lng: -62.66117668978012, lat: 0}));
683 });
684
685 test('rotates with specified offset', () => {
686 const camera = createCamera();
687 camera.easeTo({bearing: 90, offset: [100, 0], duration: 0});
688 expect(camera.getBearing()).toBe(90);
689 expect(fixedLngLat(camera.getCenter())).toEqual(fixedLngLat({lng: 70.3125, lat: 0.000002552471840999715}));
690 });
691
692 test('rotates with specified offset relative to viewport on a rotated camera', () => {
693 const camera = createCamera({bearing: 180});
694 camera.easeTo({bearing: 90, offset: [100, 0], duration: 0});
695 expect(camera.getBearing()).toBe(90);
696 expect(fixedLngLat(camera.getCenter())).toEqual(fixedLngLat({lng: -70.3125, lat: 0.000002552471840999715}));
697 });
698
699 test('emits move, zoom, rotate, and pitch events, preserving eventData', done => {
700 const camera = createCamera();
701 let movestarted, moved, zoomstarted, zoomed, rotatestarted, rotated, pitchstarted, pitched;
702 const eventData = {data: 'ok'};
703
704 expect.assertions(18);
705
706 camera
707 .on('movestart', (d) => { movestarted = d.data; })
708 .on('move', (d) => { moved = d.data; })
709 .on('moveend', (d) => {
710 expect(camera._zooming).toBeFalsy();
711 expect(camera._panning).toBeFalsy();
712 expect(camera._rotating).toBeFalsy();
713
714 expect(movestarted).toBe('ok');
715 expect(moved).toBe('ok');
716 expect(zoomed).toBe('ok');
717 expect(rotated).toBe('ok');
718 expect(pitched).toBe('ok');
719 expect(d.data).toBe('ok');
720 });
721
722 camera
723 .on('zoomstart', (d) => { zoomstarted = d.data; })
724 .on('zoom', (d) => { zoomed = d.data; })
725 .on('zoomend', (d) => {
726 expect(zoomstarted).toBe('ok');
727 expect(zoomed).toBe('ok');
728 expect(d.data).toBe('ok');
729 });
730
731 camera
732 .on('rotatestart', (d) => { rotatestarted = d.data; })
733 .on('rotate', (d) => { rotated = d.data; })
734 .on('rotateend', (d) => {
735 expect(rotatestarted).toBe('ok');
736 expect(rotated).toBe('ok');
737 expect(d.data).toBe('ok');
738 });
739
740 camera
741 .on('pitchstart', (d) => { pitchstarted = d.data; })
742 .on('pitch', (d) => { pitched = d.data; })
743 .on('pitchend', (d) => {
744 expect(pitchstarted).toBe('ok');
745 expect(pitched).toBe('ok');
746 expect(d.data).toBe('ok');
747 });
748
749 camera.easeTo(
750 {center: [100, 0], zoom: 3.2, bearing: 90, duration: 0, pitch: 45},
751 eventData);
752 done();
753 });
754
755 test('does not emit zoom events if not zooming', done => {
756 const camera = createCamera();
757
758 camera
759 .on('zoomstart', () => { done('zoomstart failed'); })
760 .on('zoom', () => { done('zoom failed'); })
761 .on('zoomend', () => { done('zoomend failed'); })
762 .on('moveend', () => { done(); });
763
764 camera.easeTo({center: [100, 0], duration: 0});
765 });
766
767 test('stops existing ease', () => {
768 const camera = createCamera();
769 camera.easeTo({center: [200, 0], duration: 100});
770 camera.easeTo({center: [100, 0], duration: 0});
771 expect(camera.getCenter()).toEqual({lng: 100, lat: 0});
772 });
773
774 test('can be called from within a moveend event handler', done => {
775 const camera = createCamera();
776 const stub = jest.spyOn(browser, 'now');
777
778 stub.mockImplementation(() => 0);
779 camera.easeTo({center: [100, 0], duration: 10});
780
781 camera.once('moveend', () => {
782 camera.easeTo({center: [200, 0], duration: 10});
783 camera.once('moveend', () => {
784 camera.easeTo({center: [300, 0], duration: 10});
785 camera.once('moveend', () => {
786 done();
787 });
788
789 setTimeout(() => {
790 stub.mockImplementation(() => 30);
791 camera.simulateFrame();
792 }, 0);
793 });
794
795 // setTimeout to avoid a synchronous callback
796 setTimeout(() => {
797 stub.mockImplementation(() => 20);
798 camera.simulateFrame();
799 }, 0);
800 });
801
802 // setTimeout to avoid a synchronous callback
803 setTimeout(() => {
804 stub.mockImplementation(() => 10);
805 camera.simulateFrame();
806 }, 0);
807 });
808
809 test('pans eastward across the antimeridian', done => {
810 const camera = createCamera();
811 const stub = jest.spyOn(browser, 'now');
812
813 camera.setCenter([170, 0]);
814 let crossedAntimeridian;
815
816 camera.on('move', () => {
817 if (camera.getCenter().lng > 170) {
818 crossedAntimeridian = true;
819 }
820 });
821
822 camera.on('moveend', () => {
823 expect(crossedAntimeridian).toBeTruthy();
824 done();
825 });
826
827 stub.mockImplementation(() => 0);
828 camera.easeTo({center: [-170, 0], duration: 10});
829
830 setTimeout(() => {
831 stub.mockImplementation(() => 1);
832 camera.simulateFrame();
833
834 setTimeout(() => {
835 stub.mockImplementation(() => 10);
836 camera.simulateFrame();
837 }, 0);
838 }, 0);
839 });
840
841 test('pans westward across the antimeridian', done => {
842 const camera = createCamera();
843 const stub = jest.spyOn(browser, 'now');
844
845 camera.setCenter([-170, 0]);
846 let crossedAntimeridian;
847
848 camera.on('move', () => {
849 if (camera.getCenter().lng < -170) {
850 crossedAntimeridian = true;
851 }
852 });
853
854 camera.on('moveend', () => {
855 expect(crossedAntimeridian).toBeTruthy();
856 done();
857 });
858
859 stub.mockImplementation(() => 0);
860 camera.easeTo({center: [170, 0], duration: 10});
861
862 setTimeout(() => {
863 stub.mockImplementation(() => 1);
864 camera.simulateFrame();
865
866 setTimeout(() => {
867 stub.mockImplementation(() => 10);
868 camera.simulateFrame();
869 }, 0);
870 }, 0);
871 });
872
873 test('animation occurs when prefers-reduced-motion: reduce is set but overridden by essential: true', done => {
874 const camera = createCamera();
875 Object.defineProperty(browser, 'prefersReducedMotion', {value: true});
876 const stubNow = jest.spyOn(browser, 'now');
877
878 // camera transition expected to take in this range when prefersReducedMotion is set and essential: true,
879 // when a duration of 200 is requested
880 const min = 100;
881 const max = 300;
882
883 let startTime;
884 camera
885 .on('movestart', () => { startTime = browser.now(); })
886 .on('moveend', () => {
887 const endTime = browser.now();
888 const timeDiff = endTime - startTime;
889 expect(timeDiff >= min && timeDiff < max).toBeTruthy();
890 done();
891 });
892
893 setTimeout(() => {
894 stubNow.mockImplementation(() => 0);
895 camera.simulateFrame();
896
897 camera.easeTo({center: [100, 0], zoom: 3.2, bearing: 90, duration: 200, essential: true});
898
899 setTimeout(() => {
900 stubNow.mockImplementation(() => 200);
901 camera.simulateFrame();
902 }, 0);
903 }, 0);
904 });
905
906 test('duration is 0 when prefers-reduced-motion: reduce is set', done => {
907 const camera = createCamera();
908 Object.defineProperty(browser, 'prefersReducedMotion', {value: true});
909 assertTransitionTime(done, camera, 0, 10);
910 camera.easeTo({center: [100, 0], zoom: 3.2, bearing: 90, duration: 1000});
911 });
912});
913
914describe('#flyTo', () => {
915 test('pans to specified location', () => {
916 const camera = createCamera();
917 camera.flyTo({center: [100, 0], animate: false});
918 expect(fixedLngLat(camera.getCenter())).toEqual({lng: 100, lat: 0});
919 });
920
921 test('throws on invalid center argument', () => {
922 const camera = createCamera();
923 expect(() => {
924 camera.flyTo({center: 1});
925 }).toThrow(Error);
926 });
927
928 test('does not throw when cameras current zoom is sufficiently greater than passed zoom option', () => {
929 const camera = createCamera({zoom: 22, center:[0, 0]});
930 expect(() => camera.flyTo({zoom:10, center:[0, 0]})).not.toThrow();
931 });
932
933 test('does not throw when cameras current zoom is above maxzoom and an offset creates infinite zoom out factor', () => {
934 const transform = new Transform(0, 20.9999, 0, 60, true);
935 transform.resize(512, 512);
936 const camera = attachSimulateFrame(new CameraMock(transform, {} as any))
937 .jumpTo({zoom: 21, center:[0, 0]});
938 camera._update = () => {};
939 expect(() => camera.flyTo({zoom:7.5, center:[0, 0], offset:[0, 70]})).not.toThrow();
940 });
941
942 test('zooms to specified level', () => {
943 const camera = createCamera();
944 camera.flyTo({zoom: 3.2, animate: false});
945 expect(fixedNum(camera.getZoom())).toBe(3.2);
946 });
947
948 test('zooms to integer level without floating point errors', () => {
949 const camera = createCamera({zoom: 0.6});
950 camera.flyTo({zoom: 2, animate: false});
951 expect(camera.getZoom()).toBe(2);
952 });
953
954 test('Zoom out from the same position to the same position with animation', done => {
955 const pos = {lng: 0, lat: 0};
956 const camera = createCamera({zoom: 20, center: pos});
957 const stub = jest.spyOn(browser, 'now');
958
959 camera.once('zoomend', () => {
960 expect(fixedLngLat(camera.getCenter())).toEqual(fixedLngLat(pos));
961 expect(camera.getZoom()).toBe(19);
962 done();
963 });
964
965 stub.mockImplementation(() => 0);
966 camera.flyTo({zoom: 19, center: pos, duration: 2});
967
968 stub.mockImplementation(() => 3);
969 camera.simulateFrame();
970 });
971
972 test('rotates to specified bearing', () => {
973 const camera = createCamera();
974 camera.flyTo({bearing: 90, animate: false});
975 expect(camera.getBearing()).toBe(90);
976 });
977
978 test('tilts to specified pitch', () => {
979 const camera = createCamera();
980 camera.flyTo({pitch: 45, animate: false});
981 expect(camera.getPitch()).toBe(45);
982 });
983
984 test('pans and zooms', () => {
985 const camera = createCamera();
986 camera.flyTo({center: [100, 0], zoom: 3.2, animate: false});
987 expect(fixedLngLat(camera.getCenter())).toEqual({lng: 100, lat: 0});
988 expect(fixedNum(camera.getZoom())).toBe(3.2);
989 });
990
991 test('pans and rotates', () => {
992 const camera = createCamera();
993 camera.flyTo({center: [100, 0], bearing: 90, animate: false});
994 expect(fixedLngLat(camera.getCenter())).toEqual({lng: 100, lat: 0});
995 expect(camera.getBearing()).toBe(90);
996 });
997
998 test('zooms and rotates', () => {
999 const camera = createCamera();
1000 camera.flyTo({zoom: 3.2, bearing: 90, animate: false});
1001 expect(fixedNum(camera.getZoom())).toBe(3.2);
1002 expect(camera.getBearing()).toBe(90);
1003 });
1004
1005 test('pans, zooms, and rotates', () => {
1006 const camera = createCamera();
1007 camera.flyTo({center: [100, 0], zoom: 3.2, bearing: 90, duration: 0, animate: false});
1008 expect(fixedLngLat(camera.getCenter())).toEqual({lng: 100, lat: 0});
1009 expect(fixedNum(camera.getZoom())).toBe(3.2);
1010 expect(camera.getBearing()).toBe(90);
1011 });
1012
1013 test('noop', () => {
1014 const camera = createCamera();
1015 camera.flyTo({animate: false});
1016 expect(fixedLngLat(camera.getCenter())).toEqual({lng: 0, lat: 0});
1017 expect(camera.getZoom()).toBe(0);
1018 expect(camera.getBearing()).toBeCloseTo(0);
1019 });
1020
1021 test('noop with offset', () => {
1022 const camera = createCamera();
1023 camera.flyTo({offset: [100, 0], animate: false});
1024 expect(fixedLngLat(camera.getCenter())).toEqual({lng: 0, lat: 0});
1025 expect(camera.getZoom()).toBe(0);
1026 expect(camera.getBearing()).toBeCloseTo(0);
1027 });
1028
1029 test('pans with specified offset', () => {
1030 const camera = createCamera();
1031 camera.flyTo({center: [100, 0], offset: [100, 0], animate: false});
1032 expect(fixedLngLat(camera.getCenter())).toEqual({lng: 29.6875, lat: 0});
1033 });
1034
1035 test('pans with specified offset relative to viewport on a rotated camera', () => {
1036 const camera = createCamera({bearing: 180});
1037 camera.easeTo({center: [100, 0], offset: [100, 0], animate: false});
1038 expect(fixedLngLat(camera.getCenter())).toEqual({lng: 170.3125, lat: 0});
1039 });
1040
1041 test('emits move, zoom, rotate, and pitch events, preserving eventData', done => {
1042 expect.assertions(18);
1043
1044 const camera = createCamera();
1045 let movestarted, moved, zoomstarted, zoomed, rotatestarted, rotated, pitchstarted, pitched;
1046 const eventData = {data: 'ok'};
1047
1048 camera
1049 .on('movestart', (d) => { movestarted = d.data; })
1050 .on('move', (d) => { moved = d.data; })
1051 .on('rotate', (d) => { rotated = d.data; })
1052 .on('pitch', (d) => { pitched = d.data; })
1053 .on('moveend', function(d) {
1054 expect(this._zooming).toBeFalsy();
1055 expect(this._panning).toBeFalsy();
1056 expect(this._rotating).toBeFalsy();
1057
1058 expect(movestarted).toBe('ok');
1059 expect(moved).toBe('ok');
1060 expect(zoomed).toBe('ok');
1061 expect(rotated).toBe('ok');
1062 expect(pitched).toBe('ok');
1063 expect(d.data).toBe('ok');
1064 });
1065
1066 camera
1067 .on('zoomstart', (d) => { zoomstarted = d.data; })
1068 .on('zoom', (d) => { zoomed = d.data; })
1069 .on('zoomend', (d) => {
1070 expect(zoomstarted).toBe('ok');
1071 expect(zoomed).toBe('ok');
1072 expect(d.data).toBe('ok');
1073 });
1074
1075 camera
1076 .on('rotatestart', (d) => { rotatestarted = d.data; })
1077 .on('rotate', (d) => { rotated = d.data; })
1078 .on('rotateend', (d) => {
1079 expect(rotatestarted).toBe('ok');
1080 expect(rotated).toBe('ok');
1081 expect(d.data).toBe('ok');
1082 });
1083
1084 camera
1085 .on('pitchstart', (d) => { pitchstarted = d.data; })
1086 .on('pitch', (d) => { pitched = d.data; })
1087 .on('pitchend', (d) => {
1088 expect(pitchstarted).toBe('ok');
1089 expect(pitched).toBe('ok');
1090 expect(d.data).toBe('ok');
1091 });
1092
1093 camera.flyTo(
1094 {center: [100, 0], zoom: 3.2, bearing: 90, duration: 0, pitch: 45, animate: false},
1095 eventData);
1096 done();
1097 });
1098
1099 test('for short flights, emits (solely) move events, preserving eventData', done => {
1100 //As I type this, the code path for guiding super-short flights is (and will probably remain) different.
1101 //As such; it deserves a separate test case. This test case flies the map from A to A.
1102 const camera = createCamera({center: [100, 0]});
1103 let movestarted, moved,
1104 zoomstarted, zoomed, zoomended,
1105 rotatestarted, rotated, rotateended,
1106 pitchstarted, pitched, pitchended;
1107 const eventData = {data: 'ok'};
1108
1109 camera
1110 .on('movestart', (d) => { movestarted = d.data; })
1111 .on('move', (d) => { moved = d.data; })
1112 .on('zoomstart', (d) => { zoomstarted = d.data; })
1113 .on('zoom', (d) => { zoomed = d.data; })
1114 .on('zoomend', (d) => { zoomended = d.data; })
1115 .on('rotatestart', (d) => { rotatestarted = d.data; })
1116 .on('rotate', (d) => { rotated = d.data; })
1117 .on('rotateend', (d) => { rotateended = d.data; })
1118 .on('pitchstart', (d) => { pitchstarted = d.data; })
1119 .on('pitch', (d) => { pitched = d.data; })
1120 .on('pitchend', (d) => { pitchended = d.data; })
1121 .on('moveend', function(d) {
1122 expect(this._zooming).toBeFalsy();
1123 expect(this._panning).toBeFalsy();
1124 expect(this._rotating).toBeFalsy();
1125
1126 expect(movestarted).toBe('ok');
1127 expect(moved).toBe('ok');
1128 expect(zoomstarted).toBeUndefined();
1129 expect(zoomed).toBeUndefined();
1130 expect(zoomended).toBeUndefined();
1131 expect(rotatestarted).toBeUndefined();
1132 expect(rotated).toBeUndefined();
1133 expect(rotateended).toBeUndefined();
1134 expect(pitched).toBeUndefined();
1135 expect(pitchstarted).toBeUndefined();
1136 expect(pitchended).toBeUndefined();
1137 expect(d.data).toBe('ok');
1138 done();
1139 });
1140
1141 const stub = jest.spyOn(browser, 'now');
1142 stub.mockImplementation(() => 0);
1143
1144 camera.flyTo({center: [100, 0], duration: 10}, eventData);
1145
1146 setTimeout(() => {
1147 stub.mockImplementation(() => 1);
1148 camera.simulateFrame();
1149
1150 setTimeout(() => {
1151 stub.mockImplementation(() => 10);
1152 camera.simulateFrame();
1153 }, 0);
1154 }, 0);
1155 });
1156
1157 test('stops existing ease', done => {
1158 const camera = createCamera();
1159 camera.flyTo({center: [200, 0], duration: 100});
1160 camera.flyTo({center: [100, 0], duration: 0});
1161 expect(fixedLngLat(camera.getCenter())).toEqual({lng: 100, lat: 0});
1162 done();
1163 });
1164
1165 test('can be called from within a moveend event handler', done => {
1166 const camera = createCamera();
1167 const stub = jest.spyOn(browser, 'now');
1168 stub.mockImplementation(() => 0);
1169
1170 camera.flyTo({center: [100, 0], duration: 10});
1171 camera.once('moveend', () => {
1172 camera.flyTo({center: [200, 0], duration: 10});
1173 camera.once('moveend', () => {
1174 camera.flyTo({center: [300, 0], duration: 10});
1175 camera.once('moveend', () => {
1176 done();
1177 });
1178 });
1179 });
1180
1181 setTimeout(() => {
1182 stub.mockImplementation(() => 10);
1183 camera.simulateFrame();
1184
1185 setTimeout(() => {
1186 stub.mockImplementation(() => 20);
1187 camera.simulateFrame();
1188
1189 setTimeout(() => {
1190 stub.mockImplementation(() => 30);
1191 camera.simulateFrame();
1192 }, 0);
1193 }, 0);
1194 }, 0);
1195 });
1196
1197 test('ascends', done => {
1198 const camera = createCamera();
1199 camera.setZoom(18);
1200 let ascended;
1201
1202 camera.on('zoom', () => {
1203 if (camera.getZoom() < 18) {
1204 ascended = true;
1205 }
1206 });
1207
1208 camera.on('moveend', () => {
1209 expect(ascended).toBeTruthy();
1210 done();
1211 });
1212
1213 const stub = jest.spyOn(browser, 'now');
1214 stub.mockImplementation(() => 0);
1215
1216 camera.flyTo({center: [100, 0], zoom: 18, duration: 10});
1217
1218 setTimeout(() => {
1219 stub.mockImplementation(() => 1);
1220 camera.simulateFrame();
1221
1222 setTimeout(() => {
1223 stub.mockImplementation(() => 10);
1224 camera.simulateFrame();
1225 }, 0);
1226 }, 0);
1227 });
1228
1229 test('pans eastward across the prime meridian', done => {
1230 const camera = createCamera();
1231 const stub = jest.spyOn(browser, 'now');
1232
1233 camera.setCenter([-10, 0]);
1234 let crossedPrimeMeridian;
1235
1236 camera.on('move', () => {
1237 if (Math.abs(camera.getCenter().lng) < 10) {
1238 crossedPrimeMeridian = true;
1239 }
1240 });
1241
1242 camera.on('moveend', () => {
1243 expect(crossedPrimeMeridian).toBeTruthy();
1244 done();
1245 });
1246
1247 stub.mockImplementation(() => 0);
1248 camera.flyTo({center: [10, 0], duration: 20});
1249
1250 setTimeout(() => {
1251 stub.mockImplementation(() => 1);
1252 camera.simulateFrame();
1253
1254 setTimeout(() => {
1255 stub.mockImplementation(() => 20);
1256 camera.simulateFrame();
1257 }, 0);
1258 }, 0);
1259 });
1260
1261 test('pans westward across the prime meridian', done => {
1262 const camera = createCamera();
1263 const stub = jest.spyOn(browser, 'now');
1264
1265 camera.setCenter([10, 0]);
1266 let crossedPrimeMeridian;
1267
1268 camera.on('move', () => {
1269 if (Math.abs(camera.getCenter().lng) < 10) {
1270 crossedPrimeMeridian = true;
1271 }
1272 });
1273
1274 camera.on('moveend', () => {
1275 expect(crossedPrimeMeridian).toBeTruthy();
1276 done();
1277 });
1278
1279 stub.mockImplementation(() => 0);
1280 camera.flyTo({center: [-10, 0], duration: 20});
1281
1282 setTimeout(() => {
1283 stub.mockImplementation(() => 1);
1284 camera.simulateFrame();
1285
1286 setTimeout(() => {
1287 stub.mockImplementation(() => 20);
1288 camera.simulateFrame();
1289 }, 0);
1290 }, 0);
1291 });
1292
1293 test('pans eastward across the antimeridian', done => {
1294 const camera = createCamera();
1295 const stub = jest.spyOn(browser, 'now');
1296
1297 camera.setCenter([170, 0]);
1298 let crossedAntimeridian;
1299
1300 camera.on('move', () => {
1301 if (camera.getCenter().lng > 170) {
1302 crossedAntimeridian = true;
1303 }
1304 });
1305
1306 camera.on('moveend', () => {
1307 expect(crossedAntimeridian).toBeTruthy();
1308 done();
1309 });
1310
1311 stub.mockImplementation(() => 0);
1312 camera.flyTo({center: [-170, 0], duration: 20});
1313
1314 setTimeout(() => {
1315 stub.mockImplementation(() => 1);
1316 camera.simulateFrame();
1317
1318 setTimeout(() => {
1319 stub.mockImplementation(() => 20);
1320 camera.simulateFrame();
1321 }, 0);
1322 }, 0);
1323 });
1324
1325 test('pans westward across the antimeridian', done => {
1326 const camera = createCamera();
1327 const stub = jest.spyOn(browser, 'now');
1328
1329 camera.setCenter([-170, 0]);
1330 let crossedAntimeridian;
1331
1332 camera.on('move', () => {
1333 if (camera.getCenter().lng < -170) {
1334 crossedAntimeridian = true;
1335 }
1336 });
1337
1338 camera.on('moveend', () => {
1339 expect(crossedAntimeridian).toBeTruthy();
1340 done();
1341 });
1342
1343 stub.mockImplementation(() => 0);
1344 camera.flyTo({center: [170, 0], duration: 10});
1345
1346 setTimeout(() => {
1347 stub.mockImplementation(() => 1);
1348 camera.simulateFrame();
1349
1350 setTimeout(() => {
1351 stub.mockImplementation(() => 10);
1352 camera.simulateFrame();
1353 }, 0);
1354 }, 0);
1355 });
1356
1357 test('does not pan eastward across the antimeridian if no world copies', done => {
1358 const camera = createCamera({renderWorldCopies: false});
1359 const stub = jest.spyOn(browser, 'now');
1360
1361 camera.setCenter([170, 0]);
1362 let crossedAntimeridian;
1363
1364 camera.on('move', () => {
1365 if (camera.getCenter().lng > 170) {
1366 crossedAntimeridian = true;
1367 }
1368 });
1369
1370 camera.on('moveend', () => {
1371 expect(crossedAntimeridian).toBeFalsy();
1372 done();
1373 });
1374
1375 stub.mockImplementation(() => 0);
1376 camera.flyTo({center: [-170, 0], duration: 10});
1377
1378 setTimeout(() => {
1379 stub.mockImplementation(() => 1);
1380 camera.simulateFrame();
1381
1382 setTimeout(() => {
1383 stub.mockImplementation(() => 10);
1384 camera.simulateFrame();
1385 }, 0);
1386 }, 0);
1387 });
1388
1389 test('does not pan westward across the antimeridian if no world copies', done => {
1390 const camera = createCamera({renderWorldCopies: false});
1391 const stub = jest.spyOn(browser, 'now');
1392
1393 camera.setCenter([-170, 0]);
1394 let crossedAntimeridian;
1395
1396 camera.on('move', () => {
1397 if (fixedLngLat(camera.getCenter(), 10).lng < -170) {
1398 crossedAntimeridian = true;
1399 }
1400 });
1401
1402 camera.on('moveend', () => {
1403 expect(crossedAntimeridian).toBeFalsy();
1404 done();
1405 });
1406
1407 stub.mockImplementation(() => 0);
1408 camera.flyTo({center: [170, 0], duration: 10});
1409
1410 setTimeout(() => {
1411 stub.mockImplementation(() => 1);
1412 camera.simulateFrame();
1413
1414 setTimeout(() => {
1415 stub.mockImplementation(() => 10);
1416 camera.simulateFrame();
1417 }, 0);
1418 }, 0);
1419 });
1420
1421 test('jumps back to world 0 when crossing the antimeridian', done => {
1422 const camera = createCamera();
1423 const stub = jest.spyOn(browser, 'now');
1424
1425 camera.setCenter([-170, 0]);
1426
1427 let leftWorld0 = false;
1428
1429 camera.on('move', () => {
1430 leftWorld0 = leftWorld0 || (camera.getCenter().lng < -180);
1431 });
1432
1433 camera.on('moveend', () => {
1434 expect(leftWorld0).toBeFalsy();
1435 done();
1436 });
1437
1438 stub.mockImplementation(() => 0);
1439 camera.flyTo({center: [170, 0], duration: 10});
1440
1441 setTimeout(() => {
1442 stub.mockImplementation(() => 1);
1443 camera.simulateFrame();
1444
1445 setTimeout(() => {
1446 stub.mockImplementation(() => 10);
1447 camera.simulateFrame();
1448 }, 0);
1449 }, 0);
1450 });
1451
1452 test('peaks at the specified zoom level', done => {
1453 const camera = createCamera({zoom: 20});
1454 const stub = jest.spyOn(browser, 'now');
1455
1456 const minZoom = 1;
1457 let zoomed = false;
1458
1459 camera.on('zoom', () => {
1460 const zoom = camera.getZoom();
1461 if (zoom < 1) {
1462 fail(`${zoom} should be >= ${minZoom} during flyTo`);
1463 }
1464
1465 if (camera.getZoom() < (minZoom + 1)) {
1466 zoomed = true;
1467 }
1468 });
1469
1470 camera.on('moveend', () => {
1471 expect(zoomed).toBeTruthy();
1472 done();
1473 });
1474
1475 stub.mockImplementation(() => 0);
1476 camera.flyTo({center: [1, 0], zoom: 20, minZoom, duration: 10});
1477
1478 setTimeout(() => {
1479 stub.mockImplementation(() => 3);
1480 camera.simulateFrame();
1481
1482 setTimeout(() => {
1483 stub.mockImplementation(() => 10);
1484 camera.simulateFrame();
1485 }, 0);
1486 }, 0);
1487 });
1488
1489 test('respects transform\'s maxZoom', done => {
1490 const transform = new Transform(2, 10, 0, 60, false);
1491 transform.resize(512, 512);
1492
1493 const camera = attachSimulateFrame(new CameraMock(transform, {} as any));
1494 camera._update = () => {};
1495
1496 camera.on('moveend', () => {
1497 expect(camera.getZoom()).toBeCloseTo(10);
1498 const {lng, lat} = camera.getCenter();
1499 expect(lng).toBeCloseTo(12);
1500 expect(lat).toBeCloseTo(34);
1501 done();
1502 });
1503
1504 const stub = jest.spyOn(browser, 'now');
1505 stub.mockImplementation(() => 0);
1506 camera.flyTo({center: [12, 34], zoom: 30, duration: 10});
1507
1508 setTimeout(() => {
1509 stub.mockImplementation(() => 10);
1510 camera.simulateFrame();
1511 }, 0);
1512 });
1513
1514 test('respects transform\'s minZoom', done => {
1515 const transform = new Transform(2, 10, 0, 60, false);
1516 transform.resize(512, 512);
1517
1518 const camera = attachSimulateFrame(new CameraMock(transform, {} as any));
1519 camera._update = () => {};
1520
1521 camera.on('moveend', () => {
1522 expect(camera.getZoom()).toBeCloseTo(2);
1523 const {lng, lat} = camera.getCenter();
1524 expect(lng).toBeCloseTo(12);
1525 expect(lat).toBeCloseTo(34);
1526 done();
1527 });
1528
1529 const stub = jest.spyOn(browser, 'now');
1530 stub.mockImplementation(() => 0);
1531 camera.flyTo({center: [12, 34], zoom: 1, duration: 10});
1532
1533 setTimeout(() => {
1534 stub.mockImplementation(() => 10);
1535 camera.simulateFrame();
1536 }, 0);
1537 });
1538
1539 test('resets duration to 0 if it exceeds maxDuration', done => {
1540 let startTime, endTime, timeDiff;
1541 const camera = createCamera({center: [37.63454, 55.75868], zoom: 18});
1542
1543 camera
1544 .on('movestart', () => { startTime = new Date(); })
1545 .on('moveend', () => {
1546 endTime = new Date();
1547 timeDiff = endTime - startTime;
1548 expect(timeDiff).toBeLessThan(30);
1549 done();
1550 });
1551
1552 camera.flyTo({center: [-122.3998631, 37.7884307], maxDuration: 100});
1553 });
1554
1555 test('flys instantly when prefers-reduce-motion:reduce is set', done => {
1556 const camera = createCamera();
1557 Object.defineProperty(browser, 'prefersReducedMotion', {value: true});
1558 assertTransitionTime(done, camera, 0, 10);
1559 camera.flyTo({center: [100, 0], bearing: 90, animate: true});
1560 });
1561});
1562
1563describe('#isEasing', () => {
1564 test('returns false when not easing', () => {
1565 const camera = createCamera();
1566 expect(!camera.isEasing()).toBeTruthy();
1567 });
1568
1569 test('returns true when panning', () => {
1570 const camera = createCamera();
1571 camera.panTo([100, 0], {duration: 1});
1572 expect(camera.isEasing()).toBeTruthy();
1573 });
1574
1575 test('returns false when done panning', done => {
1576 const camera = createCamera();
1577 camera.on('moveend', () => {
1578 expect(!camera.isEasing()).toBeTruthy();
1579 done();
1580 });
1581 const stub = jest.spyOn(browser, 'now');
1582 stub.mockImplementation(() => 0);
1583 camera.panTo([100, 0], {duration: 1});
1584 setTimeout(() => {
1585 stub.mockImplementation(() => 1);
1586 camera.simulateFrame();
1587 }, 0);
1588 });
1589
1590 test('returns true when zooming', () => {
1591 const camera = createCamera();
1592 camera.zoomTo(3.2, {duration: 1});
1593
1594 expect(camera.isEasing()).toBeTruthy();
1595 });
1596
1597 test('returns false when done zooming', done => {
1598 const camera = createCamera();
1599 camera.on('moveend', () => {
1600 expect(!camera.isEasing()).toBeTruthy();
1601 done();
1602 });
1603 const stub = jest.spyOn(browser, 'now');
1604 stub.mockImplementation(() => 0);
1605 camera.zoomTo(3.2, {duration: 1});
1606 setTimeout(() => {
1607 stub.mockImplementation(() => 1);
1608 camera.simulateFrame();
1609 }, 0);
1610 });
1611
1612 test('returns true when rotating', () => {
1613 const camera = createCamera();
1614 camera.rotateTo(90, {duration: 1});
1615 expect(camera.isEasing()).toBeTruthy();
1616 });
1617
1618 test('returns false when done rotating', done => {
1619 const camera = createCamera();
1620 camera.on('moveend', () => {
1621 expect(!camera.isEasing()).toBeTruthy();
1622 done();
1623 });
1624 const stub = jest.spyOn(browser, 'now');
1625 stub.mockImplementation(() => 0);
1626 camera.rotateTo(90, {duration: 1});
1627 setTimeout(() => {
1628 stub.mockImplementation(() => 1);
1629 camera.simulateFrame();
1630 }, 0);
1631 });
1632});
1633
1634describe('#stop', () => {
1635 test('resets camera._zooming', () => {
1636 const camera = createCamera();
1637 camera.zoomTo(3.2);
1638 camera.stop();
1639 expect(!camera._zooming).toBeTruthy();
1640 });
1641
1642 test('resets camera._rotating', () => {
1643 const camera = createCamera();
1644 camera.rotateTo(90);
1645 camera.stop();
1646 expect(!camera._rotating).toBeTruthy();
1647 });
1648
1649 test('emits moveend if panning, preserving eventData', done => {
1650 const camera = createCamera();
1651 const eventData = {data: 'ok'};
1652
1653 camera.on('moveend', (d) => {
1654 expect(d.data).toBe('ok');
1655 done();
1656 });
1657
1658 camera.panTo([100, 0], {}, eventData);
1659 camera.stop();
1660 });
1661
1662 test('emits moveend if zooming, preserving eventData', done => {
1663 const camera = createCamera();
1664 const eventData = {data: 'ok'};
1665
1666 camera.on('moveend', (d) => {
1667 expect(d.data).toBe('ok');
1668 done();
1669 });
1670
1671 camera.zoomTo(3.2, {}, eventData);
1672 camera.stop();
1673 });
1674
1675 test('emits moveend if rotating, preserving eventData', done => {
1676 const camera = createCamera();
1677 const eventData = {data: 'ok'};
1678
1679 camera.on('moveend', (d) => {
1680 expect(d.data).toBe('ok');
1681 done();
1682 });
1683
1684 camera.rotateTo(90, {}, eventData);
1685 camera.stop();
1686 });
1687
1688 test('does not emit moveend if not moving', done => {
1689 const camera = createCamera();
1690 const eventData = {data: 'ok'};
1691
1692 camera.on('moveend', (d) => {
1693 expect(d.data).toBe('ok');
1694 camera.stop();
1695 done(); // Fails with ".end() called twice" if we get here a second time.
1696 });
1697
1698 const stub = jest.spyOn(browser, 'now');
1699 stub.mockImplementation(() => 0);
1700 camera.panTo([100, 0], {duration: 1}, eventData);
1701
1702 setTimeout(() => {
1703 stub.mockImplementation(() => 1);
1704 camera.simulateFrame();
1705 }, 0);
1706 });
1707});
1708
1709describe('#cameraForBounds', () => {
1710 test('no options passed', () => {
1711 const camera = createCamera();
1712 const bb = [[-133, 16], [-68, 50]];
1713 const transform = camera.cameraForBounds(bb);
1714
1715 expect(fixedLngLat(transform.center, 4)).toEqual({lng: -100.5, lat: 34.7171});
1716 expect(fixedNum(transform.zoom, 3)).toBe(2.469);
1717 });
1718
1719 test('bearing positive number', () => {
1720 const camera = createCamera();
1721 const bb = [[-133, 16], [-68, 50]];
1722 const transform = camera.cameraForBounds(bb, {bearing: 175});
1723
1724 expect(fixedLngLat(transform.center, 4)).toEqual({lng: -100.5, lat: 34.7171});
1725 expect(fixedNum(transform.zoom, 3)).toBe(2.558);
1726 expect(transform.bearing).toBe(175);
1727 });
1728
1729 test('bearing negative number', () => {
1730 const camera = createCamera();
1731 const bb = [[-133, 16], [-68, 50]];
1732 const transform = camera.cameraForBounds(bb, {bearing: -30});
1733
1734 expect(fixedLngLat(transform.center, 4)).toEqual({lng: -100.5, lat: 34.7171});
1735 expect(fixedNum(transform.zoom, 3)).toBe(2.392);
1736 expect(transform.bearing).toBe(-30);
1737 });
1738
1739 test('padding number', () => {
1740 const camera = createCamera();
1741 const bb = [[-133, 16], [-68, 50]];
1742 const transform = camera.cameraForBounds(bb, {padding: 15});
1743
1744 expect(fixedLngLat(transform.center, 4)).toEqual({lng: -100.5, lat: 34.7171});
1745 expect(fixedNum(transform.zoom, 3)).toBe(2.382);
1746 });
1747
1748 test('padding object', () => {
1749 const camera = createCamera();
1750 const bb = [[-133, 16], [-68, 50]];
1751 const transform = camera.cameraForBounds(bb, {padding: {top: 15, right: 15, bottom: 15, left: 15}, duration: 0});
1752
1753 expect(fixedLngLat(transform.center, 4)).toEqual({lng: -100.5, lat: 34.7171});
1754 });
1755
1756 test('asymmetrical padding', () => {
1757 const camera = createCamera();
1758 const bb = [[-133, 16], [-68, 50]];
1759 const transform = camera.cameraForBounds(bb, {padding: {top: 10, right: 75, bottom: 50, left: 25}, duration: 0});
1760
1761 expect(fixedLngLat(transform.center, 4)).toEqual({lng: -96.5558, lat: 32.0833});
1762 });
1763
1764 test('bearing and asymmetrical padding', () => {
1765 const camera = createCamera();
1766 const bb = [[-133, 16], [-68, 50]];
1767 const transform = camera.cameraForBounds(bb, {bearing: 90, padding: {top: 10, right: 75, bottom: 50, left: 25}, duration: 0});
1768
1769 expect(fixedLngLat(transform.center, 4)).toEqual({lng: -103.3761, lat: 31.7099});
1770 });
1771
1772 test('offset', () => {
1773 const camera = createCamera();
1774 const bb = [[-133, 16], [-68, 50]];
1775 const transform = camera.cameraForBounds(bb, {offset: [0, 100]});
1776
1777 expect(fixedLngLat(transform.center, 4)).toEqual({lng: -100.5, lat: 44.4717});
1778 });
1779
1780 test('offset and padding', () => {
1781 const camera = createCamera();
1782 const bb = [[-133, 16], [-68, 50]];
1783 const transform = camera.cameraForBounds(bb, {padding: {top: 10, right: 75, bottom: 50, left: 25}, offset: [0, 100]});
1784
1785 expect(fixedLngLat(transform.center, 4)).toEqual({lng: -96.5558, lat: 44.4189});
1786 });
1787
1788 test('bearing, asymmetrical padding, and offset', () => {
1789 const camera = createCamera();
1790 const bb = [[-133, 16], [-68, 50]];
1791 const transform = camera.cameraForBounds(bb, {bearing: 90, padding: {top: 10, right: 75, bottom: 50, left: 25}, offset: [0, 100], duration: 0});
1792
1793 expect(fixedLngLat(transform.center, 4)).toEqual({lng: -103.3761, lat: 43.0929});
1794 });
1795});
1796
1797describe('#fitBounds', () => {
1798 test('no padding passed', () => {
1799 const camera = createCamera();
1800 const bb = [[-133, 16], [-68, 50]];
1801 camera.fitBounds(bb, {duration:0});
1802
1803 expect(fixedLngLat(camera.getCenter(), 4)).toEqual({lng: -100.5, lat: 34.7171});
1804 expect(fixedNum(camera.getZoom(), 3)).toBe(2.469);
1805 });
1806
1807 test('padding number', () => {
1808 const camera = createCamera();
1809 const bb = [[-133, 16], [-68, 50]];
1810 camera.fitBounds(bb, {padding: 15, duration:0});
1811
1812 expect(fixedLngLat(camera.getCenter(), 4)).toEqual({lng: -100.5, lat: 34.7171});
1813 expect(fixedNum(camera.getZoom(), 3)).toBe(2.382);
1814 });
1815
1816 test('padding object', () => {
1817 const camera = createCamera();
1818 const bb = [[-133, 16], [-68, 50]];
1819 camera.fitBounds(bb, {padding: {top: 10, right: 75, bottom: 50, left: 25}, duration:0});
1820
1821 expect(fixedLngLat(camera.getCenter(), 4)).toEqual({lng: -96.5558, lat: 32.0833});
1822 });
1823
1824 test('padding does not get propagated to transform.padding', () => {
1825 const camera = createCamera();
1826 const bb = [[-133, 16], [-68, 50]];
1827 camera.fitBounds(bb, {padding: {top: 10, right: 75, bottom: 50, left: 25}, duration:0});
1828 const padding = camera.transform.padding;
1829
1830 expect(padding).toEqual({
1831 left: 0,
1832 right: 0,
1833 top: 0,
1834 bottom: 0
1835 });
1836 });
1837});
1838
1839describe('#fitScreenCoordinates', () => {
1840 test('bearing 225', () => {
1841 const camera = createCamera();
1842 const p0 = [128, 128];
1843 const p1 = [256, 256];
1844 const bearing = 225;
1845 camera.fitScreenCoordinates(p0, p1, bearing, {duration:0});
1846
1847 expect(fixedLngLat(camera.getCenter(), 4)).toEqual({lng: -45, lat: 40.9799});
1848 expect(fixedNum(camera.getZoom(), 3)).toBe(1.5);
1849 expect(camera.getBearing()).toBe(-135);
1850 });
1851
1852 test('bearing 0', () => {
1853 const camera = createCamera();
1854 const p0 = [128, 128];
1855 const p1 = [256, 256];
1856 const bearing = 0;
1857 camera.fitScreenCoordinates(p0, p1, bearing, {duration:0});
1858
1859 expect(fixedLngLat(camera.getCenter(), 4)).toEqual({lng: -45, lat: 40.9799});
1860 expect(fixedNum(camera.getZoom(), 3)).toBe(2);
1861 expect(camera.getBearing()).toBeCloseTo(0);
1862 });
1863
1864 test('inverted points', () => {
1865 const camera = createCamera();
1866 const p1 = [128, 128];
1867 const p0 = [256, 256];
1868 const bearing = 0;
1869 camera.fitScreenCoordinates(p0, p1, bearing, {duration:0});
1870
1871 expect(fixedLngLat(camera.getCenter(), 4)).toEqual({lng: -45, lat: 40.9799});
1872 expect(fixedNum(camera.getZoom(), 3)).toBe(2);
1873 expect(camera.getBearing()).toBeCloseTo(0);
1874 });
1875});