最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How do I mock the IntersectionObserver API in Jest? - Stack Overflow

programmeradmin2浏览0评论

I've read all of the relevant questions on this topic, and I realize this will probably be marked as a duplicate, but I simply cannot for the life of me figure out how to get this working.

I have this simple function that lazily loads elements:

export default function lazyLoad(targets, onIntersection) {
  const observer = new IntersectionObserver((entries, self) => {
    entries.forEach((entry) => {
      if (entry.isIntersecting) {
        onIntersection(entry.target);
        self.unobserve(entry.target);
      }
    });
  });

  document.querySelectorAll(targets).forEach((target) => observer.observe(target));
  return observer;
}

Example usage:

lazyLoad('.lazy-img', (img) => {
  const pictureElement = img.parentElement;
  const source = pictureElement.querySelector('.lazy-source');

  source.srcset = source.getAttribute('data-srcset');
  img.src = img.getAttribute('data-src');
});

Now, I'm trying to test the lazyLoad function using jest, but I obviously need to mock IntersectionObserver since it's a browser API, not a native JavaScript one.

The following works for testing the observe method:

let observe;
let unobserve;

beforeEach(() => {
  observe = jest.fn();
  unobserve = jest.fn();

  window.IntersectionObserver = jest.fn(() => ({
    observe,
    unobserve,
  }));
});

describe('lazyLoad utility', () => {
  it('calls observe on each target', () => {
    for (let i = 0; i < 3; i++) {
      const target = document.createElement('img');
      target.className = 'lazy-img';
      document.body.appendChild(target);
    }

    lazyLoad(
      '.lazy-img',
      jest.fn(() => {})
    );

    expect(observe).toHaveBeenCalledTimes(3);
  });
});

But I also want to test the .isIntersecting logic, where the callback fires... Except I don't know how to do that. How can I test intersections with jest?

I've read all of the relevant questions on this topic, and I realize this will probably be marked as a duplicate, but I simply cannot for the life of me figure out how to get this working.

I have this simple function that lazily loads elements:

export default function lazyLoad(targets, onIntersection) {
  const observer = new IntersectionObserver((entries, self) => {
    entries.forEach((entry) => {
      if (entry.isIntersecting) {
        onIntersection(entry.target);
        self.unobserve(entry.target);
      }
    });
  });

  document.querySelectorAll(targets).forEach((target) => observer.observe(target));
  return observer;
}

Example usage:

lazyLoad('.lazy-img', (img) => {
  const pictureElement = img.parentElement;
  const source = pictureElement.querySelector('.lazy-source');

  source.srcset = source.getAttribute('data-srcset');
  img.src = img.getAttribute('data-src');
});

Now, I'm trying to test the lazyLoad function using jest, but I obviously need to mock IntersectionObserver since it's a browser API, not a native JavaScript one.

The following works for testing the observe method:

let observe;
let unobserve;

beforeEach(() => {
  observe = jest.fn();
  unobserve = jest.fn();

  window.IntersectionObserver = jest.fn(() => ({
    observe,
    unobserve,
  }));
});

describe('lazyLoad utility', () => {
  it('calls observe on each target', () => {
    for (let i = 0; i < 3; i++) {
      const target = document.createElement('img');
      target.className = 'lazy-img';
      document.body.appendChild(target);
    }

    lazyLoad(
      '.lazy-img',
      jest.fn(() => {})
    );

    expect(observe).toHaveBeenCalledTimes(3);
  });
});

But I also want to test the .isIntersecting logic, where the callback fires... Except I don't know how to do that. How can I test intersections with jest?

Share Improve this question asked Oct 1, 2020 at 14:17 Aleksandr HovhannisyanAleksandr Hovhannisyan 1,62021 silver badges39 bronze badges 5
  • Pass it as an argument. – Adam Jenkins Commented Oct 1, 2020 at 14:19
  • @Adam Could you please clarify what you mean? What should I pass as an argument? Thanks! – Aleksandr Hovhannisyan Commented Oct 1, 2020 at 14:20
  • lazyLoad(targets,onIntersection,observer = IntersectionObserver) – Adam Jenkins Commented Oct 1, 2020 at 14:20
  • This could also be helpful – Adam Jenkins Commented Oct 1, 2020 at 14:23
  • 1 @Adam Thanks! Unfortunately, I already read that SO post and it did not help me. I'm mainly stuck on how to simulate the .isIntersecting check for an IntersectionObserver. – Aleksandr Hovhannisyan Commented Oct 1, 2020 at 14:26
Add a ment  | 

2 Answers 2

Reset to default 5

Mocking stuff is so easy when you pass it as an argument:

export default function lazyLoad(targets, onIntersection, observerClass = IntersectionObserver) {
  const observer = new observerClass(...)
  ...
}


// test file
let entries = [];
const observeFn = jest.fn();
const unobserveFn = jest.fn()
class MockObserver {
  constructor(fn) {
    fn(entries,this);
  }

  observe() { observeFn() }
  unobserve() { unobserveFn() }
}

test('...',() => {
   // set `entries` to be something so you can mock it
   entries = ...something
   lazyLoad('something',jest.fn(),MockObserver);
});

Another option is to use mockObserver mentioned above and mock in window.

window.IntersetionObserver = mockObserver

And you don't necessary need to pass observer in ponent props.

The important point to test if entries isIntesecting is to mock IntersectionObserver as class like mentioned above.

发布评论

评论列表(0)

  1. 暂无评论