Compare commits
3 Commits
main
...
ra2/create
| Author | SHA1 | Date |
|---|---|---|
|
|
3680e42c86 | |
|
|
a25db67097 | |
|
|
8140769583 |
|
|
@ -35,114 +35,62 @@ class RackObjectMaker(BaseComponent):
|
||||||
Инициализирует компонент создания стойки.
|
Инициализирует компонент создания стойки.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
page: Экземпляр страницы Playwright
|
page (Page): Экземпляр страницы Playwright
|
||||||
"""
|
"""
|
||||||
|
|
||||||
super().__init__(page)
|
super().__init__(page)
|
||||||
|
|
||||||
# Действия:
|
# Действия:
|
||||||
|
|
||||||
def fill_rack_data(self, rack_data: RackData) -> None:
|
def _fill_combobox_field(self, field_name: str, value: str, fields_locators: dict) -> None:
|
||||||
"""
|
"""
|
||||||
Заполняет данные для создания стойки.
|
Заполняет combobox поле.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
rack_data: Данные стойки
|
field_name (str): Название поля
|
||||||
|
value (str): Значение для установки
|
||||||
|
fields_locators (dict): Словарь с найденными полями формы
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: Если поле не найдено в форме
|
||||||
"""
|
"""
|
||||||
|
|
||||||
logger.debug(f"Filling rack data: {rack_data.name}")
|
# Получаем контейнер поля по его названию
|
||||||
|
|
||||||
self._fill_text_fields(rack_data)
|
|
||||||
self._fill_combobox_fields(rack_data)
|
|
||||||
|
|
||||||
logger.debug("Rack data filled successfully")
|
|
||||||
|
|
||||||
def _get_form_fields(self) -> dict:
|
|
||||||
"""
|
|
||||||
Получает все поля формы стойки.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
dict: Словарь {название поля: Locator контейнера поля}
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Получаем контейнер формы (второй элемент)
|
|
||||||
container_locator = self.page.locator(RackLocators.FORM_INPUT_CONTAINER).nth(1)
|
|
||||||
|
|
||||||
if container_locator.count() == 0:
|
|
||||||
logger.error("Form container not found")
|
|
||||||
raise ValueError("Form container not found")
|
|
||||||
|
|
||||||
return self.get_input_fields_locators(container_locator)
|
|
||||||
|
|
||||||
def _fill_text_fields(self, rack_data: RackData) -> None:
|
|
||||||
"""Заполняет текстовые поля."""
|
|
||||||
|
|
||||||
logger.debug("Filling text fields...")
|
|
||||||
|
|
||||||
# Получаем все поля формы
|
|
||||||
fields_locators = self._get_form_fields()
|
|
||||||
|
|
||||||
logger.debug(f"Available text fields: {list(fields_locators.keys())}")
|
|
||||||
|
|
||||||
def clear_and_fill(field_name: str, value: str):
|
|
||||||
"""Очищает поле и заполняет его значением."""
|
|
||||||
|
|
||||||
if not value:
|
|
||||||
logger.debug(f"Skipping empty value for field '{field_name}'")
|
|
||||||
return
|
|
||||||
|
|
||||||
# Получаем контейнер поля
|
|
||||||
field_container = fields_locators.get(field_name)
|
field_container = fields_locators.get(field_name)
|
||||||
|
|
||||||
if not field_container:
|
if not field_container:
|
||||||
logger.warning(f"Field '{field_name}' not found in form. Available fields: {list(fields_locators.keys())}")
|
logger.error(f"Field '{field_name}' not found in form. Available fields: {list(fields_locators.keys())}")
|
||||||
return
|
raise ValueError(f"Field '{field_name}' not found in form")
|
||||||
|
|
||||||
# Находим input внутри контейнера
|
logger.debug(f"Filling field '{field_name}' with value '{value}'...")
|
||||||
input_field = field_container.locator("input").first
|
|
||||||
|
|
||||||
if input_field.count() == 0:
|
# Прокручиваем до поля
|
||||||
logger.warning(f"Input element not found in container for field '{field_name}'")
|
field_container.scroll_into_view_if_needed()
|
||||||
return
|
self.wait_for_timeout(300)
|
||||||
|
|
||||||
# Проверяем видимость
|
# Проверяем видимость поля
|
||||||
if not input_field.is_visible():
|
self.check_visibility(field_container, f"Field '{field_name}' not found")
|
||||||
logger.debug(f"Field '{field_name}' is not visible, scrolling into view...")
|
|
||||||
input_field.scroll_into_view_if_needed()
|
|
||||||
self.wait_for_timeout(500)
|
|
||||||
|
|
||||||
# Проверяем, не disabled ли поле
|
# Находим кнопку открытия выпадающего списка внутри контейнера поля
|
||||||
is_disabled = input_field.get_attribute("disabled")
|
open_button = field_container.locator(".v-input__append-inner").first
|
||||||
is_readonly = input_field.get_attribute("readonly")
|
|
||||||
|
|
||||||
if is_disabled or is_readonly:
|
# Кликаем для открытия выпадающего списка
|
||||||
logger.warning(f"Field '{field_name}' is disabled or readonly")
|
open_button.click(force=True)
|
||||||
return
|
self.wait_for_timeout(300)
|
||||||
|
|
||||||
# Очищаем поле
|
# Вводим значение из выпадающего списка
|
||||||
input_field.click()
|
dropdown_item_locator = RackLocators.DROPDOWN_ITEM_BY_TEXT.format(value)
|
||||||
input_field.press("Control+A")
|
element = self.page.locator(dropdown_item_locator).first
|
||||||
input_field.press("Backspace")
|
|
||||||
|
|
||||||
# Заполняем значение
|
# Скроллим к элементу если нужно
|
||||||
input_field.fill(value)
|
self._scroll_until_element(
|
||||||
logger.debug(f"Filled '{field_name}': {value}")
|
self.page.locator(RackLocators.DROPDOWN_LIST).first,
|
||||||
|
value
|
||||||
|
)
|
||||||
|
self.wait_for_timeout(300)
|
||||||
|
element.click()
|
||||||
|
|
||||||
# Обязательные поля
|
logger.debug(f"Field '{field_name}' filled successfully")
|
||||||
if rack_data.name:
|
|
||||||
clear_and_fill("Имя", rack_data.name)
|
|
||||||
|
|
||||||
# Опциональные поля
|
|
||||||
if rack_data.serial:
|
|
||||||
clear_and_fill("Серийный номер", rack_data.serial)
|
|
||||||
|
|
||||||
if rack_data.inventory:
|
|
||||||
clear_and_fill("Инвентарный номер", rack_data.inventory)
|
|
||||||
|
|
||||||
if rack_data.comment:
|
|
||||||
clear_and_fill("Комментарий", rack_data.comment)
|
|
||||||
|
|
||||||
logger.debug("Text fields filled successfully")
|
|
||||||
|
|
||||||
def _fill_combobox_fields(self, rack_data: RackData) -> None:
|
def _fill_combobox_fields(self, rack_data: RackData) -> None:
|
||||||
"""Заполняет combobox поля."""
|
"""Заполняет combobox поля."""
|
||||||
|
|
@ -180,59 +128,103 @@ class RackObjectMaker(BaseComponent):
|
||||||
self._fill_combobox_field("Проект/Титул", rack_data.project, fields_locators)
|
self._fill_combobox_field("Проект/Титул", rack_data.project, fields_locators)
|
||||||
logger.debug(f"Selected project/title: {rack_data.project}")
|
logger.debug(f"Selected project/title: {rack_data.project}")
|
||||||
|
|
||||||
def _fill_combobox_field(self, field_name: str, value: str, fields_locators: dict) -> None:
|
def _fill_text_fields(self, rack_data: RackData) -> None:
|
||||||
"""
|
"""Заполняет текстовые поля."""
|
||||||
Заполняет combobox поле.
|
|
||||||
|
|
||||||
Args:
|
logger.debug("Filling text fields...")
|
||||||
field_name: Название поля
|
|
||||||
value: Значение для установки
|
|
||||||
fields_locators: Словарь с найденными полями формы
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Получаем контейнер поля по его названию
|
# Получаем все поля формы
|
||||||
|
fields_locators = self._get_form_fields()
|
||||||
|
|
||||||
|
logger.debug(f"Available text fields: {list(fields_locators.keys())}")
|
||||||
|
|
||||||
|
def clear_and_fill(field_name: str, value: str):
|
||||||
|
"""Очищает поле и заполняет его значением."""
|
||||||
|
|
||||||
|
if not value:
|
||||||
|
logger.debug(f"Skipping empty value for field '{field_name}'")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Получаем контейнер поля
|
||||||
field_container = fields_locators.get(field_name)
|
field_container = fields_locators.get(field_name)
|
||||||
|
|
||||||
if not field_container:
|
if not field_container:
|
||||||
logger.error(f"Field '{field_name}' not found in form. Available fields: {list(fields_locators.keys())}")
|
logger.warning(f"Field '{field_name}' not found in form. Available fields: {list(fields_locators.keys())}")
|
||||||
raise ValueError(f"Field '{field_name}' not found in form")
|
return
|
||||||
|
|
||||||
logger.debug(f"Filling field '{field_name}' with value '{value}'...")
|
# Находим input внутри контейнера
|
||||||
|
input_field = field_container.locator("input").first
|
||||||
|
|
||||||
# Прокручиваем до поля
|
if input_field.count() == 0:
|
||||||
field_container.scroll_into_view_if_needed()
|
logger.warning(f"Input element not found in container for field '{field_name}'")
|
||||||
self.wait_for_timeout(500)
|
return
|
||||||
|
|
||||||
# Проверяем видимость поля
|
# Проверяем видимость
|
||||||
self.check_visibility(field_container, f"Field '{field_name}' not found")
|
if not input_field.is_visible():
|
||||||
|
logger.debug(f"Field '{field_name}' is not visible, scrolling into view...")
|
||||||
|
input_field.scroll_into_view_if_needed()
|
||||||
|
self.wait_for_timeout(300)
|
||||||
|
|
||||||
# Находим кнопку открытия выпадающего списка внутри контейнера поля
|
# Проверяем, не disabled ли поле
|
||||||
open_button = field_container.locator(".v-input__append-inner").first
|
is_disabled = input_field.get_attribute("disabled")
|
||||||
|
is_readonly = input_field.get_attribute("readonly")
|
||||||
|
|
||||||
# Кликаем для открытия выпадающего списка
|
if is_disabled or is_readonly:
|
||||||
open_button.click(force=True)
|
logger.warning(f"Field '{field_name}' is disabled or readonly")
|
||||||
self.wait_for_timeout(1000)
|
return
|
||||||
|
|
||||||
# Вводим значение из выпадающего списка
|
# Очищаем поле
|
||||||
dropdown_item_locator = RackLocators.DROPDOWN_ITEM_BY_TEXT.format(value)
|
input_field.click()
|
||||||
element = self.page.locator(dropdown_item_locator).first
|
input_field.press("Control+A")
|
||||||
|
input_field.press("Backspace")
|
||||||
|
|
||||||
# Скроллим к элементу если нужно
|
# Заполняем значение
|
||||||
self._scroll_until_element(
|
input_field.fill(value)
|
||||||
self.page.locator(RackLocators.DROPDOWN_LIST).first,
|
logger.debug(f"Filled '{field_name}': {value}")
|
||||||
value
|
|
||||||
)
|
|
||||||
self.wait_for_timeout(500)
|
|
||||||
element.click()
|
|
||||||
|
|
||||||
logger.debug(f"Field '{field_name}' filled successfully")
|
# Обязательные поля
|
||||||
|
if rack_data.name:
|
||||||
|
clear_and_fill("Имя", rack_data.name)
|
||||||
|
|
||||||
|
# Опциональные поля
|
||||||
|
if rack_data.serial:
|
||||||
|
clear_and_fill("Серийный номер", rack_data.serial)
|
||||||
|
|
||||||
|
if rack_data.inventory:
|
||||||
|
clear_and_fill("Инвентарный номер", rack_data.inventory)
|
||||||
|
|
||||||
|
if rack_data.comment:
|
||||||
|
clear_and_fill("Комментарий", rack_data.comment)
|
||||||
|
|
||||||
|
logger.debug("Text fields filled successfully")
|
||||||
|
|
||||||
|
def _get_form_fields(self) -> dict:
|
||||||
|
"""
|
||||||
|
Получает все поля формы стойки.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: Словарь {название поля: Locator контейнера поля}
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: Если контейнер формы не найден
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Получаем контейнер формы (второй элемент)
|
||||||
|
container_locator = self.page.locator(RackLocators.FORM_INPUT_CONTAINER).nth(1)
|
||||||
|
|
||||||
|
if container_locator.count() == 0:
|
||||||
|
logger.error("Form container not found")
|
||||||
|
raise ValueError("Form container not found")
|
||||||
|
|
||||||
|
return self.get_input_fields_locators(container_locator)
|
||||||
|
|
||||||
def _scroll_until_element(self, locator: Locator, name: str) -> None:
|
def _scroll_until_element(self, locator: Locator, name: str) -> None:
|
||||||
"""
|
"""
|
||||||
Скроллит список до тех пор, пока не перестанут подгружаться новые элементы.
|
Скроллит список до тех пор, пока не перестанут подгружаться новые элементы.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
locator: Локатор элементов или строка с CSS/XPath.
|
locator (Locator): Локатор элементов или строка с CSS/XPath
|
||||||
|
name (str): Имя элемента для поиска
|
||||||
"""
|
"""
|
||||||
|
|
||||||
loc = self.get_locator(locator)
|
loc = self.get_locator(locator)
|
||||||
|
|
@ -266,6 +258,21 @@ class RackObjectMaker(BaseComponent):
|
||||||
|
|
||||||
self.wait_for_timeout(300)
|
self.wait_for_timeout(300)
|
||||||
|
|
||||||
|
def fill_rack_data(self, rack_data: RackData) -> None:
|
||||||
|
"""
|
||||||
|
Заполняет данные для создания стойки.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
rack_data (RackData): Данные стойки
|
||||||
|
"""
|
||||||
|
|
||||||
|
logger.debug(f"Filling rack data: {rack_data.name}")
|
||||||
|
|
||||||
|
self._fill_text_fields(rack_data)
|
||||||
|
self._fill_combobox_fields(rack_data)
|
||||||
|
|
||||||
|
logger.debug("Rack data filled successfully")
|
||||||
|
|
||||||
# Проверки:
|
# Проверки:
|
||||||
|
|
||||||
def check_rack_fields_presence(self) -> None:
|
def check_rack_fields_presence(self) -> None:
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
"""Модуль фрейма создания дочернего элемента."""
|
"""Модуль фрейма создания дочернего элемента."""
|
||||||
|
|
||||||
import re
|
import re
|
||||||
from playwright.sync_api import expect, Page, Locator
|
from playwright.sync_api import Page, Locator
|
||||||
from tools.logger import get_logger
|
from tools.logger import get_logger
|
||||||
from locators.rack_locators import RackLocators
|
from locators.rack_locators import RackLocators
|
||||||
from locators.selection_bar_locators import SelectionBarLocators
|
from locators.selection_bar_locators import SelectionBarLocators
|
||||||
|
|
@ -23,7 +23,7 @@ class CreateChildElementFrame(BaseComponent):
|
||||||
Инициализирует фрейм создания дочернего элемента.
|
Инициализирует фрейм создания дочернего элемента.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
page: Экземпляр страницы Playwright
|
page (Page): Экземпляр страницы Playwright
|
||||||
"""
|
"""
|
||||||
|
|
||||||
super().__init__(page)
|
super().__init__(page)
|
||||||
|
|
@ -54,7 +54,7 @@ class CreateChildElementFrame(BaseComponent):
|
||||||
Очищает combobox поле по его названию.
|
Очищает combobox поле по его названию.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
field_name: Название поля для очистки
|
field_name (str): Название поля для очистки
|
||||||
"""
|
"""
|
||||||
|
|
||||||
logger.debug(f"Clearing combobox field '{field_name}'...")
|
logger.debug(f"Clearing combobox field '{field_name}'...")
|
||||||
|
|
@ -72,7 +72,7 @@ class CreateChildElementFrame(BaseComponent):
|
||||||
|
|
||||||
# Прокручиваем до поля
|
# Прокручиваем до поля
|
||||||
field_container.scroll_into_view_if_needed()
|
field_container.scroll_into_view_if_needed()
|
||||||
self.wait_for_timeout(500)
|
self.wait_for_timeout(300)
|
||||||
|
|
||||||
# Проверяем видимость
|
# Проверяем видимость
|
||||||
if not field_container.is_visible():
|
if not field_container.is_visible():
|
||||||
|
|
@ -88,7 +88,7 @@ class CreateChildElementFrame(BaseComponent):
|
||||||
|
|
||||||
# Если кнопка закрытия видима - кликаем на нее
|
# Если кнопка закрытия видима - кликаем на нее
|
||||||
close_button.click(force=True)
|
close_button.click(force=True)
|
||||||
self.wait_for_timeout(500)
|
self.wait_for_timeout(300)
|
||||||
logger.debug(f"Combobox field '{field_name}' cleared using close button")
|
logger.debug(f"Combobox field '{field_name}' cleared using close button")
|
||||||
else:
|
else:
|
||||||
logger.debug(f"Close button (i.mdi-close) not found for field '{field_name}'")
|
logger.debug(f"Close button (i.mdi-close) not found for field '{field_name}'")
|
||||||
|
|
@ -115,6 +115,61 @@ class CreateChildElementFrame(BaseComponent):
|
||||||
|
|
||||||
return self.selection_bar.get_selection_bar_title()
|
return self.selection_bar.get_selection_bar_title()
|
||||||
|
|
||||||
|
def is_field_filled(self, field_name: str, container_locator: Locator = None) -> bool:
|
||||||
|
"""
|
||||||
|
Проверяет, заполнено ли combobox или текстовое поле.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
field_name (str): Название поля для проверки
|
||||||
|
container_locator (Locator, optional): Локатор контейнера формы
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True если поле заполнено, False в противном случае
|
||||||
|
"""
|
||||||
|
|
||||||
|
logger.debug(f"Checking if field '{field_name}' is filled...")
|
||||||
|
|
||||||
|
# Если контейнер не передан, используем контейнер по умолчанию
|
||||||
|
if container_locator is None:
|
||||||
|
container_locator = self.page.locator(RackLocators.FORM_INPUT_CONTAINER).nth(1)
|
||||||
|
|
||||||
|
# Получаем словарь всех полей формы
|
||||||
|
fields_locators = self.get_input_fields_locators(container_locator)
|
||||||
|
|
||||||
|
if field_name not in fields_locators:
|
||||||
|
logger.debug(f"Field '{field_name}' not found in fields_locators")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Получаем контейнер поля
|
||||||
|
field_container = fields_locators[field_name]
|
||||||
|
|
||||||
|
if not field_container.is_visible():
|
||||||
|
logger.debug(f"Field '{field_name}' not visible")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Проверяем наличие выбранного значения через v-chip (чип выбранного значения в combobox)
|
||||||
|
selected_chip = field_container.locator(".v-chip").first
|
||||||
|
|
||||||
|
# Проверяем наличие текста в поле
|
||||||
|
field_text = field_container.text_content() or ""
|
||||||
|
has_text = bool(field_text.strip())
|
||||||
|
|
||||||
|
# Проверяем наличие чипа
|
||||||
|
has_chip = selected_chip.count() > 0 and selected_chip.is_visible()
|
||||||
|
|
||||||
|
# Для текстовых полей проверяем значение input
|
||||||
|
if not has_chip:
|
||||||
|
input_field = field_container.locator("input").first
|
||||||
|
if input_field.count() > 0:
|
||||||
|
input_value = input_field.input_value() or ""
|
||||||
|
has_input_value = bool(input_value.strip())
|
||||||
|
logger.debug(f"Field '{field_name}' - has input value: {has_input_value}")
|
||||||
|
has_text = has_text or has_input_value
|
||||||
|
|
||||||
|
logger.debug(f"Field '{field_name}' - has chip: {has_chip}, has text: {has_text}")
|
||||||
|
|
||||||
|
return has_chip or has_text
|
||||||
|
|
||||||
def open_object_class_combobox(self) -> None:
|
def open_object_class_combobox(self) -> None:
|
||||||
"""Открывает выпадающий список combobox."""
|
"""Открывает выпадающий список combobox."""
|
||||||
|
|
||||||
|
|
@ -138,7 +193,12 @@ class CreateChildElementFrame(BaseComponent):
|
||||||
logger.debug("Combobox menu is already open")
|
logger.debug("Combobox menu is already open")
|
||||||
|
|
||||||
def select_object_class(self, class_name: str) -> None:
|
def select_object_class(self, class_name: str) -> None:
|
||||||
"""Выбирает класс объекта из выпадающего списка."""
|
"""
|
||||||
|
Выбирает класс объекта из выпадающего списка.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
class_name (str): Название класса объекта для выбора
|
||||||
|
"""
|
||||||
|
|
||||||
logger.debug(f"Selecting object class: '{class_name}'...")
|
logger.debug(f"Selecting object class: '{class_name}'...")
|
||||||
|
|
||||||
|
|
@ -149,7 +209,7 @@ class CreateChildElementFrame(BaseComponent):
|
||||||
self.selection_bar.select_value(class_name)
|
self.selection_bar.select_value(class_name)
|
||||||
|
|
||||||
# Даем время на применение выбора
|
# Даем время на применение выбора
|
||||||
self.wait_for_timeout(3000)
|
self.wait_for_timeout(300)
|
||||||
|
|
||||||
logger.debug(f"Object class '{class_name}' successfully selected")
|
logger.debug(f"Object class '{class_name}' successfully selected")
|
||||||
|
|
||||||
|
|
@ -160,7 +220,11 @@ class CreateChildElementFrame(BaseComponent):
|
||||||
Проверяет, что поле подсвечено цветом ошибки (валидация не пройдена).
|
Проверяет, что поле подсвечено цветом ошибки (валидация не пройдена).
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
field_name: Название поля для проверки
|
field_name (str): Название поля для проверки
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: Если поле не найдено в форме
|
||||||
|
AssertionError: Если поле не подсвечено ошибкой
|
||||||
"""
|
"""
|
||||||
|
|
||||||
logger.debug(f"Checking field '{field_name}' for error highlighting...")
|
logger.debug(f"Checking field '{field_name}' for error highlighting...")
|
||||||
|
|
@ -193,7 +257,11 @@ class CreateChildElementFrame(BaseComponent):
|
||||||
Проверяет, что поле НЕ подсвечено цветом ошибки (валидация успешна).
|
Проверяет, что поле НЕ подсвечено цветом ошибки (валидация успешна).
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
field_name: Название поля для проверки
|
field_name (str): Название поля для проверки
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: Если поле не найдено в форме
|
||||||
|
AssertionError: Если поле подсвечено ошибкой
|
||||||
"""
|
"""
|
||||||
|
|
||||||
logger.debug(f"Checking field '{field_name}' for absence of error highlighting...")
|
logger.debug(f"Checking field '{field_name}' for absence of error highlighting...")
|
||||||
|
|
@ -226,12 +294,15 @@ class CreateChildElementFrame(BaseComponent):
|
||||||
Проверяет что выбран указанный класс объекта.
|
Проверяет что выбран указанный класс объекта.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
expected_class: Ожидаемый выбранный класс объекта
|
expected_class (str): Ожидаемый выбранный класс объекта
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
AssertionError: Если выбранный класс не соответствует ожидаемому
|
||||||
"""
|
"""
|
||||||
|
|
||||||
logger.debug(f"Checking selected object class: '{expected_class}'...")
|
logger.debug(f"Checking selected object class: '{expected_class}'...")
|
||||||
|
|
||||||
self.wait_for_timeout(1000)
|
self.wait_for_timeout(500)
|
||||||
actual_class = self.get_selected_object_class()
|
actual_class = self.get_selected_object_class()
|
||||||
|
|
||||||
is_match = (expected_class.lower() in actual_class.lower() or
|
is_match = (expected_class.lower() in actual_class.lower() or
|
||||||
|
|
@ -252,7 +323,10 @@ class CreateChildElementFrame(BaseComponent):
|
||||||
Проверяет заголовок тулбара.
|
Проверяет заголовок тулбара.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
expected_title: Ожидаемый заголовок тулбара
|
expected_title (str): Ожидаемый заголовок тулбара
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
AssertionError: Если заголовок не соответствует ожидаемому
|
||||||
"""
|
"""
|
||||||
|
|
||||||
logger.debug(f"Checking toolbar title: '{expected_title}'...")
|
logger.debug(f"Checking toolbar title: '{expected_title}'...")
|
||||||
|
|
@ -274,11 +348,9 @@ class CreateChildElementFrame(BaseComponent):
|
||||||
Проверяет наличие и функциональность кнопок тулбара.
|
Проверяет наличие и функциональность кнопок тулбара.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
self.toolbar.check_button_visibility("add")
|
self.toolbar.check_button_visibility("add")
|
||||||
self.toolbar.check_button_tooltip("add", "Добавить")
|
self.toolbar.check_button_tooltip("add", "Добавить")
|
||||||
self.toolbar.check_button_visibility("cancel")
|
self.toolbar.check_button_visibility("cancel")
|
||||||
self.toolbar.check_button_tooltip("cancel", "Отменить")
|
self.toolbar.check_button_tooltip("cancel", "Отменить")
|
||||||
self.toolbar.click_button("cancel")
|
self.toolbar.click_button("cancel")
|
||||||
self.wait_for_timeout(2000)
|
self.wait_for_timeout(500)
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,8 @@ class Environment:
|
||||||
DEVELOP: str = 'develop'
|
DEVELOP: str = 'develop'
|
||||||
|
|
||||||
URLS: Dict[str, str] = {
|
URLS: Dict[str, str] = {
|
||||||
TEST: 'http://192.168.2.76/',
|
TEST: 'https://192.168.2.76/',
|
||||||
DEVELOP: 'http://192.168.2.69/'
|
DEVELOP: 'https://192.168.2.69/'
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
|
|
|
||||||
|
|
@ -164,6 +164,7 @@ def get_context(browser: Browser, request: FixtureRequest, start: str) -> Browse
|
||||||
|
|
||||||
context = browser.new_context(
|
context = browser.new_context(
|
||||||
# no_viewport=True,
|
# no_viewport=True,
|
||||||
|
ignore_https_errors=True,
|
||||||
viewport= ast.literal_eval(request.config.getoption('--s')),
|
viewport= ast.literal_eval(request.config.getoption('--s')),
|
||||||
locale=request.config.getoption('l')
|
locale=request.config.getoption('l')
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -113,3 +113,16 @@ class RackLocators:
|
||||||
# Кнопка "Показать стойку"
|
# Кнопка "Показать стойку"
|
||||||
SHOW_RACK_BUTTON = ("//div[@data-testid='CABINET_SHOW__div__hideCabinet' and "
|
SHOW_RACK_BUTTON = ("//div[@data-testid='CABINET_SHOW__div__hideCabinet' and "
|
||||||
"contains(@class, 'cabinet_hide_button_trigger_hide')]")
|
"contains(@class, 'cabinet_hide_button_trigger_hide')]")
|
||||||
|
|
||||||
|
# Кнопки тулбара стойки
|
||||||
|
TOOLBAR_REPLACE_BUTTON = "[data-testid='cabinet-bar__toolbar__btn__replace']"
|
||||||
|
TOOLBAR_DONE_BUTTON = "[data-testid='cabinet-bar__toolbar__btn__done']"
|
||||||
|
TOOLBAR_CLOSE_BUTTON = "[data-testid='cabinet-bar__toolbar__btn__close']"
|
||||||
|
TOOLBAR_REMOVE_BUTTON = "[data-testid='cabinet-bar__toolbar__btn__remove']"
|
||||||
|
|
||||||
|
# Диалог удаления
|
||||||
|
REMOVE_DIALOG = "[data-testid='cabinet-bar__toolbar__dialog-remove']"
|
||||||
|
|
||||||
|
# Кнопки подтверждения удаления
|
||||||
|
CONFIRM_REMOVE_YES_BUTTON = "[data-testid='cabinet-bar__card_confirmation__btn__yes']"
|
||||||
|
CONFIRM_REMOVE_NO_BUTTON = "[data-testid='cabinet-bar__card_confirmation__btn__no']"
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ class RackPage(BasePage):
|
||||||
Инициализирует объект вкладки стойки.
|
Инициализирует объект вкладки стойки.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
page: Экземпляр страницы Playwright
|
page (Page): Экземпляр страницы Playwright
|
||||||
"""
|
"""
|
||||||
|
|
||||||
super().__init__(page)
|
super().__init__(page)
|
||||||
|
|
@ -48,13 +48,103 @@ class RackPage(BasePage):
|
||||||
show_button_locator = self.page.locator(RackLocators.SHOW_RACK_BUTTON)
|
show_button_locator = self.page.locator(RackLocators.SHOW_RACK_BUTTON)
|
||||||
self.show_button = TooltipButton(page, show_button_locator, "show_rack")
|
self.show_button = TooltipButton(page, show_button_locator, "show_rack")
|
||||||
|
|
||||||
|
# Кнопка "Переместить"
|
||||||
|
replace_button_locator = self.page.locator(RackLocators.TOOLBAR_REPLACE_BUTTON)
|
||||||
|
self.replace_button = TooltipButton(page, replace_button_locator, "replace")
|
||||||
|
|
||||||
|
# Кнопка "Сохранить"
|
||||||
|
done_button_locator = self.page.locator(RackLocators.TOOLBAR_DONE_BUTTON)
|
||||||
|
self.done_button = TooltipButton(page, done_button_locator, "done")
|
||||||
|
|
||||||
|
# Кнопка "Отменить"
|
||||||
|
close_button_locator = self.page.locator(RackLocators.TOOLBAR_CLOSE_BUTTON)
|
||||||
|
self.close_button = TooltipButton(page, close_button_locator, "close")
|
||||||
|
|
||||||
|
# Кнопка "Удалить"
|
||||||
|
remove_button_locator = self.page.locator(RackLocators.TOOLBAR_REMOVE_BUTTON)
|
||||||
|
self.remove_button = TooltipButton(page, remove_button_locator, "remove")
|
||||||
|
|
||||||
self.toolbar = ToolbarComponent(page, "")
|
self.toolbar = ToolbarComponent(page, "")
|
||||||
self.toolbar.add_tooltip_button(locator_button, "edit")
|
self.toolbar.add_tooltip_button(locator_button, "edit")
|
||||||
self.toolbar.add_tooltip_button(hide_button_locator, "hide_rack")
|
self.toolbar.add_tooltip_button(hide_button_locator, "hide_rack")
|
||||||
self.toolbar.add_tooltip_button(show_button_locator, "show_rack")
|
self.toolbar.add_tooltip_button(show_button_locator, "show_rack")
|
||||||
|
self.toolbar.add_tooltip_button(replace_button_locator, "replace")
|
||||||
|
self.toolbar.add_tooltip_button(done_button_locator, "done")
|
||||||
|
self.toolbar.add_tooltip_button(close_button_locator, "close")
|
||||||
|
self.toolbar.add_tooltip_button(remove_button_locator, "remove")
|
||||||
|
|
||||||
# Действия
|
# Действия
|
||||||
|
|
||||||
|
def click_remove_button(self) -> None:
|
||||||
|
"""
|
||||||
|
Кликает на кнопку 'Удалить' и обрабатывает диалог подтверждения.
|
||||||
|
"""
|
||||||
|
logger.debug("Clicking on 'Remove' button...")
|
||||||
|
|
||||||
|
# Проверяем видимость кнопки
|
||||||
|
self.toolbar.check_button_visibility("remove")
|
||||||
|
|
||||||
|
# Проверяем тултип кнопки (может быть "Удалить" или "Remove")
|
||||||
|
try:
|
||||||
|
self.toolbar.check_button_tooltip("remove", "Удалить")
|
||||||
|
except AssertionError:
|
||||||
|
try:
|
||||||
|
self.toolbar.check_button_tooltip("remove", "Remove")
|
||||||
|
except AssertionError:
|
||||||
|
logger.debug("Could not verify tooltip text for remove button")
|
||||||
|
|
||||||
|
# Кликаем на кнопку удаления
|
||||||
|
self.toolbar.get_button_by_name("remove").click()
|
||||||
|
self.wait_for_timeout(1000)
|
||||||
|
|
||||||
|
# Ожидаем появления диалога подтверждения
|
||||||
|
self._handle_remove_confirmation_dialog()
|
||||||
|
|
||||||
|
def confirm_remove_dialog(self, confirm: bool = True) -> None:
|
||||||
|
"""
|
||||||
|
Подтверждает или отклоняет удаление в диалоговом окне.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
confirm (bool): Если True - подтвердить удаление, если False - отменить
|
||||||
|
"""
|
||||||
|
logger.debug(f"Confirming remove dialog with: {'Да' if confirm else 'Нет'}")
|
||||||
|
|
||||||
|
# Ждем немного перед поиском диалога
|
||||||
|
self.wait_for_timeout(1500)
|
||||||
|
|
||||||
|
# Ищем активный диалог
|
||||||
|
dialog = self.page.locator("div.v-dialog--active")
|
||||||
|
|
||||||
|
# Проверяем, что диалог найден и содержит нужный текст
|
||||||
|
assert dialog.count() > 0, "No active dialog found"
|
||||||
|
|
||||||
|
# Проверяем текст диалога
|
||||||
|
dialog_text = dialog.first.text_content()
|
||||||
|
logger.debug("Dialog text: %s", dialog_text)
|
||||||
|
|
||||||
|
# Должен содержать "Запрос подтверждения" и "Удалить"
|
||||||
|
assert "Запрос подтверждения" in dialog_text, "Not a confirmation dialog"
|
||||||
|
|
||||||
|
# Ищем кнопку по data-testid
|
||||||
|
if confirm:
|
||||||
|
button = self.page.locator(RackLocators.CONFIRM_REMOVE_YES_BUTTON)
|
||||||
|
else:
|
||||||
|
button = self.page.locator(RackLocators.CONFIRM_REMOVE_NO_BUTTON)
|
||||||
|
|
||||||
|
# Проверяем, что кнопка найдена
|
||||||
|
assert button.count() > 0, "Button not found with selector"
|
||||||
|
|
||||||
|
# Кликаем на кнопку
|
||||||
|
button_text = button.first.text_content()
|
||||||
|
logger.debug("Clicking button with text: %s", button_text)
|
||||||
|
button.first.click()
|
||||||
|
self.wait_for_timeout(2000)
|
||||||
|
|
||||||
|
# Проверяем, что диалог закрылся
|
||||||
|
self.wait_for_timeout(1000)
|
||||||
|
|
||||||
|
logger.debug("Remove confirmation completed")
|
||||||
|
|
||||||
def get_available_tabs(self) -> list[str]:
|
def get_available_tabs(self) -> list[str]:
|
||||||
"""
|
"""
|
||||||
Возвращает список доступных вкладок.
|
Возвращает список доступных вкладок.
|
||||||
|
|
@ -72,7 +162,7 @@ class RackPage(BasePage):
|
||||||
tab_elements.first.wait_for(state="visible", timeout=5000)
|
tab_elements.first.wait_for(state="visible", timeout=5000)
|
||||||
|
|
||||||
total_count = tab_elements.count()
|
total_count = tab_elements.count()
|
||||||
logger.debug(f"Total top tab elements found: {total_count}")
|
logger.debug("Total top tab elements found: %d", total_count)
|
||||||
|
|
||||||
for i in range(total_count):
|
for i in range(total_count):
|
||||||
element = tab_elements.nth(i)
|
element = tab_elements.nth(i)
|
||||||
|
|
@ -84,9 +174,9 @@ class RackPage(BasePage):
|
||||||
tab_text = tab_text.strip()
|
tab_text = tab_text.strip()
|
||||||
if tab_text and tab_text not in tabs:
|
if tab_text and tab_text not in tabs:
|
||||||
tabs.append(tab_text)
|
tabs.append(tab_text)
|
||||||
logger.debug(f"Top tab found: '{tab_text}'")
|
logger.debug("Top tab found: '%s'", tab_text)
|
||||||
|
|
||||||
logger.debug(f"Available top tabs found: {tabs}")
|
logger.debug("Available top tabs found: %s", tabs)
|
||||||
return tabs
|
return tabs
|
||||||
|
|
||||||
def get_current_active_side(self) -> Optional[str]:
|
def get_current_active_side(self) -> Optional[str]:
|
||||||
|
|
@ -158,13 +248,13 @@ class RackPage(BasePage):
|
||||||
Переключается на указанную вкладку.
|
Переключается на указанную вкладку.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
tab_name: Название вкладки для переключения
|
tab_name (str): Название вкладки для переключения
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
AssertionError: Если вкладка не найдена или недоступна
|
AssertionError: Если вкладка не найдена или недоступна
|
||||||
"""
|
"""
|
||||||
|
|
||||||
logger.debug(f"Switching to tab '{tab_name}'...")
|
logger.debug("Switching to tab '%s'...", tab_name)
|
||||||
|
|
||||||
tab = self.page.locator(RackLocators.TAB_BY_NAME.format(tab_name))
|
tab = self.page.locator(RackLocators.TAB_BY_NAME.format(tab_name))
|
||||||
|
|
||||||
|
|
@ -172,7 +262,7 @@ class RackPage(BasePage):
|
||||||
|
|
||||||
# Проверяем активность ДО клика
|
# Проверяем активность ДО клика
|
||||||
if self.is_tab_active(tab_name):
|
if self.is_tab_active(tab_name):
|
||||||
logger.debug(f"Tab '{tab_name}' is already active")
|
logger.debug("Tab '%s' is already active", tab_name)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Находим первую видимую вкладку с нужным именем
|
# Находим первую видимую вкладку с нужным именем
|
||||||
|
|
@ -186,7 +276,7 @@ class RackPage(BasePage):
|
||||||
assert target_tab is not None, f"No visible/available tab '{tab_name}' found"
|
assert target_tab is not None, f"No visible/available tab '{tab_name}' found"
|
||||||
|
|
||||||
# Кликаем на вкладку
|
# Кликаем на вкладку
|
||||||
logger.debug(f"Clicking on tab '{tab_name}'...")
|
logger.debug("Clicking on tab '%s'...", tab_name)
|
||||||
target_tab.click()
|
target_tab.click()
|
||||||
|
|
||||||
# Ждем изменения активной вкладки
|
# Ждем изменения активной вкладки
|
||||||
|
|
@ -220,21 +310,21 @@ class RackPage(BasePage):
|
||||||
if main_container.count() == 0:
|
if main_container.count() == 0:
|
||||||
logger.warning("Main rack container not found")
|
logger.warning("Main rack container not found")
|
||||||
else:
|
else:
|
||||||
logger.debug(f"Main rack container found (count: {main_container.count()})")
|
logger.debug("Main rack container found (count: %d)", main_container.count())
|
||||||
expect(main_container.first).to_be_attached()
|
expect(main_container.first).to_be_attached()
|
||||||
|
|
||||||
# Проверяем наличие позиций юнитов
|
# Проверяем наличие позиций юнитов
|
||||||
unit_positions = self.page.locator(RackLocators.UNIT_POSITIONS)
|
unit_positions = self.page.locator(RackLocators.UNIT_POSITIONS)
|
||||||
if unit_positions.count() > 0:
|
if unit_positions.count() > 0:
|
||||||
logger.debug(f"Unit positions found: {unit_positions.count()}")
|
logger.debug("Unit positions found: %d", unit_positions.count())
|
||||||
if unit_positions.first.text_content():
|
if unit_positions.first.text_content():
|
||||||
content = unit_positions.first.text_content().strip()
|
content = unit_positions.first.text_content().strip()
|
||||||
logger.debug(f"First position: {content}")
|
logger.debug("First position: %s", content)
|
||||||
|
|
||||||
# Проверяем наличие кнопок добавления
|
# Проверяем наличие кнопок добавления
|
||||||
open_buttons = self.page.locator(RackLocators.ADD_CIRCLE_BUTTON)
|
open_buttons = self.page.locator(RackLocators.ADD_CIRCLE_BUTTON)
|
||||||
if open_buttons.count() > 0:
|
if open_buttons.count() > 0:
|
||||||
logger.debug(f"'add_circle' buttons found: {open_buttons.count()}")
|
logger.debug("'add_circle' buttons found: %d", open_buttons.count())
|
||||||
|
|
||||||
logger.debug("Rack interface loaded")
|
logger.debug("Rack interface loaded")
|
||||||
|
|
||||||
|
|
@ -254,8 +344,8 @@ class RackPage(BasePage):
|
||||||
device_title = first_device.get_attribute("title") or "No title"
|
device_title = first_device.get_attribute("title") or "No title"
|
||||||
|
|
||||||
logger.debug(
|
logger.debug(
|
||||||
f"Devices found: {device_count} "
|
"Devices found: %d (first: ID=%s, Title=%s)",
|
||||||
f"(first: ID={device_id}, Title={device_title})"
|
device_count, device_id, device_title
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
logger.debug("No devices detected")
|
logger.debug("No devices detected")
|
||||||
|
|
@ -263,6 +353,9 @@ class RackPage(BasePage):
|
||||||
def check_tab_switching(self) -> None:
|
def check_tab_switching(self) -> None:
|
||||||
"""
|
"""
|
||||||
Проверяет переключение между вкладками стойки.
|
Проверяет переключение между вкладками стойки.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
AssertionError: Если не удалось переключиться на все вкладки
|
||||||
"""
|
"""
|
||||||
|
|
||||||
logger.debug("Testing rack tab switching functionality...")
|
logger.debug("Testing rack tab switching functionality...")
|
||||||
|
|
@ -276,14 +369,14 @@ class RackPage(BasePage):
|
||||||
"Сервисы"
|
"Сервисы"
|
||||||
]
|
]
|
||||||
|
|
||||||
logger.debug(f"Defined tabs to test: {defined_tabs}")
|
logger.debug("Defined tabs to test: %s", defined_tabs)
|
||||||
|
|
||||||
successful_switches = 0
|
successful_switches = 0
|
||||||
failed_switches = []
|
failed_switches = []
|
||||||
|
|
||||||
# Тестируем переключение на каждую определенную вкладку
|
# Тестируем переключение на каждую определенную вкладку
|
||||||
for tab_name in defined_tabs:
|
for tab_name in defined_tabs:
|
||||||
logger.debug(f"Testing switch to tab '{tab_name}'...")
|
logger.debug("Testing switch to tab '%s'...", tab_name)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Переключаемся на вкладку
|
# Переключаемся на вкладку
|
||||||
|
|
@ -291,15 +384,15 @@ class RackPage(BasePage):
|
||||||
|
|
||||||
# Проверяем, что вкладка активна
|
# Проверяем, что вкладка активна
|
||||||
if self.is_tab_active(tab_name):
|
if self.is_tab_active(tab_name):
|
||||||
logger.debug(f"Successfully switched to tab '{tab_name}'")
|
logger.debug("Successfully switched to tab '%s'", tab_name)
|
||||||
successful_switches += 1
|
successful_switches += 1
|
||||||
else:
|
else:
|
||||||
logger.warning(f"Tab '{tab_name}' not active after switching")
|
logger.warning("Tab '%s' not active after switching", tab_name)
|
||||||
failed_switches.append(f"Tab '{tab_name}' is not active after click")
|
failed_switches.append(f"Tab '{tab_name}' is not active after click")
|
||||||
|
|
||||||
except (AssertionError, TimeoutError) as e:
|
except (AssertionError, TimeoutError) as e:
|
||||||
# Ловим только конкретные исключения, которые могут возникнуть при переключении вкладок
|
# Ловим только конкретные исключения, которые могут возникнуть при переключении вкладок
|
||||||
logger.error(f"Error switching to tab '{tab_name}': {e}")
|
logger.error("Error switching to tab '%s': %s", tab_name, e)
|
||||||
failed_switches.append(f"Tab '{tab_name}' error: {str(e)}")
|
failed_switches.append(f"Tab '{tab_name}' error: {str(e)}")
|
||||||
|
|
||||||
# Небольшая пауза между переключениями
|
# Небольшая пауза между переключениями
|
||||||
|
|
@ -307,12 +400,12 @@ class RackPage(BasePage):
|
||||||
|
|
||||||
# Формируем итоговый отчет
|
# Формируем итоговый отчет
|
||||||
logger.debug("=== TAB SWITCHING RESULTS ===")
|
logger.debug("=== TAB SWITCHING RESULTS ===")
|
||||||
logger.debug(f"Successful switches: {successful_switches}/{len(defined_tabs)}")
|
logger.debug("Successful switches: %d/%d", successful_switches, len(defined_tabs))
|
||||||
|
|
||||||
if failed_switches:
|
if failed_switches:
|
||||||
logger.debug("Failed switches:")
|
logger.debug("Failed switches:")
|
||||||
for failure in failed_switches:
|
for failure in failed_switches:
|
||||||
logger.debug(f" - {failure}")
|
logger.debug(" - %s", failure)
|
||||||
|
|
||||||
# Требуем успешного переключения на все определенные вкладки
|
# Требуем успешного переключения на все определенные вкладки
|
||||||
assert successful_switches == len(defined_tabs), (
|
assert successful_switches == len(defined_tabs), (
|
||||||
|
|
@ -322,14 +415,14 @@ class RackPage(BasePage):
|
||||||
f"Errors: {', '.join(failed_switches)}"
|
f"Errors: {', '.join(failed_switches)}"
|
||||||
)
|
)
|
||||||
|
|
||||||
logger.debug(f"All {successful_switches} defined tabs successfully switched!")
|
logger.debug("All %d defined tabs successfully switched!", successful_switches)
|
||||||
|
|
||||||
def is_tab_active(self, tab_name: str) -> bool:
|
def is_tab_active(self, tab_name: str) -> bool:
|
||||||
"""
|
"""
|
||||||
Проверяет, активна ли указанная вкладка.
|
Проверяет, активна ли указанная вкладка.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
tab_name: Название вкладки для проверки
|
tab_name (str): Название вкладки для проверки
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: True если вкладка активна, False в противном случае
|
bool: True если вкладка активна, False в противном случае
|
||||||
|
|
@ -341,10 +434,10 @@ class RackPage(BasePage):
|
||||||
if active_tab.count() > 0 and active_tab.first.is_visible():
|
if active_tab.count() > 0 and active_tab.first.is_visible():
|
||||||
active_text = active_tab.first.text_content()
|
active_text = active_tab.first.text_content()
|
||||||
if active_text and active_text.strip() == tab_name:
|
if active_text and active_text.strip() == tab_name:
|
||||||
logger.debug(f"Tab '{tab_name}' is active (via active tab class)")
|
logger.debug("Tab '%s' is active (via active tab class)", tab_name)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
logger.debug(f"Tab '{tab_name}' is not active")
|
logger.debug("Tab '%s' is not active", tab_name)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def should_be_panel_header(self, expected_toolbar_title_items: list[str]) -> None:
|
def should_be_panel_header(self, expected_toolbar_title_items: list[str]) -> None:
|
||||||
|
|
@ -352,7 +445,7 @@ class RackPage(BasePage):
|
||||||
Проверяет наличие и корректность заголовка панели.
|
Проверяет наличие и корректность заголовка панели.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
expected_toolbar_title_items: Ожидаемые элементы заголовка
|
expected_toolbar_title_items (list[str]): Ожидаемые элементы заголовка
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
AssertionError: Если заголовок панели не соответствует ожиданиям
|
AssertionError: Если заголовок панели не соответствует ожиданиям
|
||||||
|
|
@ -374,7 +467,12 @@ class RackPage(BasePage):
|
||||||
)
|
)
|
||||||
|
|
||||||
def should_be_rack_sides_displayed(self) -> None:
|
def should_be_rack_sides_displayed(self) -> None:
|
||||||
"""Проверка отображения и структуры сторон стойки."""
|
"""
|
||||||
|
Проверка отображения и структуры сторон стойки.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
AssertionError: Если стороны стойки не отображаются корректно
|
||||||
|
"""
|
||||||
|
|
||||||
logger.debug("Checking rack sides display and structure...")
|
logger.debug("Checking rack sides display and structure...")
|
||||||
|
|
||||||
|
|
@ -396,7 +494,7 @@ class RackPage(BasePage):
|
||||||
|
|
||||||
# Проверяем, какая сторона активна по умолчанию
|
# Проверяем, какая сторона активна по умолчанию
|
||||||
current_active = self.get_current_active_side()
|
current_active = self.get_current_active_side()
|
||||||
logger.debug(f"Current active side: {current_active}")
|
logger.debug("Current active side: %s", current_active)
|
||||||
|
|
||||||
# Дополнительная проверка устройств
|
# Дополнительная проверка устройств
|
||||||
self.check_physical_devices_presence()
|
self.check_physical_devices_presence()
|
||||||
|
|
@ -409,7 +507,7 @@ class RackPage(BasePage):
|
||||||
|
|
||||||
# Возвращаемся на исходную активную сторону
|
# Возвращаемся на исходную активную сторону
|
||||||
if current_active:
|
if current_active:
|
||||||
logger.debug(f"Returning to original active side: {current_active}")
|
logger.debug("Returning to original active side: %s", current_active)
|
||||||
if current_active == "Лицевая сторона":
|
if current_active == "Лицевая сторона":
|
||||||
front_side_button.click()
|
front_side_button.click()
|
||||||
else:
|
else:
|
||||||
|
|
@ -417,7 +515,7 @@ class RackPage(BasePage):
|
||||||
self.wait_for_timeout(1000)
|
self.wait_for_timeout(1000)
|
||||||
|
|
||||||
final_active = self.get_current_active_side()
|
final_active = self.get_current_active_side()
|
||||||
logger.debug(f"Final active side: {final_active}")
|
logger.debug("Final active side: %s", final_active)
|
||||||
|
|
||||||
logger.debug("All rack sides checks passed successfully")
|
logger.debug("All rack sides checks passed successfully")
|
||||||
|
|
||||||
|
|
@ -426,19 +524,37 @@ class RackPage(BasePage):
|
||||||
Проверяет наличие и функциональность кнопок тулбара.
|
Проверяет наличие и функциональность кнопок тулбара.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
AssertionError: Если кнопки недоступны или подсказки неверны.
|
AssertionError: Если кнопки недоступны или подсказки неверны
|
||||||
"""
|
"""
|
||||||
|
|
||||||
logger.debug("Checking toolbar buttons...")
|
logger.debug("Checking toolbar buttons...")
|
||||||
|
|
||||||
|
# Проверяем основные кнопки
|
||||||
self.toolbar.check_button_visibility("edit")
|
self.toolbar.check_button_visibility("edit")
|
||||||
self.toolbar.check_button_tooltip("edit", "Изменить")
|
self.toolbar.check_button_tooltip("edit", "Изменить")
|
||||||
|
|
||||||
|
# Кликаем на кнопку "Изменить" для проверки функциональности
|
||||||
self.toolbar.get_button_by_name("edit").click()
|
self.toolbar.get_button_by_name("edit").click()
|
||||||
|
|
||||||
|
# Проверяем новые кнопки тулбара
|
||||||
|
self.toolbar.check_button_visibility("replace")
|
||||||
|
self.toolbar.check_button_tooltip("replace", "Переместить")
|
||||||
|
|
||||||
|
self.toolbar.check_button_visibility("done")
|
||||||
|
self.toolbar.check_button_tooltip("done", "Сохранить")
|
||||||
|
|
||||||
|
self.toolbar.check_button_visibility("close")
|
||||||
|
self.toolbar.check_button_tooltip("close", "Отменить")
|
||||||
|
|
||||||
|
self.toolbar.check_button_visibility("remove")
|
||||||
|
self.toolbar.check_button_tooltip("remove", "Удалить")
|
||||||
|
|
||||||
def should_have_hide_rack_button(self) -> None:
|
def should_have_hide_rack_button(self) -> None:
|
||||||
"""
|
"""
|
||||||
Проверка кнопки "Скрыть стойку".
|
Проверка кнопки "Скрыть стойку".
|
||||||
Проверяет видимость, тултип, кликабельность и эффект скрытия стойки.
|
|
||||||
|
Raises:
|
||||||
|
AssertionError: Если кнопка не отображается или не работает
|
||||||
"""
|
"""
|
||||||
logger.debug("Checking 'Hide rack' button...")
|
logger.debug("Checking 'Hide rack' button...")
|
||||||
|
|
||||||
|
|
@ -465,7 +581,9 @@ class RackPage(BasePage):
|
||||||
def should_have_show_rack_button(self) -> None:
|
def should_have_show_rack_button(self) -> None:
|
||||||
"""
|
"""
|
||||||
Проверка кнопки "Показать стойку".
|
Проверка кнопки "Показать стойку".
|
||||||
Проверяет наличие, тултип, кликабельность и эффект показа стойки.
|
|
||||||
|
Raises:
|
||||||
|
AssertionError: Если кнопка не отображается или не работает
|
||||||
"""
|
"""
|
||||||
logger.debug("Checking 'Show rack' button...")
|
logger.debug("Checking 'Show rack' button...")
|
||||||
|
|
||||||
|
|
@ -496,56 +614,56 @@ class RackPage(BasePage):
|
||||||
Проверка структуры конкретной стороны стойки.
|
Проверка структуры конкретной стороны стойки.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
side_name: Название стороны для логов
|
side_name (str): Название стороны для логов
|
||||||
side_button: Локатор кнопки стороны
|
side_button: Локатор кнопки стороны
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
AssertionError: Если структура стороны некорректна
|
AssertionError: Если структура стороны некорректна
|
||||||
"""
|
"""
|
||||||
|
|
||||||
logger.debug(f"Checking {side_name}...")
|
logger.debug("Checking %s...", side_name)
|
||||||
|
|
||||||
# Логируем текущее состояние кнопки перед кликом
|
# Логируем текущее состояние кнопки перед кликом
|
||||||
button_classes = side_button.get_attribute("class") or ""
|
button_classes = side_button.get_attribute("class") or ""
|
||||||
logger.debug(f"Button classes before click: {button_classes}")
|
logger.debug("Button classes before click: %s", button_classes)
|
||||||
|
|
||||||
# Проверяем, активна ли уже эта сторона
|
# Проверяем, активна ли уже эта сторона
|
||||||
current_active = self.get_current_active_side()
|
current_active = self.get_current_active_side()
|
||||||
logger.debug(f"Current active side (before click): '{current_active}'")
|
logger.debug("Current active side (before click): '%s'", current_active)
|
||||||
|
|
||||||
if current_active == side_name:
|
if current_active == side_name:
|
||||||
logger.debug(f"{side_name} is already active")
|
logger.debug("%s is already active", side_name)
|
||||||
else:
|
else:
|
||||||
# Если не активна, кликаем для переключения
|
# Если не активна, кликаем для переключения
|
||||||
logger.debug(f"Switching to {side_name}...")
|
logger.debug("Switching to %s...", side_name)
|
||||||
side_button.click()
|
side_button.click()
|
||||||
# Даем время на перерисовку классов (увеличиваем время)
|
# Даем время на перерисовку классов (увеличиваем время)
|
||||||
self.wait_for_timeout(2500)
|
self.wait_for_timeout(2500)
|
||||||
|
|
||||||
# Проверяем классы после клика
|
# Проверяем классы после клика
|
||||||
button_classes_after = side_button.get_attribute("class") or ""
|
button_classes_after = side_button.get_attribute("class") or ""
|
||||||
logger.debug(f"Button classes after click: {button_classes_after}")
|
logger.debug("Button classes after click: %s", button_classes_after)
|
||||||
|
|
||||||
# Проверяем, что нужная сторона стала активной
|
# Проверяем, что нужная сторона стала активной
|
||||||
active_side = self.get_current_active_side()
|
active_side = self.get_current_active_side()
|
||||||
logger.debug(f"Active side after switching: '{active_side}'")
|
logger.debug("Active side after switching: '%s'", active_side)
|
||||||
|
|
||||||
assert active_side == side_name, \
|
assert active_side == side_name, \
|
||||||
f"Wrong side is active: '{active_side}', expected: '{side_name}'"
|
f"Wrong side is active: '{active_side}', expected: '{side_name}'"
|
||||||
|
|
||||||
logger.debug(f"{side_name} successfully activated")
|
logger.debug("%s successfully activated", side_name)
|
||||||
|
|
||||||
# Проверяем позиции юнитов
|
# Проверяем позиции юнитов
|
||||||
unit_positions = self.page.locator(RackLocators.UNIT_POSITIONS)
|
unit_positions = self.page.locator(RackLocators.UNIT_POSITIONS)
|
||||||
total_positions = unit_positions.count()
|
total_positions = unit_positions.count()
|
||||||
logger.debug(f"Total unit positions: {total_positions}")
|
logger.debug("Total unit positions: %d", total_positions)
|
||||||
assert total_positions > 0, f"No unit positions found on {side_name}"
|
assert total_positions > 0, f"No unit positions found on {side_name}"
|
||||||
|
|
||||||
# Проверяем юниты
|
# Проверяем юниты
|
||||||
all_units = self.page.locator(RackLocators.ALL_UNITS)
|
all_units = self.page.locator(RackLocators.ALL_UNITS)
|
||||||
all_units_count = all_units.count()
|
all_units_count = all_units.count()
|
||||||
units_per_side = all_units_count // 2
|
units_per_side = all_units_count // 2
|
||||||
logger.debug(f"Units on {side_name}: {units_per_side}")
|
logger.debug("Units on %s: %d", side_name, units_per_side)
|
||||||
|
|
||||||
# Проверяем устройства
|
# Проверяем устройства
|
||||||
devices = self.page.locator(RackLocators.DEVICE_ELEMENTS)
|
devices = self.page.locator(RackLocators.DEVICE_ELEMENTS)
|
||||||
|
|
@ -562,34 +680,39 @@ class RackPage(BasePage):
|
||||||
slots = first_device.locator(RackLocators.DEVICE_SLOTS)
|
slots = first_device.locator(RackLocators.DEVICE_SLOTS)
|
||||||
slot_count = slots.count()
|
slot_count = slots.count()
|
||||||
|
|
||||||
logger.debug(f"Devices found: {device_count} (showing first)")
|
logger.debug("Devices found: %d (showing first)", device_count)
|
||||||
logger.debug(f" Device: ID={device_id}")
|
logger.debug(" Device: ID=%s", device_id)
|
||||||
logger.debug(f" Title: {device_title}")
|
logger.debug(" Title: %s", device_title)
|
||||||
logger.debug(f" Classes: {device_classes}")
|
logger.debug(" Classes: %s", device_classes)
|
||||||
logger.debug(f" Slots: {slot_count}")
|
logger.debug(" Slots: %d", slot_count)
|
||||||
else:
|
else:
|
||||||
logger.debug("No devices detected")
|
logger.debug("No devices detected")
|
||||||
|
|
||||||
logger.debug(f"{side_name} check completed successfully")
|
logger.debug("%s check completed successfully", side_name)
|
||||||
|
|
||||||
|
def _handle_remove_confirmation_dialog(self) -> None:
|
||||||
|
"""Обрабатывает диалог подтверждения удаления."""
|
||||||
|
logger.debug("Handling remove confirmation dialog...")
|
||||||
|
self.confirm_remove_dialog(confirm=True)
|
||||||
|
|
||||||
def _wait_for_tab_activation(self, tab_name: str, timeout: int = 5000) -> None:
|
def _wait_for_tab_activation(self, tab_name: str, timeout: int = 5000) -> None:
|
||||||
"""
|
"""
|
||||||
Ожидает активации вкладки.
|
Ожидает активации вкладки.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
tab_name: Название вкладки для ожидания
|
tab_name (str): Название вкладки для ожидания
|
||||||
timeout: Время ожидания в миллисекундах
|
timeout (int, optional): Время ожидания в миллисекундах, по умолчанию 5000
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
AssertionError: Если вкладка не активирована в течение таймаута
|
AssertionError: Если вкладка не активирована в течение таймаута
|
||||||
"""
|
"""
|
||||||
|
|
||||||
logger.debug(f"Waiting for tab '{tab_name}' activation...")
|
logger.debug("Waiting for tab '%s' activation...", tab_name)
|
||||||
|
|
||||||
start_time = self.page.evaluate("Date.now()")
|
start_time = self.page.evaluate("Date.now()")
|
||||||
while self.page.evaluate("Date.now()") - start_time < timeout:
|
while self.page.evaluate("Date.now()") - start_time < timeout:
|
||||||
if self.is_tab_active(tab_name):
|
if self.is_tab_active(tab_name):
|
||||||
logger.debug(f"Tab '{tab_name}' successfully activated")
|
logger.debug("Tab '%s' successfully activated", tab_name)
|
||||||
return
|
return
|
||||||
self.wait_for_timeout(100)
|
self.wait_for_timeout(100)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ from components_derived.frames.create_child_element_frame import CreateChildElem
|
||||||
from pages.location_page import LocationPage
|
from pages.location_page import LocationPage
|
||||||
from pages.login_page import LoginPage
|
from pages.login_page import LoginPage
|
||||||
from pages.main_page import MainPage
|
from pages.main_page import MainPage
|
||||||
|
from pages.rack_page import RackPage
|
||||||
|
|
||||||
|
|
||||||
logger = get_logger("CREATE_RACK_ELEMENT_TEST")
|
logger = get_logger("CREATE_RACK_ELEMENT_TEST")
|
||||||
|
|
@ -46,24 +47,66 @@ class TestCreateRackElement:
|
||||||
# Мы на главной странице
|
# Мы на главной странице
|
||||||
self.main_page = MainPage(browser)
|
self.main_page = MainPage(browser)
|
||||||
self.main_page.should_be_navigation_panel()
|
self.main_page.should_be_navigation_panel()
|
||||||
self.main_page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
# Переходим к Объектам
|
# Переходим к Объектам
|
||||||
self.main_page.click_main_navigation_panel_item("Объекты")
|
self.main_page.click_main_navigation_panel_item("Объекты")
|
||||||
self.main_page.wait_for_timeout(2000)
|
self.main_page.wait_for_timeout(1000)
|
||||||
|
|
||||||
self.main_page.click_main_navigation_panel_item("test-zone")
|
self.main_page.click_main_navigation_panel_item("test-zone")
|
||||||
self.main_page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
# Создаем экземпляр страницы локации
|
# Создаем экземпляр страницы локации
|
||||||
self.location_page = LocationPage(browser)
|
self.location_page = LocationPage(browser)
|
||||||
|
|
||||||
#@pytest.mark.develop
|
@pytest.fixture
|
||||||
def test_create_rack_content(self, browser: Page) -> None:
|
def cleanup_racks(self, browser: Page):
|
||||||
"""Тест создания дочернего элемента типа 'Стойка'."""
|
"""Фикстура для очистки созданных стоек."""
|
||||||
|
# Список для хранения созданных в тесте стоек
|
||||||
|
created_racks = []
|
||||||
|
|
||||||
# Проверяем что кнопка "Создать" доступна
|
yield created_racks
|
||||||
self.location_page.should_be_toolbar_buttons()
|
|
||||||
|
# После завершения теста удаляем созданные стойки
|
||||||
|
if created_racks:
|
||||||
|
logger.debug(f"Cleaning up racks: {created_racks}")
|
||||||
|
|
||||||
|
self.main_page.wait_for_timeout(500)
|
||||||
|
self.main_page.click_subpanel_item("test-zone")
|
||||||
|
self.main_page.wait_for_timeout(1000)
|
||||||
|
|
||||||
|
# Удаляем каждую стойку если она существует
|
||||||
|
for rack_name in created_racks:
|
||||||
|
# Проверяем существование стойки
|
||||||
|
if self._check_rack_existance(browser, rack_name):
|
||||||
|
logger.debug(f"Deleting rack '{rack_name}'...")
|
||||||
|
|
||||||
|
# Переходим на страницу стойки для удаления
|
||||||
|
self.main_page.click_subpanel_item(rack_name, parent="test-zone")
|
||||||
|
self.main_page.wait_for_timeout(1000)
|
||||||
|
|
||||||
|
# Удаляем стойку
|
||||||
|
self._delete_rack_from_context_menu(browser, rack_name)
|
||||||
|
|
||||||
|
# Проверяем удаление
|
||||||
|
self.main_page.click_subpanel_item("test-zone")
|
||||||
|
self.main_page.wait_for_timeout(500)
|
||||||
|
|
||||||
|
# Дополнительная проверка удаления
|
||||||
|
rack_still_exists = self._check_rack_existance(browser, rack_name)
|
||||||
|
if rack_still_exists:
|
||||||
|
logger.error(f"Rack '{rack_name}' still exists after deletion attempt")
|
||||||
|
|
||||||
|
logger.debug("Racks cleanup completed")
|
||||||
|
else:
|
||||||
|
logger.debug("No racks to cleanup")
|
||||||
|
|
||||||
|
|
||||||
|
def _create_rack(self, browser: Page, rack_name: str) -> None:
|
||||||
|
"""Создает стойку.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
browser: Страница Playwright
|
||||||
|
rack_name: Имя стойки для создания
|
||||||
|
"""
|
||||||
|
logger.debug(f"Creating rack with name '{rack_name}'")
|
||||||
|
|
||||||
# Нажимаем кнопку "Создать" на тулбаре
|
# Нажимаем кнопку "Создать" на тулбаре
|
||||||
self.location_page.click_create_button()
|
self.location_page.click_create_button()
|
||||||
|
|
@ -80,19 +123,167 @@ class TestCreateRackElement:
|
||||||
# Открывается набор плашек для задания параметров стойки
|
# Открывается набор плашек для задания параметров стойки
|
||||||
rack_maker = RackObjectMaker(browser)
|
rack_maker = RackObjectMaker(browser)
|
||||||
|
|
||||||
# Проверяем заголовок формы создания
|
# Создаем объект данных стойки
|
||||||
|
rack_data = RackData(
|
||||||
|
name=rack_name,
|
||||||
|
height="42",
|
||||||
|
depth="1000"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Заполняем данные стойки
|
||||||
|
rack_maker.fill_rack_data(rack_data)
|
||||||
|
|
||||||
|
# Нажимаем кнопку создания
|
||||||
|
create_child_frame.click_add_button()
|
||||||
|
|
||||||
|
def _delete_rack_from_context_menu(self, browser: Page, rack_name: str) -> None:
|
||||||
|
"""Удаляет стойку через контекстное меню.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
browser: Страница Playwright
|
||||||
|
rack_name: Имя стойки для удаления
|
||||||
|
"""
|
||||||
|
logger.debug(f"Deleting rack '{rack_name}' from context menu...")
|
||||||
|
|
||||||
|
# 1. Находим элемент стойки в навигационной панели
|
||||||
|
rack_element = browser.locator(NavigationPanelLocators.TREEVIEW).get_by_text(rack_name, exact=True).first
|
||||||
|
|
||||||
|
# Прокручиваем до элемента если нужно
|
||||||
|
rack_element.scroll_into_view_if_needed()
|
||||||
|
self.main_page.wait_for_timeout(500)
|
||||||
|
|
||||||
|
# 2. Проверяем и нажимаем кнопку "Изменить"
|
||||||
|
logger.debug("Step 1: Clicking 'Edit' button...")
|
||||||
|
rack_page = RackPage(browser)
|
||||||
|
|
||||||
|
# Проверяем видимость кнопки
|
||||||
|
rack_page.toolbar.check_button_visibility("edit")
|
||||||
|
|
||||||
|
# Проверяем тултип кнопки
|
||||||
|
rack_page.toolbar.check_button_tooltip("edit", "Изменить")
|
||||||
|
|
||||||
|
# Кликаем на кнопку "Изменить"
|
||||||
|
rack_page.toolbar.get_button_by_name("edit").click()
|
||||||
|
|
||||||
|
logger.debug("Edit button clicked, waiting for edit form...")
|
||||||
|
|
||||||
|
# 3. Используем метод click_remove_button, который обрабатывает весь процесс удаления
|
||||||
|
# включая диалог подтверждения
|
||||||
|
logger.debug("Clicking remove button...")
|
||||||
|
rack_page.click_remove_button()
|
||||||
|
|
||||||
|
# 4. Проверяем уведомление об успешном удалении - требуется создать разработчику (заведена задача)
|
||||||
|
# Создаем экземпляр фрейма для доступа к alert компоненту
|
||||||
|
# create_child_frame = CreateChildElementFrame(browser)
|
||||||
|
|
||||||
|
# Проверяем наличие любого alert-окна (не обязательно точного текста)
|
||||||
|
# create_child_frame.alert.check_alert_presence("")
|
||||||
|
|
||||||
|
# Получаем текст alert, чтобы убедиться что удаление прошло успешно
|
||||||
|
# alert_text = create_child_frame.alert.get_text()
|
||||||
|
# logger.debug(f"Alert text after deletion: {alert_text}")
|
||||||
|
|
||||||
|
# Проверяем что в тексте есть указание на успешное удаление
|
||||||
|
# assert "удален" in alert_text.lower() or "успешно" in alert_text.lower()
|
||||||
|
|
||||||
|
# Закрываем alert
|
||||||
|
# create_child_frame.alert.close_alert()
|
||||||
|
|
||||||
|
logger.debug("Rack deletion completed")
|
||||||
|
|
||||||
|
def _perform_required_fields_test(self, create_child_frame, rack_maker, test_data):
|
||||||
|
"""Выполняет один тест валидации обязательных полей.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
create_child_frame: Фрейм создания дочернего элемента
|
||||||
|
rack_maker: Объект для работы со стойкой
|
||||||
|
test_data: Словарь с данными теста
|
||||||
|
"""
|
||||||
|
# Распаковываем данные теста
|
||||||
|
name_value = test_data["name"]
|
||||||
|
height_value = test_data["height"]
|
||||||
|
depth_value = test_data["depth"]
|
||||||
|
expected_alert_height = test_data["expected_alert_height"]
|
||||||
|
expected_alert_depth = test_data["expected_alert_depth"]
|
||||||
|
|
||||||
|
# Получаем контейнер формы
|
||||||
|
container_locator = create_child_frame.page.locator(RackLocators.FORM_INPUT_CONTAINER).nth(1)
|
||||||
|
|
||||||
|
logger.debug(f"Available fields:\
|
||||||
|
{list(create_child_frame.get_input_fields_locators(container_locator).keys())}")
|
||||||
|
|
||||||
|
# Проверяем и очищаем поле "Глубина (мм)" только если оно заполнено
|
||||||
|
logger.debug("Checking field: Глубина (мм)")
|
||||||
|
if create_child_frame.is_field_filled("Глубина (мм)", container_locator):
|
||||||
|
logger.debug("Field 'Глубина (мм)' is filled, performing clearing")
|
||||||
|
create_child_frame.clear_combobox_field("Глубина (мм)")
|
||||||
|
logger.debug("Clearing completed for 'Глубина (мм)'")
|
||||||
|
else:
|
||||||
|
logger.debug("Field 'Глубина (мм)' is already empty, skipping clearing")
|
||||||
|
|
||||||
|
# Проверяем и очищаем поле "Высота в юнитах" только если оно заполнено
|
||||||
|
logger.debug("Checking field: Высота в юнитах")
|
||||||
|
if create_child_frame.is_field_filled("Высота в юнитах", container_locator):
|
||||||
|
logger.debug("Field 'Высота в юнитах' is filled, performing clearing")
|
||||||
|
create_child_frame.clear_combobox_field("Высота в юнитах")
|
||||||
|
logger.debug("Clearing completed for 'Высота в юнитах'")
|
||||||
|
else:
|
||||||
|
logger.debug("Field 'Высота в юнитах' is already empty, skipping clearing")
|
||||||
|
|
||||||
|
# Создаем объект данных стойки
|
||||||
|
rack_data = RackData(
|
||||||
|
name=name_value,
|
||||||
|
height=height_value,
|
||||||
|
depth=depth_value
|
||||||
|
)
|
||||||
|
|
||||||
|
# Заполняем данные стойки
|
||||||
|
logger.debug(f"Setting test data - Name: '{name_value}', Height: '{height_value}', Depth: '{depth_value}'")
|
||||||
|
rack_maker.fill_rack_data(rack_data)
|
||||||
|
|
||||||
|
# Нажимаем кнопку создания
|
||||||
|
logger.debug("Submitting form for validation")
|
||||||
|
create_child_frame.click_add_button()
|
||||||
|
create_child_frame.wait_for_timeout(500)
|
||||||
|
|
||||||
|
# Проверяем валидацию полей
|
||||||
|
logger.debug("Checking validation results")
|
||||||
|
|
||||||
|
# Обрабатываем alert-окна
|
||||||
|
if not height_value:
|
||||||
|
logger.debug("Expecting height validation alert")
|
||||||
|
create_child_frame.alert.check_alert_presence(expected_alert_height)
|
||||||
|
create_child_frame.alert.close_alert_by_text(expected_alert_height)
|
||||||
|
logger.debug("Height alert handled")
|
||||||
|
|
||||||
|
if not depth_value:
|
||||||
|
logger.debug("Expecting depth validation alert")
|
||||||
|
create_child_frame.alert.check_alert_presence(expected_alert_depth)
|
||||||
|
create_child_frame.alert.close_alert_by_text(expected_alert_depth)
|
||||||
|
logger.debug("Depth alert handled")
|
||||||
|
|
||||||
|
# Проверяем подсветку обязательных полей
|
||||||
|
if height_value:
|
||||||
|
create_child_frame.check_field_error_not_highlighted("Высота в юнитах")
|
||||||
|
logger.debug("Height field validation passed")
|
||||||
|
else:
|
||||||
|
create_child_frame.check_field_error_highlighted("Высота в юнитах")
|
||||||
|
logger.debug("Height field validation failed as expected")
|
||||||
|
|
||||||
|
if depth_value:
|
||||||
|
create_child_frame.check_field_error_not_highlighted("Глубина (мм)")
|
||||||
|
logger.debug("Depth field validation passed")
|
||||||
|
else:
|
||||||
|
create_child_frame.check_field_error_highlighted("Глубина (мм)")
|
||||||
|
logger.debug("Depth field validation failed as expected")
|
||||||
|
|
||||||
|
# Проверяем, что остались на странице создания
|
||||||
create_child_frame.check_toolbar_title('Создать дочерний элемент в')
|
create_child_frame.check_toolbar_title('Создать дочерний элемент в')
|
||||||
|
logger.debug("Test completed successfully")
|
||||||
|
|
||||||
# Проверяем что после выбора 'Стойка' появляются специфичные поля
|
|
||||||
rack_maker.check_rack_fields_presence()
|
|
||||||
logger.debug("Rack-specific fields are displayed correctly")
|
|
||||||
|
|
||||||
create_child_frame.should_be_toolbar_buttons()
|
def test_create_rack_child_element(self, browser: Page, cleanup_racks) -> None:
|
||||||
|
|
||||||
#@pytest.mark.develop
|
|
||||||
def test_create_rack_child_element(self, browser: Page) -> None:
|
|
||||||
"""Тест создания дочернего элемента типа 'Стойка'."""
|
"""Тест создания дочернего элемента типа 'Стойка'."""
|
||||||
|
|
||||||
# Нажимаем кнопку "Создать" на тулбаре
|
# Нажимаем кнопку "Создать" на тулбаре
|
||||||
self.location_page.click_create_button()
|
self.location_page.click_create_button()
|
||||||
|
|
||||||
|
|
@ -120,33 +311,91 @@ class TestCreateRackElement:
|
||||||
cable_entry="сверху"
|
cable_entry="сверху"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Сохраняем имя стойки в переменную
|
||||||
|
rack_name = rack_data.name
|
||||||
|
cleanup_racks.append(rack_name)
|
||||||
|
|
||||||
# Заполняем данные стойки
|
# Заполняем данные стойки
|
||||||
rack_maker.fill_rack_data(rack_data)
|
rack_maker.fill_rack_data(rack_data)
|
||||||
|
|
||||||
# Нажимаем кнопку "Добавить"
|
# Нажимаем кнопку "Добавить"
|
||||||
create_child_frame.click_add_button()
|
create_child_frame.click_add_button()
|
||||||
create_child_frame.wait_for_timeout(2000)
|
|
||||||
|
# 1. Проверяем уведомление об успешном создании стойки - требуется создать разработчику (заведена задача)
|
||||||
|
# Проверяем наличие alert-окна
|
||||||
|
# create_child_frame.alert.check_alert_presence("")
|
||||||
|
|
||||||
|
# Получаем текст alert
|
||||||
|
# alert_text = create_child_frame.alert.get_text()
|
||||||
|
# logger.debug(f"Alert text after creation: {alert_text}")
|
||||||
|
|
||||||
|
# Проверяем, что в тексте есть указание на успешное создание
|
||||||
|
# assert "создан" in alert_text.lower() or "успешно" in alert_text.lower()
|
||||||
|
# assert final_rack_name in alert_text
|
||||||
|
|
||||||
|
# Закрываем alert
|
||||||
|
# create_child_frame.alert.close_alert()
|
||||||
|
|
||||||
|
# 2. Проверяем, что стойка создана и отображается
|
||||||
|
logger.debug(f"Verifying that rack '{rack_name}' was created...")
|
||||||
|
|
||||||
|
# Обновляем навигационную панель
|
||||||
|
self.main_page.click_main_navigation_panel_item("test-zone")
|
||||||
|
|
||||||
|
# Проверяем существование стойки в навигационной панели
|
||||||
|
rack_exists = self._check_rack_existance(browser, rack_name)
|
||||||
|
assert rack_exists, f"Rack '{rack_name}' should be visible in navigation panel after creation"
|
||||||
|
|
||||||
|
logger.debug(f"Rack '{rack_name}' is visible in navigation panel")
|
||||||
|
|
||||||
logger.debug("Test for creating 'Rack' child element completed successfully")
|
logger.debug("Test for creating 'Rack' child element completed successfully")
|
||||||
|
|
||||||
#@pytest.mark.develop
|
def test_create_rack_content(self, browser: Page) -> None:
|
||||||
def test_create_rack_with_duplicate_name(self, browser: Page) -> None:
|
"""Тест проверки содержимого формы создания стойки."""
|
||||||
"""
|
# Проверяем что кнопка "Создать" доступна
|
||||||
Тест создания стойки с уже существующим именем.
|
self.location_page.should_be_toolbar_buttons()
|
||||||
|
|
||||||
|
# Нажимаем кнопку "Создать" на тулбаре
|
||||||
|
self.location_page.click_create_button()
|
||||||
|
|
||||||
|
# Создаем фрейм создания дочернего элемента
|
||||||
|
create_child_frame = CreateChildElementFrame(browser)
|
||||||
|
|
||||||
|
# Нажимаем на плашку "Класс объекта учета"
|
||||||
|
create_child_frame.open_object_class_combobox()
|
||||||
|
|
||||||
|
# Из выпадающего меню выбираем пункт "Стойка"
|
||||||
|
create_child_frame.select_object_class("Стойка")
|
||||||
|
|
||||||
|
# Открывается набор плашек для задания параметров стойки
|
||||||
|
rack_maker = RackObjectMaker(browser)
|
||||||
|
|
||||||
|
# Проверяем заголовок формы создания
|
||||||
|
create_child_frame.check_toolbar_title('Создать дочерний элемент в')
|
||||||
|
|
||||||
|
# Проверяем что после выбора 'Стойка' появляются специфичные поля
|
||||||
|
rack_maker.check_rack_fields_presence()
|
||||||
|
logger.debug("Rack-specific fields are displayed correctly")
|
||||||
|
|
||||||
|
create_child_frame.should_be_toolbar_buttons()
|
||||||
|
|
||||||
|
def test_create_rack_with_duplicate_name(self, browser: Page, cleanup_racks) -> None:
|
||||||
|
"""Тест создания стойки с уже существующим именем.
|
||||||
|
|
||||||
Проверяет, что система корректно обрабатывает попытку создания
|
Проверяет, что система корректно обрабатывает попытку создания
|
||||||
стойки с именем, которое уже используется.
|
стойки с именем, которое уже используется.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
logger.debug("Starting test for creating rack with duplicate name")
|
logger.debug("Starting test for creating rack with duplicate name")
|
||||||
|
|
||||||
rack_name = "Test-Rack-01"
|
rack_name = "Test-Rack-Duplicate"
|
||||||
|
|
||||||
# Проверяем, существует ли уже стойка с таким именем
|
# Проверяем, существует ли уже стойка с таким именем
|
||||||
if not self._check_rack_existance(browser, rack_name):
|
if not self._check_rack_existance(browser, rack_name):
|
||||||
logger.debug(f"Rack with name '{rack_name}' not found. Creating first rack.")
|
logger.debug(f"Rack with name '{rack_name}' not found. Creating first rack.")
|
||||||
self._create_rack(browser, rack_name)
|
self._create_rack(browser, rack_name)
|
||||||
logger.debug(f"First rack with name '{rack_name}' created successfully")
|
logger.debug(f"First rack with name '{rack_name}' created successfully")
|
||||||
|
# Добавляем стойку в список для очистки
|
||||||
|
cleanup_racks.append(rack_name)
|
||||||
else:
|
else:
|
||||||
logger.debug(f"Rack with name '{rack_name}' already exists, proceeding to create second one")
|
logger.debug(f"Rack with name '{rack_name}' already exists, proceeding to create second one")
|
||||||
|
|
||||||
|
|
@ -195,136 +444,13 @@ class TestCreateRackElement:
|
||||||
|
|
||||||
logger.debug("System prevented creating rack with duplicate name")
|
logger.debug("System prevented creating rack with duplicate name")
|
||||||
|
|
||||||
def _perform_required_fields_test(self, create_child_frame, rack_maker, test_data):
|
def test_required_fields_validation(self, browser: Page, cleanup_racks) -> None:
|
||||||
"""Выполняет один тест валидации обязательных полей.
|
"""Тест проверки обязательных полей при создании стойки.
|
||||||
|
|
||||||
Args:
|
|
||||||
create_child_frame: Фрейм создания дочернего элемента
|
|
||||||
rack_maker: Объект для работы со стойкой
|
|
||||||
test_data: Словарь с данными теста
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Распаковываем данные теста
|
|
||||||
name_value = test_data["name"]
|
|
||||||
height_value = test_data["height"]
|
|
||||||
depth_value = test_data["depth"]
|
|
||||||
expected_alert_height = test_data["expected_alert_height"]
|
|
||||||
expected_alert_depth = test_data["expected_alert_depth"]
|
|
||||||
|
|
||||||
# Получаем контейнер формы
|
|
||||||
container_locator = create_child_frame.page.locator(RackLocators.FORM_INPUT_CONTAINER).nth(1)
|
|
||||||
fields_locators = create_child_frame.get_input_fields_locators(container_locator)
|
|
||||||
|
|
||||||
logger.debug(f"Available fields: {list(fields_locators.keys())}")
|
|
||||||
|
|
||||||
# Функция для проверки заполненности combobox поля
|
|
||||||
def is_field_filled(field_name: str) -> bool:
|
|
||||||
"""Проверяет, заполнено ли combobox поле."""
|
|
||||||
|
|
||||||
if field_name not in fields_locators:
|
|
||||||
logger.debug(f"Field '{field_name}' not found in fields_locators")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Получаем контейнер поля
|
|
||||||
field_container = fields_locators[field_name]
|
|
||||||
|
|
||||||
if not field_container.is_visible():
|
|
||||||
logger.debug(f"Field '{field_name}' not visible")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Проверяем наличие выбранного значения через v-chip (чип выбранного значения в combobox)
|
|
||||||
selected_chip = field_container.locator(".v-chip").first
|
|
||||||
|
|
||||||
# Проверяем наличие текста в поле
|
|
||||||
field_text = field_container.text_content() or ""
|
|
||||||
has_text = bool(field_text.strip())
|
|
||||||
|
|
||||||
# Проверяем наличие чипа
|
|
||||||
has_chip = selected_chip.count() > 0 and selected_chip.is_visible()
|
|
||||||
|
|
||||||
logger.debug(f"Field '{field_name}' - has chip: {has_chip}, has text: {has_text}")
|
|
||||||
|
|
||||||
return has_chip or has_text
|
|
||||||
|
|
||||||
# Проверяем и очищаем поле "Глубина (мм)" только если оно заполнено
|
|
||||||
logger.debug("Checking field: Глубина (мм)")
|
|
||||||
if is_field_filled("Глубина (мм)"):
|
|
||||||
logger.debug("Field 'Глубина (мм)' is filled, performing clearing")
|
|
||||||
create_child_frame.clear_combobox_field("Глубина (мм)")
|
|
||||||
logger.debug("Clearing completed for 'Глубина (мм)'")
|
|
||||||
else:
|
|
||||||
logger.debug("Field 'Глубина (мм)' is already empty, skipping clearing")
|
|
||||||
|
|
||||||
# Проверяем и очищаем поле "Высота в юнитах" только если оно заполнено
|
|
||||||
logger.debug("Checking field: Высота в юнитах")
|
|
||||||
if is_field_filled("Высота в юнитах"):
|
|
||||||
logger.debug("Field 'Высота в юнитах' is filled, performing clearing")
|
|
||||||
create_child_frame.clear_combobox_field("Высота в юнитах")
|
|
||||||
logger.debug("Clearing completed for 'Высота в юнитах'")
|
|
||||||
else:
|
|
||||||
logger.debug("Field 'Высота в юнитах' is already empty, skipping clearing")
|
|
||||||
|
|
||||||
# Создаем объект данных стойки
|
|
||||||
rack_data = RackData(
|
|
||||||
name=name_value,
|
|
||||||
height=height_value,
|
|
||||||
depth=depth_value
|
|
||||||
)
|
|
||||||
|
|
||||||
# Заполняем данные стойки
|
|
||||||
logger.debug(f"Setting test data - Name: '{name_value}', Height: '{height_value}', Depth: '{depth_value}'")
|
|
||||||
rack_maker.fill_rack_data(rack_data)
|
|
||||||
|
|
||||||
# Нажимаем кнопку создания
|
|
||||||
logger.debug("Submitting form for validation")
|
|
||||||
create_child_frame.click_add_button()
|
|
||||||
create_child_frame.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
# Проверяем валидацию полей
|
|
||||||
logger.debug("Checking validation results")
|
|
||||||
|
|
||||||
# Обрабатываем alert-окна
|
|
||||||
if not height_value:
|
|
||||||
logger.debug("Expecting height validation alert")
|
|
||||||
create_child_frame.alert.check_alert_presence(expected_alert_height)
|
|
||||||
create_child_frame.alert.close_alert_by_text(expected_alert_height)
|
|
||||||
logger.debug("Height alert handled")
|
|
||||||
|
|
||||||
if not depth_value:
|
|
||||||
logger.debug("Expecting depth validation alert")
|
|
||||||
create_child_frame.alert.check_alert_presence(expected_alert_depth)
|
|
||||||
create_child_frame.alert.close_alert_by_text(expected_alert_depth)
|
|
||||||
logger.debug("Depth alert handled")
|
|
||||||
|
|
||||||
# Проверяем подсветку обязательных полей
|
|
||||||
if height_value:
|
|
||||||
create_child_frame.check_field_error_not_highlighted("Высота в юнитах")
|
|
||||||
logger.debug("Height field validation passed")
|
|
||||||
else:
|
|
||||||
create_child_frame.check_field_error_highlighted("Высота в юнитах")
|
|
||||||
logger.debug("Height field validation failed as expected")
|
|
||||||
|
|
||||||
if depth_value:
|
|
||||||
create_child_frame.check_field_error_not_highlighted("Глубина (мм)")
|
|
||||||
logger.debug("Depth field validation passed")
|
|
||||||
else:
|
|
||||||
create_child_frame.check_field_error_highlighted("Глубина (мм)")
|
|
||||||
logger.debug("Depth field validation failed as expected")
|
|
||||||
|
|
||||||
# Проверяем, что остались на странице создания
|
|
||||||
create_child_frame.check_toolbar_title('Создать дочерний элемент в')
|
|
||||||
logger.debug("Test completed successfully")
|
|
||||||
|
|
||||||
@pytest.mark.develop
|
|
||||||
def test_required_fields_validation(self, browser: Page) -> None:
|
|
||||||
"""
|
|
||||||
Тест проверки обязательных полей при создании стойки.
|
|
||||||
|
|
||||||
Проверяет, что система корректно валидирует обязательные поля:
|
Проверяет, что система корректно валидирует обязательные поля:
|
||||||
- Поле 'Высота в юнитах' должно быть заполнено
|
- Поле 'Высота в юнитах' должно быть заполнено
|
||||||
- Поле 'Глубина (мм)' должно быть заполнено
|
- Поле 'Глубина (мм)' должно быть заполнено
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Текст сообщения alert-окна
|
# Текст сообщения alert-окна
|
||||||
expected_alert_text_height = "поле Высота в юнитах должно быть заполнено"
|
expected_alert_text_height = "поле Высота в юнитах должно быть заполнено"
|
||||||
expected_alert_text_depth = "поле Глубина (мм) должно быть заполнено"
|
expected_alert_text_depth = "поле Глубина (мм) должно быть заполнено"
|
||||||
|
|
@ -347,9 +473,9 @@ class TestCreateRackElement:
|
||||||
# Тестовые данные
|
# Тестовые данные
|
||||||
test_cases = [
|
test_cases = [
|
||||||
{
|
{
|
||||||
"name": "Test 1: Creating rack with default field values",
|
"name": "Test 1: Required fields are not filled",
|
||||||
"data": {
|
"data": {
|
||||||
"name": "",
|
"name": "Test-Rack-Required-01",
|
||||||
"height": "",
|
"height": "",
|
||||||
"depth": "",
|
"depth": "",
|
||||||
"expected_alert_height": expected_alert_text_height,
|
"expected_alert_height": expected_alert_text_height,
|
||||||
|
|
@ -357,19 +483,9 @@ class TestCreateRackElement:
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Test 2: Required fields are not filled",
|
"name": "Test 2: Only 'Height in units' field is filled",
|
||||||
"data": {
|
"data": {
|
||||||
"name": "",
|
"name": "Test-Rack-Required-02",
|
||||||
"height": "",
|
|
||||||
"depth": "",
|
|
||||||
"expected_alert_height": expected_alert_text_height,
|
|
||||||
"expected_alert_depth": expected_alert_text_depth
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Test 3: Only 'Height in units' field is filled",
|
|
||||||
"data": {
|
|
||||||
"name": "",
|
|
||||||
"height": "42",
|
"height": "42",
|
||||||
"depth": "",
|
"depth": "",
|
||||||
"expected_alert_height": expected_alert_text_height,
|
"expected_alert_height": expected_alert_text_height,
|
||||||
|
|
@ -377,9 +493,9 @@ class TestCreateRackElement:
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Test 4: Only 'Depth (mm)' field is filled",
|
"name": "Test 3: Only 'Depth (mm)' field is filled",
|
||||||
"data": {
|
"data": {
|
||||||
"name": "",
|
"name": "Test-Rack-Required-03",
|
||||||
"height": "",
|
"height": "",
|
||||||
"depth": "1000",
|
"depth": "1000",
|
||||||
"expected_alert_height": expected_alert_text_height,
|
"expected_alert_height": expected_alert_text_height,
|
||||||
|
|
@ -392,15 +508,15 @@ class TestCreateRackElement:
|
||||||
for test_case in test_cases:
|
for test_case in test_cases:
|
||||||
logger.debug(test_case["name"])
|
logger.debug(test_case["name"])
|
||||||
self._perform_required_fields_test(
|
self._perform_required_fields_test(
|
||||||
create_child_frame, rack_maker, test_case["data"]
|
create_child_frame, rack_maker, test_case["data"])
|
||||||
)
|
|
||||||
logger.debug("System prevented creating rack with invalid required fields")
|
logger.debug("System prevented creating rack with invalid required fields")
|
||||||
|
|
||||||
# 5. Тест: Заполняем все обязательные поля
|
# 4. Тест: Заполняем все обязательные поля
|
||||||
logger.debug("Test 5: All required fields are filled")
|
logger.debug("Test 4: All required fields are filled")
|
||||||
|
|
||||||
# Генерируем уникальное имя для финального теста
|
# Генерируем уникальное имя для финального теста
|
||||||
final_rack_name = "Test-Rack-Required-Final"
|
final_rack_name = "Test-Rack-Required-04"
|
||||||
|
cleanup_racks.append(final_rack_name)
|
||||||
|
|
||||||
# **ВАЖНО: Очищаем поля перед заполнением**
|
# **ВАЖНО: Очищаем поля перед заполнением**
|
||||||
logger.debug("Clearing fields before filling...")
|
logger.debug("Clearing fields before filling...")
|
||||||
|
|
@ -411,33 +527,26 @@ class TestCreateRackElement:
|
||||||
|
|
||||||
# Очищаем поле "Высота в юнитах" если оно заполнено
|
# Очищаем поле "Высота в юнитах" если оно заполнено
|
||||||
if "Высота в юнитах" in fields_locators:
|
if "Высота в юнитах" in fields_locators:
|
||||||
field_container = fields_locators["Высота в юнитах"]
|
if create_child_frame.is_field_filled("Высота в юнитах", container_locator):
|
||||||
# Проверяем наличие текста в поле
|
|
||||||
field_text = field_container.inner_text() or ""
|
|
||||||
if field_text.strip():
|
|
||||||
logger.debug("Clearing 'Высота в юнитах' field...")
|
logger.debug("Clearing 'Высота в юнитах' field...")
|
||||||
create_child_frame.clear_combobox_field("Высота в юнитах")
|
create_child_frame.clear_combobox_field("Высота в юнитах")
|
||||||
create_child_frame.wait_for_timeout(500)
|
create_child_frame.wait_for_timeout(500)
|
||||||
|
|
||||||
# Очищаем поле "Глубина (мм)" если оно заполнено
|
# Очищаем поле "Глубина (мм)" если оно заполнено
|
||||||
if "Глубина (мм)" in fields_locators:
|
if "Глубина (мм)" in fields_locators:
|
||||||
field_container = fields_locators["Глубина (мм)"]
|
if create_child_frame.is_field_filled("Глубина (мм)", container_locator):
|
||||||
# Проверяем наличие текста в поле
|
|
||||||
field_text = field_container.inner_text() or ""
|
|
||||||
if field_text.strip():
|
|
||||||
logger.debug("Clearing 'Глубина (мм)' field...")
|
logger.debug("Clearing 'Глубина (мм)' field...")
|
||||||
create_child_frame.clear_combobox_field("Глубина (мм)")
|
create_child_frame.clear_combobox_field("Глубина (мм)")
|
||||||
create_child_frame.wait_for_timeout(500)
|
create_child_frame.wait_for_timeout(500)
|
||||||
|
|
||||||
# Очищаем поле "Имя" если оно заполнено
|
# Очищаем поле "Имя" если оно заполнено
|
||||||
if "Имя" in fields_locators:
|
if "Имя" in fields_locators:
|
||||||
|
if create_child_frame.is_field_filled("Имя", container_locator):
|
||||||
|
logger.debug("Clearing 'Имя' field...")
|
||||||
|
# Специальная обработка для текстового поля
|
||||||
field_container = fields_locators["Имя"]
|
field_container = fields_locators["Имя"]
|
||||||
# Находим input внутри контейнера
|
|
||||||
input_field = field_container.locator("input").first
|
input_field = field_container.locator("input").first
|
||||||
if input_field.count() > 0:
|
if input_field.count() > 0:
|
||||||
current_value = input_field.input_value()
|
|
||||||
if current_value.strip():
|
|
||||||
logger.debug("Clearing 'Имя' field...")
|
|
||||||
input_field.click()
|
input_field.click()
|
||||||
input_field.press("Control+A")
|
input_field.press("Control+A")
|
||||||
input_field.press("Backspace")
|
input_field.press("Backspace")
|
||||||
|
|
@ -461,38 +570,48 @@ class TestCreateRackElement:
|
||||||
|
|
||||||
# Нажимаем кнопку создания
|
# Нажимаем кнопку создания
|
||||||
create_child_frame.click_add_button()
|
create_child_frame.click_add_button()
|
||||||
create_child_frame.wait_for_timeout(1000)
|
create_child_frame.wait_for_timeout(500)
|
||||||
|
|
||||||
# Проверяем, что НЕТ alert-окон для всех обязательных полей
|
# Проверяем уведомление об успешном создании стойки - требуется создать разработчику (заведена задача)
|
||||||
#create_child_frame.alert.check_alert_absence(expected_alert_text_height, 1000)
|
# Проверяем наличие alert-окна
|
||||||
#create_child_frame.alert.check_alert_absence(expected_alert_text_depth, 1000)
|
# create_child_frame.alert.check_alert_presence("")
|
||||||
#logger.debug("No alert windows for required fields appeared - all fields filled correctly")
|
|
||||||
|
|
||||||
# Проверяем, что ушли со страницы создания
|
# Получаем текст alert
|
||||||
try:
|
# alert_text = create_child_frame.alert.get_text()
|
||||||
create_child_frame.check_toolbar_title('Создать дочерний элемент в')
|
# logger.debug(f"Alert text after creation: {alert_text}")
|
||||||
logger.warning("Rack creation may not have completed successfully")
|
|
||||||
except AssertionError:
|
# Проверяем что в тексте есть указание на успешное создание
|
||||||
logger.debug("Creation page closed - rack successfully created")
|
# assert "создан" in alert_text.lower() or "успешно" in alert_text.lower()
|
||||||
|
# assert final_rack_name in alert_text
|
||||||
|
|
||||||
|
# Закрываем alert
|
||||||
|
# create_child_frame.alert.close_alert()
|
||||||
|
|
||||||
logger.debug("Required fields validation test completed successfully")
|
logger.debug("Required fields validation test completed successfully")
|
||||||
|
|
||||||
def _check_rack_existance(self, browser: Page, rack_name: str) -> bool:
|
# Вспомогательные методы проверки
|
||||||
"""Проверяет существование стойки."""
|
|
||||||
|
|
||||||
|
def _check_rack_existance(self, browser: Page, rack_name: str) -> bool:
|
||||||
|
"""Проверяет существование стойки.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
browser: Страница Playwright
|
||||||
|
rack_name: Имя стойки для проверки
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True если стойка существует, False в противном случае
|
||||||
|
"""
|
||||||
logger.debug(f"Checking existence of rack with name '{rack_name}'")
|
logger.debug(f"Checking existence of rack with name '{rack_name}'")
|
||||||
|
|
||||||
# Обновляем навигационную панель
|
# Обновляем навигационную панель
|
||||||
self.main_page.click_main_navigation_panel_item("Объекты")
|
self.main_page.click_main_navigation_panel_item("Объекты")
|
||||||
self.main_page.click_main_navigation_panel_item("Объекты")
|
self.main_page.click_main_navigation_panel_item("Объекты")
|
||||||
self.main_page.wait_for_timeout(1000)
|
|
||||||
self.main_page.click_subpanel_item("test-zone")
|
self.main_page.click_subpanel_item("test-zone")
|
||||||
self.main_page.wait_for_timeout(3000)
|
|
||||||
|
|
||||||
nav_panel_locator = NavigationPanelLocators.TREEVIEW
|
nav_panel_locator = NavigationPanelLocators.TREEVIEW
|
||||||
|
|
||||||
# Проверяем видимость элемента
|
# Проверяем видимость элемента
|
||||||
element = browser.locator(nav_panel_locator).get_by_text(rack_name).first
|
element = browser.locator(nav_panel_locator).get_by_text(rack_name, exact=True).first
|
||||||
|
|
||||||
if element.is_visible():
|
if element.is_visible():
|
||||||
logger.debug(f"Rack with name '{rack_name}' found")
|
logger.debug(f"Rack with name '{rack_name}' found")
|
||||||
|
|
@ -500,41 +619,3 @@ class TestCreateRackElement:
|
||||||
|
|
||||||
logger.debug(f"Rack with name '{rack_name}' not found")
|
logger.debug(f"Rack with name '{rack_name}' not found")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _create_rack(self, browser: Page, rack_name: str) -> None:
|
|
||||||
"""Создает стойку."""
|
|
||||||
|
|
||||||
logger.debug(f"Creating rack with name '{rack_name}'")
|
|
||||||
|
|
||||||
# Переходим обратно к созданию новой стойки
|
|
||||||
self.main_page.click_main_navigation_panel_item("test-zone")
|
|
||||||
self.main_page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
# Нажимаем кнопку "Создать" на тулбаре
|
|
||||||
self.location_page.click_create_button()
|
|
||||||
|
|
||||||
# Создаем фрейм создания дочернего элемента
|
|
||||||
create_child_frame = CreateChildElementFrame(browser)
|
|
||||||
|
|
||||||
# Нажимаем на плашку "Класс объекта учета"
|
|
||||||
create_child_frame.open_object_class_combobox()
|
|
||||||
|
|
||||||
# Из выпадающего меню выбираем пункт "Стойка"
|
|
||||||
create_child_frame.select_object_class("Стойка")
|
|
||||||
|
|
||||||
# Открывается набор плашек для задания параметров стойки
|
|
||||||
rack_maker = RackObjectMaker(browser)
|
|
||||||
|
|
||||||
# Создаем объект данных стойки
|
|
||||||
rack_data = RackData(
|
|
||||||
name=rack_name,
|
|
||||||
height="42",
|
|
||||||
depth="1000"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Заполняем данные стойки
|
|
||||||
rack_maker.fill_rack_data(rack_data)
|
|
||||||
|
|
||||||
# Нажимаем кнопку создания
|
|
||||||
create_child_frame.click_add_button()
|
|
||||||
create_child_frame.wait_for_timeout(2000)
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue