4 https://bugzilla.mozilla.org/show_bug.cgi?id=1243846
6 Some tests ported from IntersectionObserver/polyfill/intersection-observer-test.html
8 Original license header:
10 Copyright 2016 Google Inc. All Rights Reserved.
11 Licensed under the Apache License, Version 2.0 (the "License");
12 you may not use this file except in compliance with the License.
13 You may obtain a copy of the License at
14 http://www.apache.org/licenses/LICENSE-2.0
15 Unless required by applicable law or agreed to in writing, software
16 distributed under the License is distributed on an "AS IS" BASIS,
17 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 See the License for the specific language governing permissions and
19 limitations under the License.
22 <meta charset=
"utf-8">
23 <title>Test for Bug
1243846</title>
24 <script src=
"/tests/SimpleTest/SimpleTest.js"></script>
25 <link rel=
"stylesheet" type=
"text/css" href=
"/tests/SimpleTest/test.css"/>
27 <body onload=
"onLoad()">
28 <a target=
"_blank" href=
"https://bugzilla.mozilla.org/show_bug.cgi?id=1243846">Mozilla Bug
1243846</a>
31 <script type=
"application/javascript">
32 /* eslint
"no-shadow": [
"error", {
"allow": [
"done",
"next"]}] */
34 var curDescribeMsg = '';
37 function beforeEach_fn() { };
38 function afterEach_fn() { };
44 function beforeEach(fn) {
48 function afterEach(fn) {
52 function it(msg, fn) {
54 msg: `${msg} [${curDescribeMsg}]`,
60 function callDelayed(fn) {
64 requestAnimationFrame(function tick() {
65 var i = callbacks.length;
67 var cb = callbacks[i];
68 SimpleTest.executeSoon(function() { SimpleTest.executeSoon(cb) });
69 callbacks.splice(i,
1);
71 requestAnimationFrame(tick);
74 function expect(val) {
77 throwException (regexp) {
80 ok(false, `${curItMsg} - an exception should have beeen thrown`);
82 ok(regexp.test(e), `${curItMsg} - supplied regexp should match thrown exception`);
86 var fn = function (expected) {
87 is(val, expected, curItMsg);
92 fn.greaterThan = function (other) {
93 ok(val
> other, `${curItMsg} - ${val} should be greater than ${other}`);
95 fn.lessThan = function (other) {
96 ok(val < other, `${curItMsg} - ${val} should be less than ${other}`);
101 if (Array.isArray(expected)) {
102 if (!Array.isArray(val)) {
103 ok(false, curItMsg, `${curItMsg} - should be an array,`);
106 is(val.length, expected.length, curItMsg, `${curItMsg} - arrays should be the same length`);
107 if (expected.length != val.length) {
110 for (var i =
0; i < expected.length; i++) {
111 is(val[i], expected[i], `${curItMsg} - array elements at position ${i} should be equal`);
112 if (expected[i] != val[i]) {
123 function describe(msg, fn) {
124 curDescribeMsg = msg;
130 var test = tests.shift();
132 console.log(test.msg);
154 var fn = function () {
156 fn.lastCall = { args: arguments };
162 fn.lastCall = { args: [] };
163 fn.waitForNotification = (fn1) =
> {
170 var ASYNC_TIMEOUT =
300;
174 var noop = function() {};
177 // References to DOM elements, which are accessible to any test
178 // and reset prior to each test so state isn't shared.
189 describe('IntersectionObserver', function() {
196 beforeEach(function() {
202 afterEach(function() {
203 if (io && 'disconnect' in io) io.disconnect();
206 window.onmessage = null;
213 describe('constructor', function() {
215 it('throws when callback is not a function', function() {
217 io = new IntersectionObserver(null);
218 }).to.throwException(/.*/i);
222 it('instantiates root correctly', function() {
223 io = new IntersectionObserver(noop);
224 expect(io.root).to.be(null);
226 io = new IntersectionObserver(noop, {root: rootEl});
227 expect(io.root).to.be(rootEl);
231 it('throws when root is not an Element', function() {
233 io = new IntersectionObserver(noop, {root: 'foo'});
234 }).to.throwException(/.*/i);
238 it('instantiates rootMargin correctly', function() {
239 io = new IntersectionObserver(noop, {rootMargin: '
10px'});
240 expect(io.rootMargin).to.be('
10px
10px
10px
10px');
242 io = new IntersectionObserver(noop, {rootMargin: '
10px -
5%'});
243 expect(io.rootMargin).to.be('
10px -
5%
10px -
5%');
245 io = new IntersectionObserver(noop, {rootMargin: '
10px
20%
0px'});
246 expect(io.rootMargin).to.be('
10px
20%
0px
20%');
248 io = new IntersectionObserver(noop, {rootMargin: '
0px
0px -
5%
5px'});
249 expect(io.rootMargin).to.be('
0px
0px -
5%
5px');
253 it('throws when rootMargin is not in pixels or percent', function() {
255 io = new IntersectionObserver(noop, {rootMargin: 'auto'});
256 }).to.throwException(/pixels.*percent/i);
260 it('instantiates thresholds correctly', function() {
261 io = new IntersectionObserver(noop);
262 expect(io.thresholds).to.eql([
0]);
264 io = new IntersectionObserver(noop, {threshold:
0.5});
265 expect(io.thresholds).to.eql([
0.5]);
267 io = new IntersectionObserver(noop, {threshold: [
0.25,
0.5,
0.75]});
268 expect(io.thresholds).to.eql([
0.25,
0.5,
0.75]);
270 io = new IntersectionObserver(noop, {threshold: [
1,
.5,
0]});
271 expect(io.thresholds).to.eql([
0,
.5,
1]);
274 it('throws when a threshold value is not between
0 and
1', function() {
276 io = new IntersectionObserver(noop, {threshold: [
0, -
1]});
277 }).to.throwException(/threshold/i);
280 it('throws when a threshold value is not a number', function() {
282 io = new IntersectionObserver(noop, {threshold:
"foo"});
283 }).to.throwException(/.*/i);
289 describe('observe', function() {
291 it('throws when target is not an Element', function() {
293 io = new IntersectionObserver(noop);
295 }).to.throwException(/.*/i);
299 it('triggers if target intersects when observing begins', function(done) {
300 io = new IntersectionObserver(function(records) {
301 expect(records.length).to.be(
1);
302 expect(records[
0].intersectionRatio).to.be(
1);
305 io.observe(targetEl1);
309 it('triggers with the correct arguments', function(done) {
310 io = new IntersectionObserver(function(records, observer) {
311 expect(records.length).to.be(
1);
312 expect(records[
0] instanceof IntersectionObserverEntry).to.be.ok();
313 expect(observer).to.be(io);
314 expect(this).to.be(io);
317 io.observe(targetEl1);
321 it('does trigger if target does not intersect when observing begins',
324 var spy = sinon.spy();
325 io = new IntersectionObserver(spy, {root: rootEl});
327 targetEl2.style.top = '-
40px';
328 io.observe(targetEl2);
329 callDelayed(function() {
330 expect(spy.callCount).to.be(
1);
336 it('triggers if target or root becomes invisible',
339 var spy = sinon.spy();
340 io = new IntersectionObserver(spy, {root: rootEl});
344 io.observe(targetEl1);
345 spy.waitForNotification(function() {
346 expect(spy.callCount).to.be(
1);
347 var records = sortRecords(spy.lastCall.args[
0]);
348 expect(records.length).to.be(
1);
349 expect(records[
0].intersectionRatio).to.be(
1);
354 targetEl1.style.display = 'none';
355 spy.waitForNotification(function() {
356 expect(spy.callCount).to.be(
2);
357 var records = sortRecords(spy.lastCall.args[
0]);
358 expect(records.length).to.be(
1);
359 expect(records[
0].intersectionRatio).to.be(
0);
364 targetEl1.style.display = 'block';
365 spy.waitForNotification(function() {
366 expect(spy.callCount).to.be(
3);
367 var records = sortRecords(spy.lastCall.args[
0]);
368 expect(records.length).to.be(
1);
369 expect(records[
0].intersectionRatio).to.be(
1);
374 rootEl.style.display = 'none';
375 spy.waitForNotification(function() {
376 expect(spy.callCount).to.be(
4);
377 var records = sortRecords(spy.lastCall.args[
0]);
378 expect(records.length).to.be(
1);
379 expect(records[
0].intersectionRatio).to.be(
0);
384 rootEl.style.display = 'block';
385 spy.waitForNotification(function() {
386 expect(spy.callCount).to.be(
5);
387 var records = sortRecords(spy.lastCall.args[
0]);
388 expect(records.length).to.be(
1);
389 expect(records[
0].intersectionRatio).to.be(
1);
397 it('handles container elements with non-visible overflow',
400 var spy = sinon.spy();
401 io = new IntersectionObserver(spy, {root: rootEl});
405 io.observe(targetEl1);
406 spy.waitForNotification(function() {
407 expect(spy.callCount).to.be(
1);
408 var records = sortRecords(spy.lastCall.args[
0]);
409 expect(records.length).to.be(
1);
410 expect(records[
0].intersectionRatio).to.be(
1);
415 targetEl1.style.left = '-
40px';
416 spy.waitForNotification(function() {
417 expect(spy.callCount).to.be(
2);
418 var records = sortRecords(spy.lastCall.args[
0]);
419 expect(records.length).to.be(
1);
420 expect(records[
0].intersectionRatio).to.be(
0);
425 parentEl.style.overflow = 'visible';
426 spy.waitForNotification(function() {
427 expect(spy.callCount).to.be(
3);
428 var records = sortRecords(spy.lastCall.args[
0]);
429 expect(records.length).to.be(
1);
430 expect(records[
0].intersectionRatio).to.be(
1);
438 it('observes one target at a single threshold correctly', function(done) {
440 var spy = sinon.spy();
441 io = new IntersectionObserver(spy, {root: rootEl, threshold:
0.5});
445 targetEl1.style.left = '-
5px';
446 io.observe(targetEl1);
447 spy.waitForNotification(function() {
448 expect(spy.callCount).to.be(
1);
449 var records = sortRecords(spy.lastCall.args[
0]);
450 expect(records.length).to.be(
1);
451 expect(records[
0].intersectionRatio).to.be.greaterThan(
0.5);
456 targetEl1.style.left = '-
15px';
457 spy.waitForNotification(function() {
458 expect(spy.callCount).to.be(
2);
459 var records = sortRecords(spy.lastCall.args[
0]);
460 expect(records.length).to.be(
1);
461 expect(records[
0].intersectionRatio).to.be.lessThan(
0.5);
466 targetEl1.style.left = '-
25px';
467 callDelayed(function() {
468 expect(spy.callCount).to.be(
2);
473 targetEl1.style.left = '-
10px';
474 spy.waitForNotification(function() {
475 expect(spy.callCount).to.be(
3);
476 var records = sortRecords(spy.lastCall.args[
0]);
477 expect(records.length).to.be(
1);
478 expect(records[
0].intersectionRatio).to.be(
0.5);
487 it('observes multiple targets at multiple thresholds correctly',
490 var spy = sinon.spy();
491 io = new IntersectionObserver(spy, {
493 threshold: [
1,
0.5,
0]
498 targetEl1.style.top = '
0px';
499 targetEl1.style.left = '-
15px';
500 targetEl2.style.top = '-
5px';
501 targetEl2.style.left = '
0px';
502 targetEl3.style.top = '
0px';
503 targetEl3.style.left = '
205px';
504 io.observe(targetEl1);
505 io.observe(targetEl2);
506 io.observe(targetEl3);
507 spy.waitForNotification(function() {
508 expect(spy.callCount).to.be(
1);
509 var records = sortRecords(spy.lastCall.args[
0]);
510 expect(records.length).to.be(
3);
511 expect(records[
0].target).to.be(targetEl1);
512 expect(records[
0].intersectionRatio).to.be(
0.25);
513 expect(records[
1].target).to.be(targetEl2);
514 expect(records[
1].intersectionRatio).to.be(
0.75);
519 targetEl1.style.top = '
0px';
520 targetEl1.style.left = '-
5px';
521 targetEl2.style.top = '-
15px';
522 targetEl2.style.left = '
0px';
523 targetEl3.style.top = '
0px';
524 targetEl3.style.left = '
195px';
525 spy.waitForNotification(function() {
526 expect(spy.callCount).to.be(
2);
527 var records = sortRecords(spy.lastCall.args[
0]);
528 expect(records.length).to.be(
3);
529 expect(records[
0].target).to.be(targetEl1);
530 expect(records[
0].intersectionRatio).to.be(
0.75);
531 expect(records[
1].target).to.be(targetEl2);
532 expect(records[
1].intersectionRatio).to.be(
0.25);
533 expect(records[
2].target).to.be(targetEl3);
534 expect(records[
2].intersectionRatio).to.be(
0.25);
539 targetEl1.style.top = '
0px';
540 targetEl1.style.left = '
5px';
541 targetEl2.style.top = '-
25px';
542 targetEl2.style.left = '
0px';
543 targetEl3.style.top = '
0px';
544 targetEl3.style.left = '
185px';
545 spy.waitForNotification(function() {
546 expect(spy.callCount).to.be(
3);
547 var records = sortRecords(spy.lastCall.args[
0]);
548 expect(records.length).to.be(
3);
549 expect(records[
0].target).to.be(targetEl1);
550 expect(records[
0].intersectionRatio).to.be(
1);
551 expect(records[
1].target).to.be(targetEl2);
552 expect(records[
1].intersectionRatio).to.be(
0);
553 expect(records[
2].target).to.be(targetEl3);
554 expect(records[
2].intersectionRatio).to.be(
0.75);
559 targetEl1.style.top = '
0px';
560 targetEl1.style.left = '
15px';
561 targetEl2.style.top = '-
35px';
562 targetEl2.style.left = '
0px';
563 targetEl3.style.top = '
0px';
564 targetEl3.style.left = '
175px';
565 spy.waitForNotification(function() {
566 expect(spy.callCount).to.be(
4);
567 var records = sortRecords(spy.lastCall.args[
0]);
568 expect(records.length).to.be(
1);
569 expect(records[
0].target).to.be(targetEl3);
570 expect(records[
0].intersectionRatio).to.be(
1);
578 it('handles rootMargin properly', function(done) {
580 parentEl.style.overflow = 'visible';
581 targetEl1.style.top = '
0px';
582 targetEl1.style.left = '-
20px';
583 targetEl2.style.top = '-
20px';
584 targetEl2.style.left = '
0px';
585 targetEl3.style.top = '
0px';
586 targetEl3.style.left = '
200px';
587 targetEl4.style.top = '
180px';
588 targetEl4.style.left = '
180px';
592 io = new IntersectionObserver(function(records) {
593 records = sortRecords(records);
594 expect(records.length).to.be(
4);
595 expect(records[
0].target).to.be(targetEl1);
596 expect(records[
0].intersectionRatio).to.be(
1);
597 expect(records[
1].target).to.be(targetEl2);
598 expect(records[
1].intersectionRatio).to.be(
.5);
599 expect(records[
2].target).to.be(targetEl3);
600 expect(records[
2].intersectionRatio).to.be(
.5);
601 expect(records[
3].target).to.be(targetEl4);
602 expect(records[
3].intersectionRatio).to.be(
1);
605 }, {root: rootEl, rootMargin: '
10px'});
607 io.observe(targetEl1);
608 io.observe(targetEl2);
609 io.observe(targetEl3);
610 io.observe(targetEl4);
613 io = new IntersectionObserver(function(records) {
614 records = sortRecords(records);
615 expect(records.length).to.be(
4);
616 expect(records[
0].target).to.be(targetEl1);
617 expect(records[
0].intersectionRatio).to.be(
0.5);
618 expect(records[
2].target).to.be(targetEl3);
619 expect(records[
2].intersectionRatio).to.be(
0.5);
620 expect(records[
3].target).to.be(targetEl4);
621 expect(records[
3].intersectionRatio).to.be(
0.5);
624 }, {root: rootEl, rootMargin: '-
10px
10%'});
626 io.observe(targetEl1);
627 io.observe(targetEl2);
628 io.observe(targetEl3);
629 io.observe(targetEl4);
632 io = new IntersectionObserver(function(records) {
633 records = sortRecords(records);
634 expect(records.length).to.be(
4);
635 expect(records[
0].target).to.be(targetEl1);
636 expect(records[
0].intersectionRatio).to.be(
0.5);
637 expect(records[
3].target).to.be(targetEl4);
638 expect(records[
3].intersectionRatio).to.be(
0.5);
641 }, {root: rootEl, rootMargin: '-
5% -
2.5%
0px'});
643 io.observe(targetEl1);
644 io.observe(targetEl2);
645 io.observe(targetEl3);
646 io.observe(targetEl4);
649 io = new IntersectionObserver(function(records) {
650 records = sortRecords(records);
651 expect(records.length).to.be(
4);
652 expect(records[
0].target).to.be(targetEl1);
653 expect(records[
0].intersectionRatio).to.be(
0.5);
654 expect(records[
1].target).to.be(targetEl2);
655 expect(records[
1].intersectionRatio).to.be(
0.5);
656 expect(records[
3].target).to.be(targetEl4);
657 expect(records[
3].intersectionRatio).to.be(
0.25);
660 }, {root: rootEl, rootMargin: '
5% -
2.5% -
10px -
190px'});
662 io.observe(targetEl1);
663 io.observe(targetEl2);
664 io.observe(targetEl3);
665 io.observe(targetEl4);
671 it('handles targets on the boundary of root', function(done) {
673 var spy = sinon.spy();
674 io = new IntersectionObserver(spy, {root: rootEl});
678 targetEl1.style.top = '
0px';
679 targetEl1.style.left = '-
21px';
680 targetEl2.style.top = '-
20px';
681 targetEl2.style.left = '
0px';
682 io.observe(targetEl1);
683 io.observe(targetEl2);
684 spy.waitForNotification(function() {
685 expect(spy.callCount).to.be(
1);
686 var records = sortRecords(spy.lastCall.args[
0]);
687 expect(records.length).to.be(
2);
688 expect(records[
1].intersectionRatio).to.be(
0);
689 expect(records[
1].target).to.be(targetEl2);
694 targetEl1.style.top = '
0px';
695 targetEl1.style.left = '-
20px';
696 targetEl2.style.top = '-
21px';
697 targetEl2.style.left = '
0px';
698 spy.waitForNotification(function() {
699 expect(spy.callCount).to.be(
2);
700 var records = sortRecords(spy.lastCall.args[
0]);
701 expect(records.length).to.be(
2);
702 expect(records[
0].intersectionRatio).to.be(
0);
703 expect(records[
0].isIntersecting).to.be.ok();
704 expect(records[
0].target).to.be(targetEl1);
705 expect(records[
1].intersectionRatio).to.be(
0);
706 expect(records[
1].target).to.be(targetEl2);
711 targetEl1.style.top = '-
20px';
712 targetEl1.style.left = '
200px';
713 targetEl2.style.top = '
200px';
714 targetEl2.style.left = '
200px';
715 spy.waitForNotification(function() {
716 expect(spy.callCount).to.be(
3);
717 var records = sortRecords(spy.lastCall.args[
0]);
718 expect(records.length).to.be(
1);
719 expect(records[
0].intersectionRatio).to.be(
0);
720 expect(records[
0].target).to.be(targetEl2);
725 targetEl3.style.top = '
20px';
726 targetEl3.style.left = '-
20px';
727 targetEl4.style.top = '-
20px';
728 targetEl4.style.left = '
20px';
729 io.observe(targetEl3);
730 io.observe(targetEl4);
731 spy.waitForNotification(function() {
732 expect(spy.callCount).to.be(
4);
733 var records = sortRecords(spy.lastCall.args[
0]);
734 expect(records.length).to.be(
2);
735 expect(records[
0].intersectionRatio).to.be(
0);
736 expect(records[
0].isIntersecting).to.be.ok();
737 expect(records[
0].target).to.be(targetEl3);
738 expect(records[
1].intersectionRatio).to.be(
0);
739 expect(records[
1].target).to.be(targetEl4);
748 it('handles zero-size targets within the root coordinate space',
751 var spy = sinon.spy();
752 io = new IntersectionObserver(spy, {root: rootEl});
756 targetEl1.style.top = '
0px';
757 targetEl1.style.left = '
0px';
758 targetEl1.style.width = '
0px';
759 targetEl1.style.height = '
0px';
760 io.observe(targetEl1);
761 spy.waitForNotification(function() {
762 var records = sortRecords(spy.lastCall.args[
0]);
763 expect(records.length).to.be(
1);
764 expect(records[
0].intersectionRatio).to.be(
1);
765 expect(records[
0].isIntersecting).to.be.ok();
770 targetEl1.style.top = '-
1px';
771 spy.waitForNotification(function() {
772 var records = sortRecords(spy.lastCall.args[
0]);
773 expect(records.length).to.be(
1);
774 expect(records[
0].intersectionRatio).to.be(
0);
775 expect(records[
0].isIntersecting).to.be(false);
783 it('handles root/target elements not yet in the DOM', function(done) {
788 var spy = sinon.spy();
789 io = new IntersectionObserver(spy, {root: rootEl});
793 io.observe(targetEl1);
797 document.getElementById('fixtures').appendChild(rootEl);
798 callDelayed(function() {
799 expect(spy.callCount).to.be(
1);
804 parentEl.insertBefore(targetEl1, targetEl2);
805 spy.waitForNotification(function() {
806 expect(spy.callCount).to.be(
2);
807 var records = sortRecords(spy.lastCall.args[
0]);
808 expect(records.length).to.be(
1);
809 expect(records[
0].intersectionRatio).to.be(
1);
810 expect(records[
0].target).to.be(targetEl1);
815 grandParentEl.remove();
816 spy.waitForNotification(function() {
817 expect(spy.callCount).to.be(
3);
818 var records = sortRecords(spy.lastCall.args[
0]);
819 expect(records.length).to.be(
1);
820 expect(records[
0].intersectionRatio).to.be(
0);
821 expect(records[
0].target).to.be(targetEl1);
826 rootEl.appendChild(targetEl1);
827 spy.waitForNotification(function() {
828 expect(spy.callCount).to.be(
4);
829 var records = sortRecords(spy.lastCall.args[
0]);
830 expect(records.length).to.be(
1);
831 expect(records[
0].intersectionRatio).to.be(
1);
832 expect(records[
0].target).to.be(targetEl1);
838 spy.waitForNotification(function() {
839 expect(spy.callCount).to.be(
5);
840 var records = sortRecords(spy.lastCall.args[
0]);
841 expect(records.length).to.be(
1);
842 expect(records[
0].intersectionRatio).to.be(
0);
843 expect(records[
0].target).to.be(targetEl1);
851 it('handles sub-root element scrolling', function(done) {
852 io = new IntersectionObserver(function(records) {
853 expect(records.length).to.be(
1);
854 expect(records[
0].intersectionRatio).to.be(
1);
858 io.observe(targetEl3);
859 callDelayed(function() {
860 parentEl.scrollLeft =
40;
865 it('supports CSS transitions and transforms', function(done) {
867 targetEl1.style.top = '
220px';
868 targetEl1.style.left = '
220px';
872 io = new IntersectionObserver(function(records) {
874 if (callCount <=
1) {
877 expect(records.length).to.be(
1);
878 expect(records[
0].intersectionRatio).to.be(
1);
880 }, {root: rootEl, threshold: [
1]});
882 io.observe(targetEl1);
883 callDelayed(function() {
884 targetEl1.style.transform = 'translateX(-
40px) translateY(-
40px)';
889 it('uses the viewport when no root is specified', function(done) {
890 window.onmessage = function (e) {
891 expect(e.data).to.be.ok();
896 var win = window.open(
"intersectionobserver_window.html");
899 it('triggers only once if observed multiple times (and does not crash when collected)', function(done) {
900 var spy = sinon.spy();
901 io = new IntersectionObserver(spy, {root: rootEl});
902 io.observe(targetEl1);
903 io.observe(targetEl1);
904 io.observe(targetEl1);
906 spy.waitForNotification(function() {
907 callDelayed(function () {
908 expect(spy.callCount).to.be(
1);
916 describe('observe subframe', function () {
918 it('boundingClientRect matches target.getBoundingClientRect() for an element inside an iframe',
921 io = new IntersectionObserver(function(records) {
922 expect(records.length).to.be(
1);
923 expect(records[
0].boundingClientRect.top, targetEl5.getBoundingClientRect().top);
924 expect(records[
0].boundingClientRect.left, targetEl5.getBoundingClientRect().left);
925 expect(records[
0].boundingClientRect.width, targetEl5.getBoundingClientRect().width);
926 expect(records[
0].boundingClientRect.height, targetEl5.getBoundingClientRect().height);
928 }, {threshold: [
1]});
930 targetEl4.onload = function () {
931 targetEl5 = targetEl4.contentDocument.getElementById('target5');
932 io.observe(targetEl5);
935 targetEl4.src =
"intersectionobserver_iframe.html";
938 it('rootBounds is set to null for cross-origin observations', function(done) {
940 window.onmessage = function (e) {
941 expect(e.data).to.be(true);
945 targetEl4.src =
"http://example.org/tests/dom/base/test/intersectionobserver_cross_domain_iframe.html";
951 describe('takeRecords', function() {
953 it('supports getting records before the callback is invoked', function(done) {
955 var lastestRecords = [];
956 io = new IntersectionObserver(function(records) {
957 lastestRecords = lastestRecords.concat(records);
959 io.observe(targetEl1);
961 window.requestAnimationFrame && requestAnimationFrame(function wait() {
962 lastestRecords = lastestRecords.concat(io.takeRecords());
963 if (!lastestRecords.length) {
964 requestAnimationFrame(wait);
967 callDelayed(function() {
968 expect(lastestRecords.length).to.be(
1);
969 expect(lastestRecords[
0].intersectionRatio).to.be(
1);
978 describe('unobserve', function() {
980 it('removes targets from the internal store', function(done) {
982 var spy = sinon.spy();
983 io = new IntersectionObserver(spy, {root: rootEl});
987 targetEl1.style.top = targetEl2.style.top = '
0px';
988 targetEl1.style.left = targetEl2.style.left = '
0px';
989 io.observe(targetEl1);
990 io.observe(targetEl2);
991 spy.waitForNotification(function() {
992 expect(spy.callCount).to.be(
1);
993 var records = sortRecords(spy.lastCall.args[
0]);
994 expect(records.length).to.be(
2);
995 expect(records[
0].target).to.be(targetEl1);
996 expect(records[
0].intersectionRatio).to.be(
1);
997 expect(records[
1].target).to.be(targetEl2);
998 expect(records[
1].intersectionRatio).to.be(
1);
1003 io.unobserve(targetEl1);
1004 targetEl1.style.top = targetEl2.style.top = '
0px';
1005 targetEl1.style.left = targetEl2.style.left = '-
40px';
1006 spy.waitForNotification(function() {
1007 expect(spy.callCount).to.be(
2);
1008 var records = sortRecords(spy.lastCall.args[
0]);
1009 expect(records.length).to.be(
1);
1010 expect(records[
0].target).to.be(targetEl2);
1011 expect(records[
0].intersectionRatio).to.be(
0);
1016 io.unobserve(targetEl2);
1017 targetEl1.style.top = targetEl2.style.top = '
0px';
1018 targetEl1.style.left = targetEl2.style.left = '
0px';
1019 callDelayed(function() {
1020 expect(spy.callCount).to.be(
2);
1030 describe('disconnect', function() {
1032 it('removes all targets and stops listening for changes', function(done) {
1034 var spy = sinon.spy();
1035 io = new IntersectionObserver(spy, {root: rootEl});
1039 targetEl1.style.top = targetEl2.style.top = '
0px';
1040 targetEl1.style.left = targetEl2.style.left = '
0px';
1041 io.observe(targetEl1);
1042 io.observe(targetEl2);
1043 spy.waitForNotification(function() {
1044 expect(spy.callCount).to.be(
1);
1045 var records = sortRecords(spy.lastCall.args[
0]);
1046 expect(records.length).to.be(
2);
1047 expect(records[
0].target).to.be(targetEl1);
1048 expect(records[
0].intersectionRatio).to.be(
1);
1049 expect(records[
1].target).to.be(targetEl2);
1050 expect(records[
1].intersectionRatio).to.be(
1);
1056 targetEl1.style.top = targetEl2.style.top = '
0px';
1057 targetEl1.style.left = targetEl2.style.left = '-
40px';
1058 callDelayed(function() {
1059 expect(spy.callCount).to.be(
1);
1073 * Runs a sequence of function and when finished invokes the done callback.
1074 * Each function in the sequence is invoked with its own done function and
1075 * it should call that function once it's complete.
1076 * @param {Array
<Function>} functions An array of async functions.
1077 * @param {Function} done A final callback to be invoked once all function
1080 function runSequence(functions, done) {
1081 var next = functions.shift();
1084 runSequence(functions, done);
1093 * Sorts an array of records alphebetically by ascending ID. Since the current
1094 * native implementation doesn't sort change entries by `observe` order, we do
1095 * that ourselves for the non-polyfill case. Since all tests call observe
1096 * on targets in sequential order, this should always match.
1097 * https://crbug.com/
613679
1098 * @param {Array
<IntersectionObserverEntry>} entries The entries to sort.
1099 * @return {Array
<IntersectionObserverEntry>} The sorted array.
1101 function sortRecords(entries) {
1102 entries = entries.sort(function(a, b) {
1103 return a.target.id < b.target.id ? -
1 :
1;
1110 * Adds the common styles used by all tests to the page.
1112 function addStyles() {
1113 var styles = document.createElement('style');
1114 styles.id = 'styles';
1115 document.documentElement.appendChild(styles);
1119 ' position: relative;' +
1122 ' background: #eee' +
1125 ' position: relative;' +
1130 ' position: absolute;' +
1133 ' overflow: hidden;' +
1136 ' background: #ddd;' +
1138 '#target1, #target2, #target3, #target4 {' +
1139 ' position: absolute;' +
1144 ' transform: translateX(
0px) translateY(
0px);' +
1145 ' transition: transform
.5s;' +
1146 ' background: #f00;' +
1150 styles.innerHTML = cssText;
1155 * Adds the DOM fixtures used by all tests to the page and assigns them to
1156 * global variables so they can be referenced within the tests.
1158 function addFixtures() {
1159 var fixtures = document.createElement('div');
1160 fixtures.id = 'fixtures';
1162 fixtures.innerHTML =
1164 '
<div id=
"grand-parent">' +
1165 '
<div id=
"parent">' +
1166 '
<div id=
"target1"></div>' +
1167 '
<div id=
"target2"></div>' +
1168 '
<div id=
"target3"></div>' +
1169 '
<iframe id=
"target4"></iframe>' +
1174 document.body.appendChild(fixtures);
1176 rootEl = document.getElementById('root');
1177 grandParentEl = document.getElementById('grand-parent');
1178 parentEl = document.getElementById('parent');
1179 targetEl1 = document.getElementById('target1');
1180 targetEl2 = document.getElementById('target2');
1181 targetEl3 = document.getElementById('target3');
1182 targetEl4 = document.getElementById('target4');
1187 * Removes the common styles from the page.
1189 function removeStyles() {
1190 var styles = document.getElementById('styles');
1196 * Removes the DOM fixtures from the page and resets the global references.
1198 function removeFixtures() {
1199 var fixtures = document.getElementById('fixtures');
1203 grandParentEl = null;
1212 SpecialPowers.pushPrefEnv({
"set": [[
"dom.IntersectionObserver.enabled", true]]}, next);
1215 SimpleTest.waitForExplicitFinish();