import { create } from '~/lib/utils/intersection_observer';

describe('IntersectionObserver Utility', () => {
  beforeAll(() => {
    global.IntersectionObserver = class MockIntersectionObserver {
      constructor(callback) {
        this.callback = callback;

        this.entries = [];
      }

      addEntry(entry) {
        this.entries.push(entry);
      }

      trigger() {
        this.callback(this.entries);
      }
    };
  });
  describe('create', () => {
    describe('memoization', () => {
      const options = { rootMargin: '1px 1px 1px 1px' };
      let expectedOutput;

      beforeEach(() => {
        create.cache.clear();
        expectedOutput = create(options);
      });

      it('returns the same Observer for the same options input', () => {
        expect(expectedOutput.id).toBe(create(options).id);
      });

      it('creates a new Observer for unique input options', () => {
        expect(expectedOutput.id).not.toBe(create({ rootMargin: '1px 2px 3px 4px' }));
      });

      it('creates a new Observer for the same input options in different object references', () => {
        expect(expectedOutput.id).not.toBe(create({ rootMargin: '1px 1px 1px 1px' }));
      });
    });
  });

  describe('Observer behavior', () => {
    let observer = null;
    let id = null;

    beforeEach(() => {
      create.cache.clear();
      ({ observer, id } = create());
    });

    it.each`
      isIntersecting | event
      ${false}       | ${'IntersectionDisappear'}
      ${true}        | ${'IntersectionAppear'}
    `(
      'should emit the correct event on the entry target based on the computed Intersection',
      async ({ isIntersecting, event }) => {
        const target = document.createElement('div');
        observer.addEntry({ target, isIntersecting });

        target.addEventListener(event, (e) => {
          expect(e.detail.observer).toBe(id);
        });

        observer.trigger();
      },
    );

    it('should always emit an Update event with the entry and the observer', () => {
      const target = document.createElement('div');
      const entry = { target };

      observer.addEntry(entry);

      target.addEventListener('IntersectionUpdate', (e) => {
        expect(e.detail.observer).toBe(id);
        expect(e.detail.entry).toStrictEqual(entry);
      });

      observer.trigger();
    });
  });
});
