# Исходный код components.base_component

    """Базовый модуль для работы с компонентами страницы.

    Содержит базовый класс для взаимодействия с элементами страницы через Playwright.
    """

    from playwright.sync_api import Page, Locator, expect
    from tools.logger import get_logger

    logger = get_logger("BASE_COMPONENT")

    class BaseComponent:
        """Базовый компонент для работы с элементами страницы.

        Предоставляет общие методы для взаимодействия с элементами:
        - получение локаторов
        - проверка видимости элементов
        - работа с прокруткой
        """

        def __init__(self, page: Page):
            """Инициализация базового компонента.

            Args:
                page: экземпляр страницы Playwright.
            """

            self.page = page

        # Действия:

        def get_locator(self, locator: str | Locator) -> Locator:
            """Получение объекта Locator из строки или существующего Locator.

            Args:
                locator: строка с CSS/XPath селектором или объект Locator.

            Returns:
                Locator: объект для работы с элементом.

            Raises:
                TypeError: если передан некорректный тип локатора.
            """

            if isinstance(locator, Locator):
                return locator
            elif isinstance(locator, str):
                return self.page.locator(locator)
            else:
                raise TypeError("locator value should be string type or Locator type")

        # Закомментированный код сохранен без изменений
        # def wait_for_all_elements(self, locator: Locator, timeout=5000):
        #     loc = self.get_locator(locator)
        #     elements = self.page.locator(loc).all()
        #
        #     for element in elements:
        #         self.page.locator(loc).wait_for(timeout=timeout)
        #
        #     return elements

        # Проверки:

        def check_absence(self, locator: str | Locator, msg: str) -> None:
            """Проверка отсутствия элемента на странице.

            Args:
                locator: локатор элемента (строка или объект Locator).
                msg: сообщение об ошибке при неудачной проверке.

            Raises:
                AssertionError: если элемент виден на странице.
            """

            loc = self.get_locator(locator)
            expect(loc).to_be_hidden(timeout=12000), msg

        def check_visibility(self, locator: str | Locator, msg: str) -> None:
            """Проверка видимости элемента на странице.

            Args:
                locator: локатор элемента (строка или объект Locator).
                msg: сообщение об ошибке при неудачной проверке.

            Raises:
                AssertionError: если элемент не виден на странице.
            """

            loc = self.get_locator(locator)
            expect(loc).to_be_visible(visible=True, timeout=12000), msg

        def is_scrollable_vertically(self, locator: str | Locator) -> bool:
            """Проверка возможности вертикальной прокрутки элемента.

            Args:
                locator: локатор элемента.

            Returns:
                bool: True если элемент можно прокрутить вертикально.
            """

            loc = self.get_locator(locator)
            return loc.evaluate("el => el.scrollHeight > el.clientHeight")

        def is_scrollable_horizontally(self, locator: str | Locator) -> bool:
            """Проверка возможности горизонтальной прокрутки элемента.

            Args:
                locator: локатор элемента.

            Returns:
                bool: True если элемент можно прокрутить горизонтально.
            """

            loc = self.get_locator(locator)
            return loc.evaluate("el => el.scrollWidth > el.clientWidth")

        # Методы прокрутки:

        def scroll_up(self, locator: str | Locator, timeout: int = 10000) -> None:
            """Прокрутка элемента до самого верха.

            Args:
                locator: локатор элемента.
                timeout: максимальное время ожидания в мс.

            Raises:
                AssertionError: если прокрутка не выполнена до верха.
            """

            loc = self.get_locator(locator)
            max_attempts = 3
            attempt = 0

            while attempt < max_attempts:
                # Получаем текущую позицию прокрутки
                previous_position = loc.evaluate("el => el.scrollTop")

                # Прокручиваем до верха
                loc.evaluate("el => el.scrollTo(0, 0)")

                # Ждем завершения прокрутки с использованием timeout
                loc.wait_for(timeout=min(2000, timeout))

                # Получаем новую позицию прокрутки
                current_position = loc.evaluate("el => el.scrollTop")

                # Проверяем, достигли ли мы верха
                if current_position <= 1:  # <= 1 для учета погрешности
                    return  # Успешно прокрутили до верха

                # Проверяем, была ли вообще прокрутка
                if current_position >= previous_position:
                    attempt += 1
                else:
                    attempt = 0  # Сбрасываем счетчик, если была прокрутка

            # Если вышли из цикла - не смогли прокрутить до верха
            final_position = loc.evaluate("el => el.scrollTop")
            scroll_height = loc.evaluate("el => el.scrollHeight")
            client_height = loc.evaluate("el => el.clientHeight")

            raise AssertionError(
                f"Не удалось прокрутить до верха. "
                f"Текущая позиция: {final_position}, ожидалось: 0, "
                f"Высота контента: {scroll_height}, "
                f"Высота видимой области: {client_height}"
            )

        def scroll_down(self, locator: str | Locator, timeout: int = 10000) -> None:
            """Прокрутка элемента до самого конца.

            Args:
                locator: локатор элемента.
                timeout: максимальное время ожидания в мс.

            Raises:
                AssertionError: если прокрутка не выполнена до конца.
            """

            loc = self.get_locator(locator)
            max_attempts = 3
            attempt = 0

            while attempt < max_attempts:
                # Получаем текущую позицию прокрутки и размеры
                current_position = loc.evaluate("el => el.scrollTop")
                scroll_height = loc.evaluate("el => el.scrollHeight")
                client_height = loc.evaluate("el => el.clientHeight")

                # Проверяем, достигли ли мы конца ДО прокрутки
                if current_position + client_height >= scroll_height - 5:  # -5 для учета погрешности
                    return  # Уже прокручено до конца

                # Прокручиваем до конца
                loc.evaluate("el => el.scrollTo(0, el.scrollHeight)")

                # Ждем загрузки контента
                loc.wait_for(timeout=min(2000, timeout))

                # Получаем новую позицию прокрутки
                new_position = loc.evaluate("el => el.scrollTop")
                new_scroll_height = loc.evaluate("el => el.scrollHeight")
                new_client_height = loc.evaluate("el => el.clientHeight")

                # Проверяем, достигли ли мы конца
                if new_position + new_client_height >= new_scroll_height - 5:  # -5 для учета погрешности
                    return  # Успешно прокрутили до конца

                # Проверяем, была ли прокрутка
                if new_position <= current_position and new_scroll_height <= scroll_height:
                    # Прокрутки не произошло или контент не изменился
                    attempt += 1
                else:
                    attempt = 0  # Сбрасываем счетчик, если была прокрутка

            # Если вышли из цикла - не смогли прокрутить до конца
            final_position = loc.evaluate("el => el.scrollTop")
            final_scroll_height = loc.evaluate("el => el.scrollHeight")
            final_client_height = loc.evaluate("el => el.clientHeight")
            max_scroll_y = final_scroll_height - final_client_height

            raise AssertionError(
                f"Не удалось прокрутить до конца. "
                f"Текущая позиция: {final_position}, ожидалось: {max_scroll_y}, "
                f"Высота контента: {final_scroll_height}, "
                f"Высота видимой области: {final_client_height}, "
                f"Сумма: {final_position + final_client_height}"
            )

        def scroll_left(self, locator: str | Locator, timeout: int = 10000) -> None:
            """Прокрутка элемента до самого левого края.

            Args:
                locator: локатор элемента.
                timeout: максимальное время ожидания в мс.

            Raises:
                AssertionError: если прокрутка не выполнена до левого края.
            """

            loc = self.get_locator(locator)
            max_attempts = 3
            attempt = 0

            while attempt < max_attempts:
                # Получаем текущую позицию прокрутки
                previous_position = loc.evaluate("el => el.scrollLeft")

                # Прокручиваем до левого края
                loc.evaluate("el => el.scrollTo(0, el.scrollTop)")

                # Ждем завершения прокрутки с использованием timeout
                loc.wait_for(timeout=min(2000, timeout))

                # Получаем новую позицию прокрутки
                current_position = loc.evaluate("el => el.scrollLeft")

                # Проверяем, достигли ли мы левого края
                if current_position <= 1:  # <= 1 для учета погрешности
                    return  # Успешно прокрутили до левого края

                # Проверяем, была ли вообще прокрутка
                if current_position >= previous_position:
                    attempt += 1
                else:
                    attempt = 0  # Сбрасываем счетчик, если была прокрутка

            # Если вышли из цикла - не смогли прокрутить до левого края
            final_position = loc.evaluate("el => el.scrollLeft")
            scroll_width = loc.evaluate("el => el.scrollWidth")
            client_width = loc.evaluate("el => el.clientWidth")

            raise AssertionError(
                f"Не удалось прокрутить до левого края. "
                f"Текущая позиция: {final_position}, ожидалось: 0, "
                f"Ширина контента: {scroll_width}, "
                f"Ширина видимой области: {client_width}"
            )

        def scroll_right(self, locator: str | Locator, timeout: int = 10000) -> None:
            """Прокрутка элемента до самого правого края.

            Args:
                locator: локатор элемента.
                timeout: максимальное время ожидания в мс.

            Raises:
                AssertionError: если прокрутка не выполнена до правого края.
            """

            loc = self.get_locator(locator)
            max_attempts = 3
            attempt = 0

            while attempt < max_attempts:
                # Получаем текущую позицию прокрутки
                previous_position = loc.evaluate("el => el.scrollLeft")

                # Прокручиваем до правого края
                loc.evaluate("el => el.scrollTo(el.scrollWidth, el.scrollTop)")

                # Ждем завершения прокрутки с использованием timeout
                loc.wait_for(timeout=min(2000, timeout))

                # Получаем новую позицию прокрутки
                current_position = loc.evaluate("el => el.scrollLeft")
                scroll_width = loc.evaluate("el => el.scrollWidth")
                client_width = loc.evaluate("el => el.clientWidth")

                # Проверяем, достигли ли мы правого края
                max_scroll_x = scroll_width - client_width
                if current_position >= max_scroll_x - 1:  # -1 для учета погрешности
                    return  # Успешно прокрутили до правого края

                # Проверяем, была ли вообще прокрутка
                if current_position <= previous_position:
                    attempt += 1
                else:
                    attempt = 0  # Сбрасываем счетчик, если была прокрутка

            # Если вышли из цикла - не смогли прокрутить до правого края
            final_position = loc.evaluate("el => el.scrollLeft")
            scroll_width = loc.evaluate("el => el.scrollWidth")
            client_width = loc.evaluate("el => el.clientWidth")
            max_scroll_x = scroll_width - client_width

            raise AssertionError(
                f"Не удалось прокрутить до правого края. "
                f"Текущая позиция: {final_position}, ожидалось: {max_scroll_x}, "
                f"Ширина контента: {scroll_width}, "
                f"Ширина видимой области: {client_width}"
            )