Загрузка данных


jest.mock('@src/constants', () => ({
  CHART_DRAWING_TOOLBAR_CONTAINER: 'chart-drawing-toolbar-container',
  CHART_MODAL_CONTAINER_CLASSNAME: 'chart-modal-container',
  CHART_PANE_OVERLAY_CONTAINER: 'chart-pane-overlay-container',
  CHART_ROOT_CLASSNAME: 'chart-root',
}));

import {
  CHART_DRAWING_TOOLBAR_CONTAINER,
  CHART_MODAL_CONTAINER_CLASSNAME,
  CHART_PANE_OVERLAY_CONTAINER,
  CHART_ROOT_CLASSNAME,
} from '@src/constants';

import { ContainerManager } from '../ContainerManager';

describe('ContainerManager', () => {
  let parentContainer: HTMLDivElement;

  beforeEach(() => {
    document
      .querySelectorAll('link[href*="fonts.googleapis.com"]')
      .forEach((link) => link.remove());

    parentContainer = document.createElement('div');
    document.body.appendChild(parentContainer);
  });

  afterEach(() => {
    parentContainer.remove();
  });

  describe('createContainers', () => {
    it('должен создавать основные контейнеры графика с корректной структурой', () => {
      const {
        headerContainer,
        footerContainer,
        chartContainer,
        chartAreaContainer,
        toolBarContainer,
        modalContainer,
        controlBarContainer,
        drawingToolbarContainer,
      } = ContainerManager.createContainers({
        parentContainer,
        showBottomPanel: true,
        showMenuButton: true,
      });

      expect(parentContainer.classList.contains(CHART_ROOT_CLASSNAME)).toBe(true);

      expect(parentContainer.children[0]).toBe(headerContainer);
      expect(parentContainer.children[1]).toBe(chartContainer);
      expect(parentContainer.children[2]).toBe(footerContainer);

      expect(chartContainer.contains(toolBarContainer)).toBe(true);
      expect(chartContainer.contains(chartAreaContainer)).toBe(true);
      expect(chartContainer.contains(modalContainer)).toBe(true);

      expect(chartAreaContainer.contains(controlBarContainer)).toBe(true);
      expect(chartAreaContainer.contains(drawingToolbarContainer)).toBe(true);

      expect(
        drawingToolbarContainer.classList.contains(CHART_DRAWING_TOOLBAR_CONTAINER),
      ).toBe(true);

      expect(
        modalContainer.classList.contains(CHART_MODAL_CONTAINER_CLASSNAME),
      ).toBe(true);

      expect(modalContainer.hidden).toBe(true);
    });

    it('должен создавать сетку с нижней панелью', () => {
      ContainerManager.createContainers({
        parentContainer,
        showBottomPanel: true,
        showMenuButton: true,
      });

      expect(parentContainer.style.gridTemplateRows).toBe(
        '36px minmax(0, 1fr) 32px',
      );
    });

    it('должен создавать сетку без нижней панели', () => {
      const { footerContainer } = ContainerManager.createContainers({
        parentContainer,
        showBottomPanel: false,
        showMenuButton: true,
      });

      expect(parentContainer.style.gridTemplateRows).toBe(
        '36px minmax(0, 1fr)',
      );
      expect(parentContainer.contains(footerContainer)).toBe(false);
    });

    it('должен отображать панель инструментов при включённой кнопке меню', () => {
      const { chartContainer, toolBarContainer } =
        ContainerManager.createContainers({
          parentContainer,
          showMenuButton: true,
        });

      expect(chartContainer.contains(toolBarContainer)).toBe(true);
      expect(chartContainer.style.gridTemplateColumns).toBe(
        '42px minmax(0, 1fr)',
      );
    });

    it('должен скрывать панель инструментов при выключенной кнопке меню', () => {
      const { chartContainer, toolBarContainer } =
        ContainerManager.createContainers({
          parentContainer,
          showMenuButton: false,
        });

      expect(chartContainer.contains(toolBarContainer)).toBe(false);
      expect(chartContainer.style.gridTemplateColumns).toBe(
        'minmax(0, 1fr)',
      );
    });

    it('должен переключать видимость панели инструментов', () => {
      const { chartContainer, toolBarContainer, toggleToolbar } =
        ContainerManager.createContainers({
          parentContainer,
          showMenuButton: true,
        });

      expect(toggleToolbar()).toBe(false);
      expect(chartContainer.contains(toolBarContainer)).toBe(false);
      expect(chartContainer.style.gridTemplateColumns).toBe(
        'minmax(0, 1fr)',
      );

      expect(toggleToolbar()).toBe(true);
      expect(chartContainer.contains(toolBarContainer)).toBe(true);
      expect(chartContainer.style.gridTemplateColumns).toBe(
        '42px minmax(0, 1fr)',
      );
    });

    it('должен удалять старое содержимое родительского контейнера перед созданием графика', () => {
      const oldElement = document.createElement('div');

      parentContainer.appendChild(oldElement);

      ContainerManager.createContainers({
        parentContainer,
        showMenuButton: true,
      });

      expect(parentContainer.contains(oldElement)).toBe(false);
    });

    it('должен добавлять шрифт только один раз', () => {
      ContainerManager.createContainers({
        parentContainer,
        showMenuButton: true,
      });

      ContainerManager.createContainers({
        parentContainer,
        showMenuButton: true,
      });

      const fontLinks = document.querySelectorAll(
        'link[href*="fonts.googleapis.com"]',
      );

      expect(fontLinks).toHaveLength(1);
      expect(fontLinks[0]?.getAttribute('href')).toBe(
        'https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,100..900&display=swap',
      );
    });
  });

  describe('createPaneContainers', () => {
    it('должен создавать контейнеры легенды и оверлея панели', () => {
      const { legendContainer, paneOverlayContainer } =
        ContainerManager.createPaneContainers();

      expect(legendContainer.style.position).toBe('absolute');
      expect(legendContainer.style.pointerEvents).toBe('none');

      expect(
        paneOverlayContainer.classList.contains(CHART_PANE_OVERLAY_CONTAINER),
      ).toBe(true);

      expect(paneOverlayContainer.style.position).toBe('absolute');
      expect(paneOverlayContainer.style.pointerEvents).toBe('none');
    });

    it('должен размещать слои графика в корректном порядке', () => {
      const { controlBarContainer, drawingToolbarContainer, modalContainer } =
        ContainerManager.createContainers({
          parentContainer,
          showMenuButton: true,
        });

      const { legendContainer, paneOverlayContainer } =
        ContainerManager.createPaneContainers();

      const controlBarZIndex = Number(controlBarContainer.style.zIndex);
      const paneOverlayZIndex = Number(paneOverlayContainer.style.zIndex);
      const drawingZIndex = Number(drawingToolbarContainer.style.zIndex);
      const legendZIndex = Number(legendContainer.style.zIndex);
      const modalZIndex = Number(modalContainer.style.zIndex);

      expect(controlBarZIndex).toBe(paneOverlayZIndex);
      expect(drawingZIndex).toBeGreaterThan(paneOverlayZIndex);
      expect(legendZIndex).toBeGreaterThan(drawingZIndex);
      expect(modalZIndex).toBeGreaterThan(legendZIndex);
    });
  });

  describe('clearContainers', () => {
    it('должен очищать содержимое родительского контейнера', () => {
      parentContainer.innerHTML = '<div>content</div>';

      ContainerManager.clearContainers(parentContainer);

      expect(parentContainer.innerHTML).toBe('');
    });
  });
});