import { cleanup } from '@testing-library/react';

import {
  EventManager,
  isElementOverflowing,
  isVisibleInViewport,
  stringToColor,
  stringToHashCode,
  throttleEvent,
  truncateWithEllipsis
} from '.';

afterEach(cleanup);

describe('Check "utils/common/helpers"', () => {
  describe('EventManager', () => {
    it('should emit event for two listeners', () => {
      const manager = new EventManager();
      const listener1 = jest.fn();
      const listener2 = jest.fn();
      manager.on('testEvent', listener1);
      manager.on('testEvent', listener2);
      manager.emit('testEvent');
      expect(listener1).toHaveBeenCalled();
      expect(listener2).toHaveBeenCalled();
    });

    it('should emit event once', () => {
      const manager = new EventManager();
      const listener = jest.fn();
      manager.once('testEvent', listener);
      manager.emit('testEvent');
      manager.emit('testEvent');
      expect(listener).toHaveBeenCalledTimes(1);
    });

    it('should remove all listeners', () => {
      const manager = new EventManager();
      manager.removeAllListeners();
      expect(manager.events).toMatchObject({});
    });

    it('should set maxListeners', () => {
      const newSize = 5;
      const manager = new EventManager();
      manager.setMaxListeners(newSize);
      expect(manager.maxListeners).toBe(newSize);
    });

    it('should check warnOnce', () => {
      const originalWarn = console.warn;
      const consoleOutput: string[] = [];
      console.warn = (output: string) => consoleOutput.push(output);

      const manager = new EventManager();
      manager.setMaxListeners(0);
      manager.on('testEvent', jest.fn());
      manager.on('testEvent', jest.fn());
      expect(consoleOutput.length).toEqual(1);

      console.warn = originalWarn;
    });
  });

  describe('isElementOverflowing', () => {
    it(`check with DOM element`, () => {
      const element = document.createElement('div');
      expect(isElementOverflowing(element)).toBeFalsy();
    });
  });

  describe('isVisibleInViewport', () => {
    it(`check with DOM element`, () => {
      const element = document.createElement('div');
      expect(isVisibleInViewport(element)).toBeTruthy();
    });
  });

  describe('stringToColor', () => {
    it(`check with empty string`, () => {
      expect(stringToColor('')).toEqual('#000000');
    });

    it(`check with "JS" initials`, () => {
      expect(stringToColor('JS')).toEqual('#490900');
    });
  });

  describe('stringToHashCode', () => {
    it(`check with empty string`, () => {
      expect(stringToHashCode('')).toEqual(0);
    });

    it(`check with "Hello" string`, () => {
      expect(stringToHashCode('Hello')).toEqual(69609650);
    });

    it(`check without Math.imul`, () => {
      const imul = Math.imul;
      Math.imul = undefined as unknown as typeof imul;
      expect(stringToHashCode('Hello')).toEqual(69609650);
      Math.imul = imul;
    });
  });

  describe('throttleEvent', () => {
    it(`check with Jest.spyOn`, () => {
      const requestAnimationFrameSpy = jest
        .spyOn(window, 'requestAnimationFrame')
        .mockImplementation((callback: FrameRequestCallback): number => {
          callback(0);
          return 0;
        });
      const throttleEventHandler = jest.fn();

      const throttledEventHandler = throttleEvent(throttleEventHandler);
      throttledEventHandler(new Event('click'));
      expect(throttleEventHandler).toHaveBeenCalled();

      expect(requestAnimationFrameSpy).toHaveBeenCalled();
      requestAnimationFrameSpy.mockClear();
    });
  });

  const ellipsis = '&hellip;';
  const text = 'lorem ipsum';

  describe('truncateWithEllipsis', () => {
    it(`check with empty string`, () => {
      expect(truncateWithEllipsis('', 0)).toEqual('');
    });

    it(`check with text that fits`, () => {
      expect(truncateWithEllipsis(text, text.length)).toEqual(text);
    });

    it(`check with custom ellipsis`, () => {
      expect(truncateWithEllipsis(text, 1, ellipsis)).toEqual(ellipsis);
    });
  });
});
