refactor: унификация работы с формами создания и редактирования стоек
- Добавлен базовый класс BaseRackForm с общей логикой для работы с формамиra6/create_rack
parent
b024fac0d8
commit
f075024386
|
|
@ -0,0 +1,469 @@
|
||||||
|
"""Базовый модуль для работы с формами стойки."""
|
||||||
|
|
||||||
|
import time
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import Optional, List, Dict, Any, Tuple
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
from playwright.sync_api import Page
|
||||||
|
from tools.logger import get_logger
|
||||||
|
from elements.text_input_element import TextInput
|
||||||
|
from components.base_component import BaseComponent
|
||||||
|
from components.dropdown_list_component import DropdownList
|
||||||
|
|
||||||
|
logger = get_logger("BASE_RACK_FORM")
|
||||||
|
logger.setLevel("INFO")
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class BaseRackData:
|
||||||
|
"""Базовый класс для хранения данных стойки."""
|
||||||
|
|
||||||
|
# Основные поля
|
||||||
|
name: str = ""
|
||||||
|
serial: str = ""
|
||||||
|
inventory: str = ""
|
||||||
|
comment: str = ""
|
||||||
|
|
||||||
|
# Combobox поля
|
||||||
|
cable_entry: str = ""
|
||||||
|
state: str = ""
|
||||||
|
depth: str = ""
|
||||||
|
usize: str = ""
|
||||||
|
|
||||||
|
# Combobox поля (справочники)
|
||||||
|
owner: str = ""
|
||||||
|
service_org: str = ""
|
||||||
|
project: str = ""
|
||||||
|
|
||||||
|
|
||||||
|
class BaseRackForm(BaseComponent, ABC):
|
||||||
|
"""Базовый компонент для работы с формами стойки."""
|
||||||
|
|
||||||
|
# Маппинг текстовых полей (должен быть переопределен в наследниках)
|
||||||
|
TEXT_FIELDS_MAPPING: Dict[str, Tuple[str, str]] = {}
|
||||||
|
TEXT_FIELDS_LOCATORS: Dict[str, str] = {}
|
||||||
|
|
||||||
|
# Маппинг combobox полей (должен быть переопределен в наследниках)
|
||||||
|
COMBOBOX_FIELDS_MAPPING: Dict[str, Tuple[str, str, str]] = {}
|
||||||
|
COMBOBOX_FIELDS_LOCATORS: Dict[str, str] = {}
|
||||||
|
|
||||||
|
# Дополнительные типы полей (checkbox и т.д.) - опционально
|
||||||
|
CHECKBOX_FIELDS_MAPPING: Dict[str, Tuple[str, str]] = {}
|
||||||
|
CHECKBOX_FIELDS_LOCATORS: Dict[str, str] = {}
|
||||||
|
|
||||||
|
def __init__(self, page: Page, form_container_locator: str) -> None:
|
||||||
|
"""Инициализирует базовый компонент формы стойки.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
page: Экземпляр страницы Playwright
|
||||||
|
form_container_locator: Локатор контейнера формы
|
||||||
|
"""
|
||||||
|
super().__init__(page)
|
||||||
|
self.page = page
|
||||||
|
self.form_container_locator = form_container_locator
|
||||||
|
self.content_items: Dict[str, Any] = {}
|
||||||
|
self.available_fields = None
|
||||||
|
|
||||||
|
# Инициализация полей формы
|
||||||
|
self._init_form_fields()
|
||||||
|
|
||||||
|
def _init_form_fields(self) -> None:
|
||||||
|
"""Инициализирует все поля формы."""
|
||||||
|
container_locator = self.page.locator(self.form_container_locator)
|
||||||
|
if container_locator.count() > 0:
|
||||||
|
self.available_fields = self.get_input_fields_locators(container_locator)
|
||||||
|
|
||||||
|
self._init_text_fields()
|
||||||
|
self._init_combobox_fields()
|
||||||
|
self._init_checkbox_fields()
|
||||||
|
|
||||||
|
def _init_text_fields(self) -> None:
|
||||||
|
"""Инициализирует текстовые поля формы."""
|
||||||
|
for field_label, (attr_name, widget_name) in self.TEXT_FIELDS_MAPPING.items():
|
||||||
|
locator = self.TEXT_FIELDS_LOCATORS.get(field_label)
|
||||||
|
if not locator:
|
||||||
|
continue
|
||||||
|
self._init_single_text_field(field_label, locator, widget_name)
|
||||||
|
|
||||||
|
def _init_single_text_field(self, field_label: str, locator: str, widget_name: str) -> None:
|
||||||
|
"""Инициализирует одно текстовое поле."""
|
||||||
|
try:
|
||||||
|
element = self.page.locator(locator).first
|
||||||
|
if element.count() > 0 and element.is_visible():
|
||||||
|
field_input = TextInput(self.page, element, widget_name)
|
||||||
|
self.content_items[widget_name] = field_input
|
||||||
|
logger.debug(f"Initialized text field: '{field_label}'")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error initializing text field '{field_label}': {e}")
|
||||||
|
|
||||||
|
def _init_combobox_fields(self) -> None:
|
||||||
|
"""Инициализирует combobox поля формы."""
|
||||||
|
for field_label, (attr_name, input_name, list_name) in self.COMBOBOX_FIELDS_MAPPING.items():
|
||||||
|
locator = self.COMBOBOX_FIELDS_LOCATORS.get(field_label)
|
||||||
|
if not locator:
|
||||||
|
continue
|
||||||
|
self._init_single_combobox_field(field_label, locator, input_name, list_name)
|
||||||
|
|
||||||
|
def _init_single_combobox_field(
|
||||||
|
self, field_label: str, locator: str, input_name: str, list_name: str
|
||||||
|
) -> None:
|
||||||
|
"""Инициализирует одно combobox поле."""
|
||||||
|
try:
|
||||||
|
element = self.page.locator(locator).first
|
||||||
|
if element.count() > 0 and element.is_visible():
|
||||||
|
field_input = TextInput(self.page, element, input_name)
|
||||||
|
self.content_items[input_name] = field_input
|
||||||
|
self.content_items[list_name] = DropdownList(self.page)
|
||||||
|
logger.debug(f"Initialized combobox field: '{field_label}'")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error initializing combobox field '{field_label}': {e}")
|
||||||
|
|
||||||
|
def _init_checkbox_fields(self) -> None:
|
||||||
|
"""Инициализирует checkbox поля формы (опционально)."""
|
||||||
|
if not self.CHECKBOX_FIELDS_MAPPING:
|
||||||
|
return
|
||||||
|
|
||||||
|
for field_label, (attr_name, widget_name) in self.CHECKBOX_FIELDS_MAPPING.items():
|
||||||
|
locator = self.CHECKBOX_FIELDS_LOCATORS.get(field_label)
|
||||||
|
if not locator:
|
||||||
|
continue
|
||||||
|
self._init_single_checkbox_field(field_label, locator, widget_name)
|
||||||
|
|
||||||
|
def _init_single_checkbox_field(self, field_label: str, locator: str, widget_name: str) -> None:
|
||||||
|
"""Инициализирует одно checkbox поле."""
|
||||||
|
try:
|
||||||
|
checkbox_input = self.page.locator(locator).first
|
||||||
|
if checkbox_input.count() == 0:
|
||||||
|
logger.debug(f"Checkbox '{field_label}' not found")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Импортируем здесь чтобы избежать циклических импортов
|
||||||
|
from elements.checkbox_element import Checkbox
|
||||||
|
|
||||||
|
checkbox = Checkbox(self.page, checkbox_input, widget_name)
|
||||||
|
self.content_items[widget_name] = checkbox
|
||||||
|
logger.debug(f"Initialized checkbox field: '{field_label}'")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error initializing checkbox '{field_label}': {e}")
|
||||||
|
|
||||||
|
def get_content_item(self, item_name: str) -> Any:
|
||||||
|
"""Возвращает элемент контента по имени."""
|
||||||
|
return self.content_items.get(item_name)
|
||||||
|
|
||||||
|
def clear_field(self, field_name: str) -> None:
|
||||||
|
"""Очищает указанное поле."""
|
||||||
|
logger.debug(f"Clearing field: '{field_name}'")
|
||||||
|
|
||||||
|
# Проверяем, не является ли поле чекбоксом
|
||||||
|
if field_name in self.CHECKBOX_FIELDS_LOCATORS:
|
||||||
|
logger.debug(f"Field '{field_name}' is a checkbox, skipping clear operation")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Получаем локатор поля
|
||||||
|
locator = self._get_field_locator(field_name)
|
||||||
|
if not locator:
|
||||||
|
logger.warning(f"Unknown field: {field_name}")
|
||||||
|
return
|
||||||
|
|
||||||
|
field_element = self.page.locator(locator).first
|
||||||
|
if field_element.count() == 0:
|
||||||
|
logger.debug(f"Field '{field_name}' not found")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Очистка в зависимости от типа поля
|
||||||
|
if field_name in self.TEXT_FIELDS_LOCATORS:
|
||||||
|
self._clear_text_field(field_element, field_name)
|
||||||
|
elif field_name in self.COMBOBOX_FIELDS_LOCATORS:
|
||||||
|
self._clear_combobox_field(field_element, field_name)
|
||||||
|
|
||||||
|
def _get_field_locator(self, field_name: str) -> Optional[str]:
|
||||||
|
"""Получает локатор поля по его названию."""
|
||||||
|
if field_name in self.COMBOBOX_FIELDS_LOCATORS:
|
||||||
|
return self.COMBOBOX_FIELDS_LOCATORS[field_name]
|
||||||
|
elif field_name in self.TEXT_FIELDS_LOCATORS:
|
||||||
|
return self.TEXT_FIELDS_LOCATORS[field_name]
|
||||||
|
elif field_name in self.CHECKBOX_FIELDS_LOCATORS:
|
||||||
|
return self.CHECKBOX_FIELDS_LOCATORS[field_name]
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _clear_text_field(self, field_element, field_name: str) -> None:
|
||||||
|
"""Очищает текстовое поле."""
|
||||||
|
try:
|
||||||
|
field_element.click()
|
||||||
|
field_element.page.keyboard.press("Control+A")
|
||||||
|
field_element.page.keyboard.press("Backspace")
|
||||||
|
self.wait_for_timeout(200)
|
||||||
|
logger.debug(f"Text field '{field_name}' cleared")
|
||||||
|
except Exception as e:
|
||||||
|
logger.debug(f"Could not clear text field '{field_name}': {e}")
|
||||||
|
|
||||||
|
def _clear_combobox_field(self, field_element, field_name: str) -> None:
|
||||||
|
"""Очищает combobox поле."""
|
||||||
|
try:
|
||||||
|
parent_container = field_element.locator(
|
||||||
|
"xpath=ancestor::div[contains(@class, 'v-input')]"
|
||||||
|
).first
|
||||||
|
|
||||||
|
if parent_container.count() == 0:
|
||||||
|
logger.debug(f"Parent container not found for field '{field_name}'")
|
||||||
|
return
|
||||||
|
|
||||||
|
clear_button = parent_container.locator(
|
||||||
|
".v-input__icon--clear button, .v-input__icon--append button, i.mdi-close-circle, i.mdi-close"
|
||||||
|
).first
|
||||||
|
|
||||||
|
if clear_button.count() > 0 and clear_button.is_visible():
|
||||||
|
clear_button.click()
|
||||||
|
self.wait_for_timeout(300)
|
||||||
|
logger.debug(f"Combobox field '{field_name}' cleared")
|
||||||
|
else:
|
||||||
|
logger.debug(f"Clear button not found for field '{field_name}'")
|
||||||
|
except Exception as e:
|
||||||
|
logger.debug(f"Error clearing combobox field '{field_name}': {e}")
|
||||||
|
|
||||||
|
def _scroll_to_element_in_dropdown(self, value: str) -> bool:
|
||||||
|
"""Скроллит выпадающий список до элемента с нужным текстом."""
|
||||||
|
logger.debug(f"Scrolling to find element with text: '{value}'")
|
||||||
|
|
||||||
|
dropdown_menu = self.page.locator("div.menuable__content__active").first
|
||||||
|
if dropdown_menu.count() == 0:
|
||||||
|
logger.error("Active dropdown menu not found")
|
||||||
|
return False
|
||||||
|
|
||||||
|
max_attempts = 10
|
||||||
|
attempts = 0
|
||||||
|
|
||||||
|
while attempts < max_attempts:
|
||||||
|
visible_items = dropdown_menu.locator("a.v-list__tile, div[role='listitem']").all()
|
||||||
|
|
||||||
|
if visible_items:
|
||||||
|
for item in visible_items:
|
||||||
|
item_text = item.text_content() or ""
|
||||||
|
if value in item_text:
|
||||||
|
logger.debug(f"Found element with text '{value}'")
|
||||||
|
item.scroll_into_view_if_needed()
|
||||||
|
self.wait_for_timeout(300)
|
||||||
|
return True
|
||||||
|
|
||||||
|
last_item = visible_items[-1]
|
||||||
|
last_item_text = last_item.text_content() or ""
|
||||||
|
logger.debug(f"Scrolling to last visible item: '{last_item_text}'")
|
||||||
|
last_item.scroll_into_view_if_needed()
|
||||||
|
self.wait_for_timeout(500)
|
||||||
|
else:
|
||||||
|
dropdown_menu.evaluate("(el) => el.scrollTop += 200")
|
||||||
|
self.wait_for_timeout(300)
|
||||||
|
|
||||||
|
attempts += 1
|
||||||
|
|
||||||
|
logger.warning(f"Element with text '{value}' not found after {max_attempts} attempts")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _fill_text_fields(self, rack_data: BaseRackData, results: Dict[str, int]) -> None:
|
||||||
|
"""Заполняет текстовые поля."""
|
||||||
|
for field_label, (attr_name, field_name) in self.TEXT_FIELDS_MAPPING.items():
|
||||||
|
value = getattr(rack_data, attr_name, "")
|
||||||
|
if not value or not str(value).strip():
|
||||||
|
continue
|
||||||
|
self._fill_single_text_field(field_label, field_name, value, results)
|
||||||
|
|
||||||
|
def _fill_single_text_field(
|
||||||
|
self, field_label: str, field_name: str, value: str, results: Dict[str, int]
|
||||||
|
) -> None:
|
||||||
|
"""Заполняет одно текстовое поле."""
|
||||||
|
try:
|
||||||
|
input_field = self.get_content_item(field_name)
|
||||||
|
if input_field:
|
||||||
|
input_field.input_value(value)
|
||||||
|
results["text_fields_filled"] += 1
|
||||||
|
logger.debug(f"Field '{field_label}' filled: '{value}'")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error filling field '{field_label}': {e}")
|
||||||
|
|
||||||
|
def _fill_combobox_fields(self, rack_data: BaseRackData, results: Dict[str, int]) -> None:
|
||||||
|
"""Заполняет combobox поля."""
|
||||||
|
for field_label, (attr_name, input_name, list_name) in self.COMBOBOX_FIELDS_MAPPING.items():
|
||||||
|
value = getattr(rack_data, attr_name, "")
|
||||||
|
if not value or not str(value).strip():
|
||||||
|
continue
|
||||||
|
self._fill_single_combobox_field(field_label, input_name, list_name, value, results)
|
||||||
|
|
||||||
|
def _fill_single_combobox_field(
|
||||||
|
self, field_label: str, input_name: str, list_name: str, value: str, results: Dict[str, int]
|
||||||
|
) -> None:
|
||||||
|
"""Заполняет одно combobox поле."""
|
||||||
|
try:
|
||||||
|
combobox_field = self.get_content_item(input_name)
|
||||||
|
if not combobox_field:
|
||||||
|
logger.warning(f"Field '{field_label}' input not found")
|
||||||
|
return
|
||||||
|
|
||||||
|
combobox_field.click(force=True)
|
||||||
|
self.wait_for_timeout(1000)
|
||||||
|
|
||||||
|
if not self._scroll_to_element_in_dropdown(value):
|
||||||
|
logger.error(f"Could not find element with text '{value}' after scrolling")
|
||||||
|
self.page.mouse.click(10, 10)
|
||||||
|
self.wait_for_timeout(300)
|
||||||
|
return
|
||||||
|
|
||||||
|
dropdown_menu = self.page.locator("div.menuable__content__active").first
|
||||||
|
item_locator = self._find_dropdown_item(dropdown_menu, value)
|
||||||
|
|
||||||
|
if item_locator and item_locator.count() > 0:
|
||||||
|
item_locator.scroll_into_view_if_needed()
|
||||||
|
self.wait_for_timeout(300)
|
||||||
|
item_locator.click()
|
||||||
|
results["combobox_fields_filled"] += 1
|
||||||
|
logger.debug(f"Field '{field_label}' set: '{value}'")
|
||||||
|
self.wait_for_timeout(500)
|
||||||
|
else:
|
||||||
|
logger.error(f"Item with text '{value}' not found in dropdown for field '{field_label}'")
|
||||||
|
self.page.mouse.click(10, 10)
|
||||||
|
self.wait_for_timeout(300)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error filling combobox '{field_label}': {e}")
|
||||||
|
self.page.mouse.click(10, 10)
|
||||||
|
|
||||||
|
def _find_dropdown_item(self, dropdown_menu, value: str):
|
||||||
|
"""Находит элемент в выпадающем списке."""
|
||||||
|
item_locator = dropdown_menu.locator(f"a.v-list__tile:has-text('{value}')").first
|
||||||
|
if item_locator.count() == 0:
|
||||||
|
item_locator = dropdown_menu.locator(f"span:has-text('{value}')").first
|
||||||
|
if item_locator.count() == 0:
|
||||||
|
item_locator = dropdown_menu.locator(f"div[role='listitem']:has-text('{value}')").first
|
||||||
|
return item_locator
|
||||||
|
|
||||||
|
def _fill_checkbox_fields(self, rack_data: BaseRackData, results: Dict[str, int]) -> None:
|
||||||
|
"""Заполняет checkbox поля (опционально)."""
|
||||||
|
if not hasattr(self, 'CHECKBOX_FIELDS_MAPPING'):
|
||||||
|
return
|
||||||
|
|
||||||
|
for field_label, (attr_name, widget_name) in self.CHECKBOX_FIELDS_MAPPING.items():
|
||||||
|
value = getattr(rack_data, attr_name, None)
|
||||||
|
if value is None:
|
||||||
|
continue
|
||||||
|
self._fill_single_checkbox_field(field_label, widget_name, value, results)
|
||||||
|
|
||||||
|
def _fill_single_checkbox_field(
|
||||||
|
self, field_label: str, widget_name: str, value: bool, results: Dict[str, int]
|
||||||
|
) -> None:
|
||||||
|
"""Заполняет одно checkbox поле."""
|
||||||
|
try:
|
||||||
|
checkbox = self.get_content_item(widget_name)
|
||||||
|
if not checkbox:
|
||||||
|
logger.warning(f"Checkbox '{field_label}' not found")
|
||||||
|
return
|
||||||
|
|
||||||
|
if value:
|
||||||
|
checkbox.check(force=True)
|
||||||
|
logger.debug(f"Checkbox '{field_label}' checked")
|
||||||
|
else:
|
||||||
|
checkbox.uncheck(force=True)
|
||||||
|
logger.debug(f"Checkbox '{field_label}' unchecked")
|
||||||
|
|
||||||
|
results["checkboxes_set"] += 1
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error setting checkbox '{field_label}': {e}")
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def fill_rack_data(self, rack_data: BaseRackData) -> Dict[str, int]:
|
||||||
|
"""Абстрактный метод для заполнения данных стойки."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def is_field_highlighted_as_error(self, field_name: str) -> bool:
|
||||||
|
"""Проверяет, подсвечено ли поле как ошибочное."""
|
||||||
|
# Для чекбоксов не проверяем ошибки
|
||||||
|
if field_name in self.CHECKBOX_FIELDS_LOCATORS:
|
||||||
|
return False
|
||||||
|
|
||||||
|
locator = self._get_field_locator(field_name)
|
||||||
|
if not locator:
|
||||||
|
return False
|
||||||
|
|
||||||
|
field_element = self.page.locator(locator).first
|
||||||
|
if field_element.count() == 0:
|
||||||
|
logger.debug(f"Field '{field_name}' not found")
|
||||||
|
return False
|
||||||
|
|
||||||
|
parent_input = field_element.locator(
|
||||||
|
"xpath=ancestor::div[contains(@class, 'v-input')]"
|
||||||
|
).first
|
||||||
|
|
||||||
|
if parent_input.count() > 0:
|
||||||
|
class_attr = parent_input.get_attribute("class") or ""
|
||||||
|
is_error = "v-input--error" in class_attr or "error--text" in class_attr
|
||||||
|
logger.debug(f"Field '{field_name}' error state: {is_error}")
|
||||||
|
return is_error
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def verify_required_fields_highlighted(self, field_names: List[str]) -> Dict[str, bool]:
|
||||||
|
"""Проверяет, что указанные поля подсвечены как обязательные."""
|
||||||
|
results = {}
|
||||||
|
for field_name in field_names:
|
||||||
|
results[field_name] = self.is_field_highlighted_as_error(field_name)
|
||||||
|
logger.debug(f"Field '{field_name}' highlighted: {results[field_name]}")
|
||||||
|
return results
|
||||||
|
|
||||||
|
def wait_for_field_error(self, field_name: str, timeout: int = 5000) -> bool:
|
||||||
|
"""Ожидает появления подсветки ошибки на поле."""
|
||||||
|
if field_name in self.CHECKBOX_FIELDS_LOCATORS:
|
||||||
|
return False
|
||||||
|
|
||||||
|
start_time = time.time()
|
||||||
|
while (time.time() - start_time) * 1000 < timeout:
|
||||||
|
if self.is_field_highlighted_as_error(field_name):
|
||||||
|
return True
|
||||||
|
self.wait_for_timeout(200)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_field_value(self, field_name: str) -> Optional[str]:
|
||||||
|
"""Получает значение поля."""
|
||||||
|
# Для чекбоксов
|
||||||
|
if field_name in self.CHECKBOX_FIELDS_LOCATORS:
|
||||||
|
for field_label, (attr_name, widget_name) in self.CHECKBOX_FIELDS_MAPPING.items():
|
||||||
|
if attr_name == field_name or field_label == field_name:
|
||||||
|
checkbox = self.get_content_item(widget_name)
|
||||||
|
if checkbox:
|
||||||
|
return str(checkbox.is_checked())
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Для текстовых полей
|
||||||
|
if field_name in self.TEXT_FIELDS_LOCATORS:
|
||||||
|
for field_label, (attr_name, widget_name) in self.TEXT_FIELDS_MAPPING.items():
|
||||||
|
if attr_name == field_name or field_label == field_name:
|
||||||
|
input_field = self.get_content_item(widget_name)
|
||||||
|
if input_field:
|
||||||
|
return input_field.get_input_value()
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Для combobox полей
|
||||||
|
return self._get_combobox_value(field_name)
|
||||||
|
|
||||||
|
def _get_combobox_value(self, field_name: str) -> Optional[str]:
|
||||||
|
"""Получает значение combobox поля."""
|
||||||
|
locator = self.COMBOBOX_FIELDS_LOCATORS.get(field_name)
|
||||||
|
if not locator:
|
||||||
|
for field_label, (attr_name, input_name, _) in self.COMBOBOX_FIELDS_MAPPING.items():
|
||||||
|
if attr_name == field_name or field_label == field_name:
|
||||||
|
input_field = self.get_content_item(input_name)
|
||||||
|
if input_field:
|
||||||
|
selections = input_field.element.locator(
|
||||||
|
"xpath=ancestor::div[contains(@class, 'v-select__selections')]"
|
||||||
|
).first
|
||||||
|
if selections.count() > 0:
|
||||||
|
value_span = selections.locator("span").first
|
||||||
|
return value_span.text_content() or ""
|
||||||
|
return None
|
||||||
|
|
||||||
|
element = self.page.locator(locator).first
|
||||||
|
if element.count() > 0:
|
||||||
|
selections = element.locator(
|
||||||
|
"xpath=ancestor::div[contains(@class, 'v-select__selections')]"
|
||||||
|
).first
|
||||||
|
if selections.count() > 0:
|
||||||
|
value_span = selections.locator("span").first
|
||||||
|
return value_span.text_content() or ""
|
||||||
|
return None
|
||||||
Loading…
Reference in New Issue