Восстановление затертых коммитов
parent
d7cbedafa4
commit
47d5306c5d
|
|
@ -28,13 +28,22 @@ class EventPanelComponent(BaseComponent):
|
||||||
self.states_tab = TabButton(page, self.page.locator(EventPanelLocators.TAB_STATES), "states_tab")
|
self.states_tab = TabButton(page, self.page.locator(EventPanelLocators.TAB_STATES), "states_tab")
|
||||||
self.actions_tab = TabButton(page, self.page.locator(EventPanelLocators.TAB_ACTIONS), "actions_tab")
|
self.actions_tab = TabButton(page, self.page.locator(EventPanelLocators.TAB_ACTIONS), "actions_tab")
|
||||||
self.events_tab = TabButton(page, self.page.locator(EventPanelLocators.TAB_EVENTS), "events_tab")
|
self.events_tab = TabButton(page, self.page.locator(EventPanelLocators.TAB_EVENTS), "events_tab")
|
||||||
self.maintenance_tab = TabButton(page, self.page.locator(EventPanelLocators.TAB_MAINTENANCE), "maintenance_tab")
|
self.maintenance_tab = TabButton(page,
|
||||||
|
self.page.locator(EventPanelLocators.TAB_MAINTENANCE), "maintenance_tab")
|
||||||
self.system_log_tab = TabButton(page, self.page.locator(EventPanelLocators.TAB_SYSTEM_LOG), "system_log_tab")
|
self.system_log_tab = TabButton(page, self.page.locator(EventPanelLocators.TAB_SYSTEM_LOG), "system_log_tab")
|
||||||
|
|
||||||
self.unknown_reason_button = TooltipButton(page, self.page.locator(EventPanelLocators.BUTTONS_EVENT).nth(0), "unknown_reason_button")
|
self.unknown_reason_button = TooltipButton(page,
|
||||||
self.warning_button = TooltipButton(page, self.page.locator(EventPanelLocators.BUTTONS_EVENT).nth(1), "warning_button")
|
self.page.locator(EventPanelLocators.BUTTONS_EVENT).nth(0),
|
||||||
self.damage_button = TooltipButton(page, self.page.locator(EventPanelLocators.BUTTONS_EVENT).nth(2), "damage_button")
|
"unknown_reason_button")
|
||||||
self.failure_button = TooltipButton(page, self.page.locator(EventPanelLocators.BUTTONS_EVENT).nth(3), "failure_button")
|
self.warning_button = TooltipButton(page,
|
||||||
|
self.page.locator(EventPanelLocators.BUTTONS_EVENT).nth(1),
|
||||||
|
"warning_button")
|
||||||
|
self.damage_button = TooltipButton(page,
|
||||||
|
self.page.locator(EventPanelLocators.BUTTONS_EVENT).nth(2),
|
||||||
|
"damage_button")
|
||||||
|
self.failure_button = TooltipButton(page,
|
||||||
|
self.page.locator(EventPanelLocators.BUTTONS_EVENT).nth(3),
|
||||||
|
"failure_button")
|
||||||
|
|
||||||
buttons_service_locators = self.page.locator(EventPanelLocators.BUTTONS_SERVICE).get_by_role("button").all()
|
buttons_service_locators = self.page.locator(EventPanelLocators.BUTTONS_SERVICE).get_by_role("button").all()
|
||||||
self.search_button = Button(page, buttons_service_locators[0], "search_button")
|
self.search_button = Button(page, buttons_service_locators[0], "search_button")
|
||||||
|
|
@ -53,7 +62,7 @@ class EventPanelComponent(BaseComponent):
|
||||||
|
|
||||||
button_locator = self.page.locator(EventPanelLocators.TAB_EXPAND_BUTTONS).\
|
button_locator = self.page.locator(EventPanelLocators.TAB_EXPAND_BUTTONS).\
|
||||||
get_by_role("button").filter(has_text='expand_more')
|
get_by_role("button").filter(has_text='expand_more')
|
||||||
button_locator.click()
|
button_locator.click(force=True)
|
||||||
|
|
||||||
def click_system_log_tab(self) -> SystemLogEventsContainer:
|
def click_system_log_tab(self) -> SystemLogEventsContainer:
|
||||||
"""Выполняет нажатие tab-кнопки Системный журнал."""
|
"""Выполняет нажатие tab-кнопки Системный журнал."""
|
||||||
|
|
@ -68,7 +77,7 @@ class EventPanelComponent(BaseComponent):
|
||||||
"""Выполняет нажатие кнопки пользователя."""
|
"""Выполняет нажатие кнопки пользователя."""
|
||||||
|
|
||||||
self.should_be_user_button()
|
self.should_be_user_button()
|
||||||
self.user_button.click()
|
self.user_button.click(force=True)
|
||||||
|
|
||||||
user_card = UserCard(self.page)
|
user_card = UserCard(self.page)
|
||||||
return user_card
|
return user_card
|
||||||
|
|
@ -155,8 +164,10 @@ class EventPanelComponent(BaseComponent):
|
||||||
self.states_tab.check_have_text('Состояния', "Tab button with text Состояния is missing on event panel")
|
self.states_tab.check_have_text('Состояния', "Tab button with text Состояния is missing on event panel")
|
||||||
self.actions_tab.check_have_text('Действия',"Tab button with text Действия is missing on event panel")
|
self.actions_tab.check_have_text('Действия',"Tab button with text Действия is missing on event panel")
|
||||||
self.events_tab.check_have_text('События', "Tab button with text События is missing on event panel")
|
self.events_tab.check_have_text('События', "Tab button with text События is missing on event panel")
|
||||||
self.maintenance_tab.check_have_text('Обслуживание', "Tab button with text Обслуживание is missing on event panel")
|
self.maintenance_tab.check_have_text('Обслуживание',
|
||||||
self.system_log_tab.check_have_text('Системный журнал', "Tab button with text Системный журнал is missing on event panel")
|
"Tab button with text Обслуживание is missing on event panel")
|
||||||
|
self.system_log_tab.check_have_text('Системный журнал',
|
||||||
|
"Tab button with text Системный журнал is missing on event panel")
|
||||||
|
|
||||||
def should_be_event_buttons(self) -> None:
|
def should_be_event_buttons(self) -> None:
|
||||||
"""Проверяет наличие блока кнопок-счетчиков событий."""
|
"""Проверяет наличие блока кнопок-счетчиков событий."""
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
"""Модуль expand_button_component содержит класс для работы с кнопкой расширения/уменьшения рабочей области вкладки
|
||||||
|
на странице."""
|
||||||
|
|
||||||
|
from playwright.sync_api import Page
|
||||||
|
from tools.logger import get_logger
|
||||||
|
from elements.button_element import Button
|
||||||
|
from components.base_component import BaseComponent
|
||||||
|
|
||||||
|
logger = get_logger("EXPAND_BUTTON")
|
||||||
|
|
||||||
|
|
||||||
|
class ExpandButton(BaseComponent):
|
||||||
|
"""Класс для работы с кнопкой расширения/сжатия рабочей области вкладки на странице.
|
||||||
|
|
||||||
|
"""
|
||||||
|
def __init__(self, page: Page):
|
||||||
|
"""Инициализирует компонент.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
page: Экземпляр страницы Playwright.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__(page)
|
||||||
|
|
||||||
|
self.expand_work_area_button_locator = page.get_by_role("button").filter(has_text="navigate_")
|
||||||
|
self.expand_work_area_button = Button(page,
|
||||||
|
self.expand_work_area_button_locator,
|
||||||
|
"expand_work_area_button")
|
||||||
|
|
||||||
|
# Действия:
|
||||||
|
def expand(self) -> None:
|
||||||
|
"""Нажатие кнопки для расширения рабочей области вкладки"""
|
||||||
|
|
||||||
|
can_do_expand = self.is_in_expand_state()
|
||||||
|
|
||||||
|
if can_do_expand:
|
||||||
|
self.expand_work_area_button.click()
|
||||||
|
else:
|
||||||
|
assert False, "Work area already expanded"
|
||||||
|
|
||||||
|
def reduce(self) -> None:
|
||||||
|
"""Нажатие кнопки для сжатия рабочей области вкладки"""
|
||||||
|
|
||||||
|
can_do_expand = self.is_in_expand_state()
|
||||||
|
|
||||||
|
if can_do_expand:
|
||||||
|
assert False, "Work area already reduced"
|
||||||
|
else:
|
||||||
|
self.expand_work_area_button.click()
|
||||||
|
|
||||||
|
# Проверки:
|
||||||
|
def is_in_expand_state(self) -> bool:
|
||||||
|
"""Проверяет состояние кнопки, способность ее увеличить размер рабочей области вкладки"""
|
||||||
|
|
||||||
|
button_state = self.expand_work_area_button_locator.text_content()
|
||||||
|
if button_state.find("before") != -1:
|
||||||
|
return True
|
||||||
|
elif button_state.find("next") != -1:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
assert False, f"Got unexpected button state {button_state}"
|
||||||
|
|
||||||
|
def should_be_button(self) -> None:
|
||||||
|
"""Проверяет наличие кнопки расширения/сжатия рабочей области вкладки.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
AssertionError: Если кнопка отсутствует.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.expand_work_area_button.check_visibility(
|
||||||
|
"Expand work area button is missing on page"
|
||||||
|
)
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
"""Модуль компонента таблицы. Содержит класс для работы с табличными данными."""
|
"""Модуль компонента таблицы. Содержит класс для работы с табличными данными."""
|
||||||
|
|
||||||
|
import math
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from playwright.sync_api import Page, expect, Locator
|
from playwright.sync_api import Page, expect, Locator
|
||||||
from tools.logger import get_logger
|
from tools.logger import get_logger
|
||||||
|
|
@ -34,10 +35,10 @@ class TableComponent(BaseComponent):
|
||||||
assert arrow_button.is_enabled(), f"Arrow button is missing in {index} header cell"
|
assert arrow_button.is_enabled(), f"Arrow button is missing in {index} header cell"
|
||||||
arrow_button.click()
|
arrow_button.click()
|
||||||
|
|
||||||
def datetime2timestamp(self, date_string: str) -> float|None:
|
def datetime2timestamp(self, date_string: str, format_string = None) -> float|None:
|
||||||
""" Конвертация строкового представления даты и времени в Unix timestamp
|
""" Конвертация строкового представления даты и времени в Unix timestamp
|
||||||
Args:
|
Args:
|
||||||
date_string: Строка с датой и временем в формате d.m.Y H:M:S.
|
date_string: Строка с датой и временем в формате d.m.Y H:M:S (default value).
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
float: Unix timestamp.
|
float: Unix timestamp.
|
||||||
|
|
@ -45,7 +46,8 @@ class TableComponent(BaseComponent):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Формат, соответствующий строке с датой и временем
|
# Формат, соответствующий строке с датой и временем
|
||||||
format_string = "%d.%m.%Y %H:%M:%S"
|
if format_string is None:
|
||||||
|
format_string = "%d.%m.%Y %H:%M:%S"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
date_object = datetime.strptime(date_string, format_string)
|
date_object = datetime.strptime(date_string, format_string)
|
||||||
|
|
@ -75,6 +77,31 @@ class TableComponent(BaseComponent):
|
||||||
else:
|
else:
|
||||||
assert False, f"Got unsupported arrow state: {state}"
|
assert False, f"Got unsupported arrow state: {state}"
|
||||||
|
|
||||||
|
def get_column(self, table_locator: str | Locator, index: int) -> list[str]:
|
||||||
|
"""Возвращает столбец таблицы по индексу.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
table_locator: Локатор таблицы.
|
||||||
|
index: Индекс столбца.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Список значений требуемого столбца.
|
||||||
|
"""
|
||||||
|
|
||||||
|
table_content = self.read(table_locator)
|
||||||
|
|
||||||
|
if len(table_content) == 0:
|
||||||
|
assert False, "The contents of the table are missing"
|
||||||
|
|
||||||
|
del table_content[0]
|
||||||
|
|
||||||
|
assert index in range(len(table_content[0])), \
|
||||||
|
"Column index is out of range"
|
||||||
|
column = []
|
||||||
|
for i in range(len(table_content)):
|
||||||
|
column.append(table_content[i][index])
|
||||||
|
return column
|
||||||
|
|
||||||
def get_header_cell_button(self, table_locator: str | Locator, index: int) -> Locator:
|
def get_header_cell_button(self, table_locator: str | Locator, index: int) -> Locator:
|
||||||
""" Поиск кнопки в ячейке заголовка таблицы
|
""" Поиск кнопки в ячейке заголовка таблицы
|
||||||
|
|
||||||
|
|
@ -179,11 +206,14 @@ class TableComponent(BaseComponent):
|
||||||
if item in arrow_state:
|
if item in arrow_state:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if item == '':
|
||||||
|
continue
|
||||||
|
|
||||||
if item not in expected_headers:
|
if item not in expected_headers:
|
||||||
is_equals = False
|
is_equals = False
|
||||||
|
|
||||||
assert is_equals, \
|
assert is_equals, \
|
||||||
f"Expected events table headers {expected_headers} are not equal {actual_headers}"
|
f"Expected table headers {expected_headers} are not equal {actual_headers}"
|
||||||
|
|
||||||
def check_content(self,
|
def check_content(self,
|
||||||
locator: str | Locator,
|
locator: str | Locator,
|
||||||
|
|
@ -296,3 +326,48 @@ class TableComponent(BaseComponent):
|
||||||
|
|
||||||
new_color = hover_element.evaluate("el => window.getComputedStyle(el).backgroundColor")
|
new_color = hover_element.evaluate("el => window.getComputedStyle(el).backgroundColor")
|
||||||
assert initial_color != new_color, "Color of row did not change when hovering the cursor"
|
assert initial_color != new_color, "Color of row did not change when hovering the cursor"
|
||||||
|
|
||||||
|
def check_mui_table_row_highlighting(self, locator: str | Locator,
|
||||||
|
row_index: int,
|
||||||
|
offset_x: float,
|
||||||
|
offset_y: float,
|
||||||
|
scale_x: float,
|
||||||
|
scale_y: float) -> None:
|
||||||
|
"""Проверяет изменение цвета строки при наведении.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
locator: Локатор таблицы.
|
||||||
|
row_index: Индекс проверяемой строки.
|
||||||
|
offset_x, offset_y: смещение координат таблицы относительно начала координат
|
||||||
|
scale_x, scale_y: коээфициенты масштабирования (причина: несовпадение масштабов контента страницы и фрейма)
|
||||||
|
"""
|
||||||
|
|
||||||
|
table = self.get_locator(locator)
|
||||||
|
row = table.locator("tbody").locator(".MuiTableRow-root").nth(row_index)
|
||||||
|
|
||||||
|
# Прокручиваем и ждем
|
||||||
|
row.scroll_into_view_if_needed()
|
||||||
|
self.page.wait_for_timeout(1000)
|
||||||
|
|
||||||
|
# Получение "ограничительной рамки" строки
|
||||||
|
bounding_box = row.evaluate("el => el.getBoundingClientRect()")
|
||||||
|
assert bounding_box, "Requested row is not visible"
|
||||||
|
|
||||||
|
# Получение текущего цвета фона
|
||||||
|
initial_color = row.evaluate("el => window.getComputedStyle(el).backgroundColor")
|
||||||
|
|
||||||
|
# Вычисление координат целевой строки таблицы и перевод на нее курсора мыши
|
||||||
|
bounding_box = row.evaluate("el => el.getBoundingClientRect()")
|
||||||
|
|
||||||
|
# center_x = (bounding_box["x"] + bounding_box["width"] / 2 + offset_x) * scale_x
|
||||||
|
# center_y = (bounding_box["y"] + bounding_box["height"] / 2 + offset_y) * scale_y
|
||||||
|
|
||||||
|
center_x = (bounding_box["x"] + bounding_box["width"] / 2) * scale_x + offset_x
|
||||||
|
center_y = (bounding_box["y"] + bounding_box["height"] / 2) * scale_y + offset_y
|
||||||
|
|
||||||
|
self.page.mouse.move(math.ceil(center_x), math.ceil(center_y), steps=5)
|
||||||
|
self.page.wait_for_timeout(1000)
|
||||||
|
|
||||||
|
# Получение текущего цвета фона
|
||||||
|
new_color = row.evaluate("el => window.getComputedStyle(el).backgroundColor")
|
||||||
|
assert initial_color != new_color, "Color of row did not change when hovering the cursor"
|
||||||
|
|
|
||||||
|
|
@ -32,15 +32,15 @@ class SystemLogEventsContainer(EventsContainerComponent):
|
||||||
|
|
||||||
sidebar_filter = self.get_sidebar_filter()
|
sidebar_filter = self.get_sidebar_filter()
|
||||||
sidebar_filter.add_filtering_parameter("filter_type", "Тип")
|
sidebar_filter.add_filtering_parameter("filter_type", "Тип")
|
||||||
sidebar_filter.add_filtering_parameter("filter_strictness", "Строгость")
|
sidebar_filter.add_filtering_parameter("filter_strictness", "Критичность")
|
||||||
sidebar_filter.add_filtering_parameter("filter_host", "Хост")
|
sidebar_filter.add_filtering_parameter("filter_host", "Объект")
|
||||||
|
|
||||||
# Действия:
|
# Действия:
|
||||||
# Проверки:
|
# Проверки:
|
||||||
def check_content(self) -> None:
|
def check_content(self) -> None:
|
||||||
"""Проверяет содержимое контейнера для отображения событий системного журнала."""
|
"""Проверяет содержимое контейнера для отображения событий системного журнала."""
|
||||||
|
|
||||||
expected_headers = ['ТИП', 'ВРЕМЯ', 'СТРОГОСТЬ', 'ХОСТ', 'ОПИСАНИЕ']
|
expected_headers = ['ТИП', 'ВРЕМЯ', 'КРИТИЧНОСТЬ', 'ОБЪЕКТ', 'ОПИСАНИЕ']
|
||||||
|
|
||||||
self.should_be_toolbar()
|
self.should_be_toolbar()
|
||||||
self.should_be_base_toolbar_buttons()
|
self.should_be_base_toolbar_buttons()
|
||||||
|
|
@ -71,8 +71,8 @@ class SystemLogEventsContainer(EventsContainerComponent):
|
||||||
|
|
||||||
filter_strictness_bar = sidebar_filter.get_filtering_parameter("filter_strictness")
|
filter_strictness_bar = sidebar_filter.get_filtering_parameter("filter_strictness")
|
||||||
filter_strictness_title = filter_strictness_bar.get_selection_bar_title()
|
filter_strictness_title = filter_strictness_bar.get_selection_bar_title()
|
||||||
assert filter_strictness_title == "Строгость", "Filtering parameter bar 'Строгость' is missing"
|
assert filter_strictness_title == "Критичность", "Filtering parameter bar 'Критичность' is missing"
|
||||||
|
|
||||||
filter_host_bar = sidebar_filter.get_filtering_parameter("filter_host")
|
filter_host_bar = sidebar_filter.get_filtering_parameter("filter_host")
|
||||||
filter_host_title = filter_host_bar.get_selection_bar_title()
|
filter_host_title = filter_host_bar.get_selection_bar_title()
|
||||||
assert filter_host_title == "Хост", "Filtering parameter bar 'Хост' is missing"
|
assert filter_host_title == "Объект", "Filtering parameter bar 'Объект' is missing"
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,6 @@ class AddLocalUserModalWindow(ModalWindowComponent):
|
||||||
# Локаторы элементов формы
|
# Локаторы элементов формы
|
||||||
text_field_locator = ModalWindowLocators.TEXT_FIELD_INPUT_FORM_USER_DATA
|
text_field_locator = ModalWindowLocators.TEXT_FIELD_INPUT_FORM_USER_DATA
|
||||||
input_form_locator = ModalWindowLocators.INPUT_FORM_USER_DATA
|
input_form_locator = ModalWindowLocators.INPUT_FORM_USER_DATA
|
||||||
label_locator = ModalWindowLocators.LABEL_INPUT_FORM_USER_DATA
|
|
||||||
|
|
||||||
# Настройка заголовка и кнопки закрытия тулбара
|
# Настройка заголовка и кнопки закрытия тулбара
|
||||||
self.window_title = "Добавить нового пользователя"
|
self.window_title = "Добавить нового пользователя"
|
||||||
|
|
@ -52,42 +51,30 @@ class AddLocalUserModalWindow(ModalWindowComponent):
|
||||||
self.add_toolbar_title(self.window_title)
|
self.add_toolbar_title(self.window_title)
|
||||||
self.add_toolbar_button(locator_button_toolbar_close, "close")
|
self.add_toolbar_button(locator_button_toolbar_close, "close")
|
||||||
|
|
||||||
# Добавление элементов формы по порядку расположения в окне
|
|
||||||
checkbox_1 = Checkbox(
|
|
||||||
page,
|
|
||||||
self.page.locator(input_form_locator).get_by_role("checkbox").nth(0),
|
|
||||||
"active_directory"
|
|
||||||
)
|
|
||||||
|
|
||||||
self.add_content_item("active_directory_checkbox", checkbox_1)
|
|
||||||
|
|
||||||
label_1 = Text(
|
|
||||||
page,
|
|
||||||
self.page.locator(label_locator).nth(0),
|
|
||||||
"active_directory_checkbox_label"
|
|
||||||
)
|
|
||||||
self.add_content_item("active_directory_checkbox_label", label_1)
|
|
||||||
|
|
||||||
# Поле Имя
|
# Поле Имя
|
||||||
loc = f"{input_form_locator}/div[2]/{text_field_locator}"
|
loc = f"{input_form_locator}/div[1]/{text_field_locator}"
|
||||||
name_input = TextInput(page, self.page.locator(loc), "name_input")
|
name_input = TextInput(page, self.page.locator(loc), "name_input")
|
||||||
self.add_content_item("name_input", name_input)
|
self.add_content_item("name_input", name_input)
|
||||||
|
|
||||||
# Чекбокс "Блокировка" - индекс 1
|
|
||||||
checkbox_2 = Checkbox(
|
|
||||||
page,
|
|
||||||
self.page.locator(input_form_locator).get_by_role("checkbox").nth(1),
|
|
||||||
"blocking"
|
|
||||||
)
|
|
||||||
self.add_content_item("blocking_checkbox", checkbox_2)
|
|
||||||
|
|
||||||
# Метка "Блокировка" - индекс 1
|
# Метка "Блокировка"
|
||||||
label_2 = Text(
|
label_blocking_locator = self.page.locator(input_form_locator). \
|
||||||
|
locator("//label").get_by_text("Блокировка")
|
||||||
|
label_blocking = Text(
|
||||||
page,
|
page,
|
||||||
self.page.locator(label_locator).nth(1),
|
label_blocking_locator,
|
||||||
"blocking_checkbox_label"
|
"blocking_checkbox_label"
|
||||||
)
|
)
|
||||||
self.add_content_item("blocking_checkbox_label", label_2)
|
|
||||||
|
self.add_content_item("blocking_checkbox_label", label_blocking)
|
||||||
|
|
||||||
|
# Чекбокс "Блокировка"
|
||||||
|
checkbox_blocking = Checkbox(
|
||||||
|
page,
|
||||||
|
label_blocking_locator.locator("../..").get_by_role("checkbox"),
|
||||||
|
"blocking"
|
||||||
|
)
|
||||||
|
self.add_content_item("blocking_checkbox", checkbox_blocking)
|
||||||
|
|
||||||
# Поле Роль
|
# Поле Роль
|
||||||
role_loc = self.page.locator(input_form_locator).get_by_role("combobox").nth(0)
|
role_loc = self.page.locator(input_form_locator).get_by_role("combobox").nth(0)
|
||||||
|
|
@ -96,41 +83,42 @@ class AddLocalUserModalWindow(ModalWindowComponent):
|
||||||
self.add_content_item("roles_list", DropdownList(page))
|
self.add_content_item("roles_list", DropdownList(page))
|
||||||
|
|
||||||
# Поле Пароль
|
# Поле Пароль
|
||||||
loc = f"{input_form_locator}/div[5]/{text_field_locator}"
|
loc = f"{input_form_locator}/div[4]/{text_field_locator}"
|
||||||
password_input = TextInput(page, self.page.locator(loc), "password_input")
|
password_input = TextInput(page, self.page.locator(loc), "password_input")
|
||||||
self.add_content_item("password_input", password_input)
|
self.add_content_item("password_input", password_input)
|
||||||
|
|
||||||
# Поле Комментарий
|
# Поле Комментарий
|
||||||
loc = f"{input_form_locator}/div[6]/{text_field_locator}"
|
loc = f"{input_form_locator}/div[5]/{text_field_locator}"
|
||||||
commentary_input = TextInput(page, self.page.locator(loc), "commentary_input")
|
commentary_input = TextInput(page, self.page.locator(loc), "commentary_input")
|
||||||
self.add_content_item("commentary_input", commentary_input)
|
self.add_content_item("commentary_input", commentary_input)
|
||||||
|
|
||||||
# Поле E-mail
|
# Поле E-mail
|
||||||
loc = f"{input_form_locator}/div[7]/{text_field_locator}"
|
loc = f"{input_form_locator}/div[6]/{text_field_locator}"
|
||||||
email_input = TextInput(page, self.page.locator(loc), "email_input")
|
email_input = TextInput(page, self.page.locator(loc), "email_input")
|
||||||
self.add_content_item("email_input", email_input)
|
self.add_content_item("email_input", email_input)
|
||||||
|
|
||||||
# Поле Номер для СМС
|
# Поле Номер для СМС
|
||||||
loc = f"{input_form_locator}/div[8]/{text_field_locator}"
|
loc = f"{input_form_locator}/div[7]/{text_field_locator}"
|
||||||
phone_input = TextInput(page, self.page.locator(loc), "phone_input")
|
phone_input = TextInput(page, self.page.locator(loc), "phone_input")
|
||||||
self.add_content_item("phone_input", phone_input)
|
self.add_content_item("phone_input", phone_input)
|
||||||
|
|
||||||
# Чекбокс "Подписка на Push-уведомления" - индекс 2
|
# Метка "Подписка на Push-уведомления"
|
||||||
checkbox_3 = Checkbox(
|
label_push_locator = self.page.locator(input_form_locator). \
|
||||||
|
locator("//label").get_by_text("Подписка на Push-уведомления")
|
||||||
|
label_push = Text(
|
||||||
page,
|
page,
|
||||||
self.page.locator(ModalWindowLocators.INPUT_FORM_USER_DATA)
|
label_push_locator,
|
||||||
.get_by_role("checkbox").nth(2),
|
|
||||||
"push_notification"
|
|
||||||
)
|
|
||||||
self.add_content_item("push_notification_checkbox", checkbox_3)
|
|
||||||
|
|
||||||
# Метка "Подписка на Push-уведомления" - индекс 2
|
|
||||||
label_3 = Text(
|
|
||||||
page,
|
|
||||||
self.page.locator(label_locator).nth(2),
|
|
||||||
"push_notification_checkbox_label"
|
"push_notification_checkbox_label"
|
||||||
)
|
)
|
||||||
self.add_content_item("push_notification_checkbox_label", label_3)
|
self.add_content_item("push_notification_checkbox_label", label_push)
|
||||||
|
|
||||||
|
# Чекбокс "Подписка на Push-уведомления" - индекс 2
|
||||||
|
checkbox_push = Checkbox(
|
||||||
|
page,
|
||||||
|
label_push_locator.locator("../..").get_by_role("checkbox"),
|
||||||
|
"push_notification"
|
||||||
|
)
|
||||||
|
self.add_content_item("push_notification_checkbox", checkbox_push)
|
||||||
|
|
||||||
# Добавление кнопок действий
|
# Добавление кнопок действий
|
||||||
locator_button_add = self.page.get_by_role("button", name="Добавить")
|
locator_button_add = self.page.get_by_role("button", name="Добавить")
|
||||||
|
|
@ -143,16 +131,6 @@ class AddLocalUserModalWindow(ModalWindowComponent):
|
||||||
self.new_user_confirm = ConfirmComponent(page, " Отмена ", " Добавить ")
|
self.new_user_confirm = ConfirmComponent(page, " Отмена ", " Добавить ")
|
||||||
|
|
||||||
# Действия:
|
# Действия:
|
||||||
def check_active_directory_checkbox(self):
|
|
||||||
"""Включает чек-бокс Active Directory."""
|
|
||||||
|
|
||||||
self.get_content_item("active_directory_checkbox").check(force=True)
|
|
||||||
|
|
||||||
def uncheck_active_directory_checkbox(self):
|
|
||||||
"""Выключает чек-бокс Active Directory."""
|
|
||||||
|
|
||||||
self.get_content_item("active_directory_checkbox").uncheck(force=True)
|
|
||||||
|
|
||||||
def check_blocking_checkbox(self):
|
def check_blocking_checkbox(self):
|
||||||
"""Включает чек-бокс Блокировка."""
|
"""Включает чек-бокс Блокировка."""
|
||||||
|
|
||||||
|
|
@ -255,13 +233,6 @@ class AddLocalUserModalWindow(ModalWindowComponent):
|
||||||
|
|
||||||
self.check_by_window_title()
|
self.check_by_window_title()
|
||||||
|
|
||||||
is_checked = self.get_content_item("active_directory_checkbox").is_checked()
|
|
||||||
if is_checked:
|
|
||||||
assert False, (
|
|
||||||
"The checkbox 'Active Directory' should not be checked for "
|
|
||||||
"the add local user window"
|
|
||||||
)
|
|
||||||
|
|
||||||
self.check_toolbar_button_visibility("close")
|
self.check_toolbar_button_visibility("close")
|
||||||
self.check_toolbar_button_tooltip("close", "Закрыть")
|
self.check_toolbar_button_tooltip("close", "Закрыть")
|
||||||
|
|
||||||
|
|
@ -271,12 +242,7 @@ class AddLocalUserModalWindow(ModalWindowComponent):
|
||||||
for name in self.content_items:
|
for name in self.content_items:
|
||||||
item = self.get_content_item(name)
|
item = self.get_content_item(name)
|
||||||
|
|
||||||
if name == "active_directory_checkbox_label":
|
if name == "blocking_checkbox_label":
|
||||||
item.check_have_text(
|
|
||||||
"Active Directory",
|
|
||||||
"Label 'Active Directory' is missing"
|
|
||||||
)
|
|
||||||
elif name == "blocking_checkbox_label":
|
|
||||||
item.check_have_text(
|
item.check_have_text(
|
||||||
"Блокировка",
|
"Блокировка",
|
||||||
"Label 'Блокировка' is missing"
|
"Label 'Блокировка' is missing"
|
||||||
|
|
|
||||||
|
|
@ -43,11 +43,14 @@ class BaseElement:
|
||||||
return "base element"
|
return "base element"
|
||||||
|
|
||||||
# Действия:
|
# Действия:
|
||||||
def click(self) -> None:
|
def click(self, force=False) -> None:
|
||||||
"""Выполняет клик по элементу."""
|
"""Выполняет клик по элементу."""
|
||||||
|
|
||||||
logger.info(f"Clicking {self.type_of} '{self.name}'")
|
logger.info(f"Clicking {self.type_of} '{self.name}'")
|
||||||
self.locator.click()
|
if force:
|
||||||
|
self.locator.click(force=True)
|
||||||
|
else:
|
||||||
|
self.locator.click()
|
||||||
|
|
||||||
def get_text(self, index: int) -> str:
|
def get_text(self, index: int) -> str:
|
||||||
"""Возвращает текст элемента по указанному индексу."""
|
"""Возвращает текст элемента по указанному индексу."""
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,12 @@
|
||||||
Позволяет проверять состояние и взаимодействовать с элементами вкладки.
|
Позволяет проверять состояние и взаимодействовать с элементами вкладки.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from playwright.sync_api import Page
|
import math
|
||||||
from locators.table_locators import TableLocators
|
from playwright.sync_api import Page, Locator, expect
|
||||||
from components.toolbar_component import ToolbarComponent
|
from elements.text_element import Text
|
||||||
|
from elements.button_element import Button
|
||||||
from components.table_component import TableComponent
|
from components.table_component import TableComponent
|
||||||
|
from components.expand_button_component import ExpandButton
|
||||||
from pages.base_page import BasePage
|
from pages.base_page import BasePage
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -26,9 +28,97 @@ class ServiceStatusTab(BasePage):
|
||||||
|
|
||||||
super().__init__(page)
|
super().__init__(page)
|
||||||
|
|
||||||
self.toolbar = ToolbarComponent(page, "Статус обслуживания")
|
self.iframe_container_locator = page.locator("div.container.iframe-wrapper")
|
||||||
|
self.iframe_locator = page.frame_locator("//iframe[@class='iframe-class']")
|
||||||
|
self.table_locator = self.iframe_locator.locator("div.MuiBox-root > div > table")
|
||||||
|
self.tab_title_locator = self.iframe_locator.locator("//h5[text()='Состояние контейнеров']")
|
||||||
|
self.update_button_locator = self.iframe_locator.get_by_role("button", name='Обновить')
|
||||||
|
self.analyzer_open_button_locator = self.iframe_locator.get_by_role("button", name='открыть анализатор')
|
||||||
|
|
||||||
|
self.tab_title = Text(page,
|
||||||
|
self.tab_title_locator,
|
||||||
|
"tab_title")
|
||||||
|
self.update_button = Button(page,
|
||||||
|
self.update_button_locator,
|
||||||
|
"update_button")
|
||||||
|
self.analyzer_open_button = Button(page,
|
||||||
|
self.analyzer_open_button_locator,
|
||||||
|
"analyzer_open_button")
|
||||||
|
self.expand_work_area_button = ExpandButton(page)
|
||||||
self.services_table = TableComponent(page)
|
self.services_table = TableComponent(page)
|
||||||
|
|
||||||
|
# Действия:
|
||||||
|
def calculate_coordinates(self, locator: Locator) -> dict:
|
||||||
|
"""Вычисление координат элемента для манипуляции с ним мышкой"""
|
||||||
|
|
||||||
|
coordinates = {}
|
||||||
|
|
||||||
|
offsets_scales = self.get_offsets_scales()
|
||||||
|
|
||||||
|
bounding_box = locator.evaluate("el => el.getBoundingClientRect()")
|
||||||
|
assert bounding_box, "Required element is not visible"
|
||||||
|
|
||||||
|
offset_x = offsets_scales["offset_x"]
|
||||||
|
offset_y = offsets_scales["offset_y"]
|
||||||
|
scale_x = offsets_scales["scale_x"]
|
||||||
|
scale_y = offsets_scales["scale_y"]
|
||||||
|
|
||||||
|
coordinates["center_x"] = (bounding_box["x"] + bounding_box["width"] / 2) * scale_x + offset_x
|
||||||
|
coordinates["center_y"] = (bounding_box["y"] + bounding_box["height"] / 2) * scale_y + offset_y
|
||||||
|
return coordinates
|
||||||
|
|
||||||
|
def get_offsets_scales(self) -> dict:
|
||||||
|
"""Возвращает словарь, содержащий смещения контейнера фрейма и его коэффициенты"""
|
||||||
|
|
||||||
|
offsets_scales = {}
|
||||||
|
|
||||||
|
iframe_container_bounding_box = self.iframe_container_locator.\
|
||||||
|
evaluate("el => el.getBoundingClientRect()")
|
||||||
|
assert iframe_container_bounding_box, "Iframe container is not visible"
|
||||||
|
|
||||||
|
iframe_bounding_box = self.iframe_locator.locator("div#root > div"). \
|
||||||
|
evaluate("el => el.getBoundingClientRect()")
|
||||||
|
assert iframe_bounding_box, "Iframe content is not visible"
|
||||||
|
|
||||||
|
offsets_scales["offset_x"] = iframe_container_bounding_box["x"]
|
||||||
|
offsets_scales["offset_y"] = iframe_container_bounding_box["y"]
|
||||||
|
offsets_scales["scale_x"] = iframe_container_bounding_box["width"]/iframe_bounding_box["width"]
|
||||||
|
offsets_scales["scale_y"] = iframe_container_bounding_box["height"]/iframe_bounding_box["height"]
|
||||||
|
|
||||||
|
return offsets_scales
|
||||||
|
|
||||||
|
def datetime2timestamp(self, date_column: str) -> list[float]:
|
||||||
|
""" Конвертация строкового представления даты и времени в Unix timestamp
|
||||||
|
"""
|
||||||
|
|
||||||
|
converted = []
|
||||||
|
|
||||||
|
for date in date_column:
|
||||||
|
timestamp = self.services_table.datetime2timestamp(date,format_string="%d-%m-%y %H:%M:%S")
|
||||||
|
assert timestamp, f"Error conversation to timestamp for {date}"
|
||||||
|
converted.append(timestamp)
|
||||||
|
|
||||||
|
return converted
|
||||||
|
|
||||||
|
def click_update_button(self) -> None:
|
||||||
|
"""Нажатие кнопки 'Обновить'"""
|
||||||
|
|
||||||
|
coordinates = self.calculate_coordinates(self.update_button_locator)
|
||||||
|
self.page.mouse.click(math.ceil(coordinates["center_x"]),
|
||||||
|
math.ceil(coordinates["center_y"]), delay=300)
|
||||||
|
|
||||||
|
def get_column(self, index: int) -> list[str]:
|
||||||
|
"""Возвращает столбец таблицы по индексу.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list[str]: Столбец таблицы по индексу.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
AssertionError: Если таблица пуста или столбца с таким индексом в таблице нет.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self.services_table.get_column(self.table_locator, index)
|
||||||
|
|
||||||
def get_rows_count(self) -> int:
|
def get_rows_count(self) -> int:
|
||||||
"""Возвращает количество строк в таблице (без заголовка).
|
"""Возвращает количество строк в таблице (без заголовка).
|
||||||
|
|
||||||
|
|
@ -39,18 +129,51 @@ class ServiceStatusTab(BasePage):
|
||||||
AssertionError: Если таблица пуста.
|
AssertionError: Если таблица пуста.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return self.services_table.get_rows_count(TableLocators.TABLE_WORK_AREA)
|
return self.services_table.get_rows_count(self.table_locator)
|
||||||
|
|
||||||
def scroll_services_table_up(self) -> None:
|
def expand_tab(self) -> None:
|
||||||
"""Прокручивает таблицу сервисов вверх."""
|
"""Расширяет рабочую область складки."""
|
||||||
|
|
||||||
self.services_table.scroll_up(TableLocators.TABLE_SCROLL_CONTAINER)
|
iframe_container_bounding_box = self.iframe_container_locator.\
|
||||||
|
evaluate("el => el.getBoundingClientRect()")
|
||||||
|
widht_before = iframe_container_bounding_box["width"]
|
||||||
|
|
||||||
def scroll_services_table_down(self) -> None:
|
self.expand_work_area_button.expand()
|
||||||
"""Прокручивает таблицу сервисов вниз."""
|
|
||||||
|
|
||||||
self.services_table.scroll_down(TableLocators.TABLE_SCROLL_CONTAINER)
|
iframe_container_bounding_box = self.iframe_container_locator.\
|
||||||
|
evaluate("el => el.getBoundingClientRect()")
|
||||||
|
widht_after = iframe_container_bounding_box["width"]
|
||||||
|
|
||||||
|
assert widht_before < widht_after,"Services statuses tab should be expanded"
|
||||||
|
|
||||||
|
def reduce_tab(self) -> None:
|
||||||
|
"""Сжимает рабочую область складки."""
|
||||||
|
|
||||||
|
iframe_container_bounding_box = self.iframe_container_locator.\
|
||||||
|
evaluate("el => el.getBoundingClientRect()")
|
||||||
|
widht_before = iframe_container_bounding_box["width"]
|
||||||
|
|
||||||
|
self.expand_work_area_button.reduce()
|
||||||
|
|
||||||
|
iframe_container_bounding_box = self.iframe_container_locator.\
|
||||||
|
evaluate("el => el.getBoundingClientRect()")
|
||||||
|
widht_after = iframe_container_bounding_box["width"]
|
||||||
|
|
||||||
|
assert widht_before > widht_after,"Services statuses tab should be reduced"
|
||||||
|
|
||||||
|
def scroll_services_tab_up(self) -> None:
|
||||||
|
"""Прокручивает содержимое вкладки вверх."""
|
||||||
|
|
||||||
|
self.tab_title_locator.scroll_into_view_if_needed()
|
||||||
|
|
||||||
|
def scroll_services_tab_down(self) -> None:
|
||||||
|
"""Прокручивает содержимое вкладки вниз."""
|
||||||
|
|
||||||
|
nrows = self.get_rows_count()
|
||||||
|
last_row = self.services_table.get_row_locator(self.table_locator, nrows-1)
|
||||||
|
last_row.scroll_into_view_if_needed()
|
||||||
|
|
||||||
|
# Проверки:
|
||||||
def check_services_table_content(self) -> None:
|
def check_services_table_content(self) -> None:
|
||||||
"""Проверяет содержимое таблицы сервисов.
|
"""Проверяет содержимое таблицы сервисов.
|
||||||
|
|
||||||
|
|
@ -61,15 +184,54 @@ class ServiceStatusTab(BasePage):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
expected_headers = [
|
expected_headers = [
|
||||||
'Контейнер',
|
'СТЕК',
|
||||||
'Время создания',
|
'КОНТЕЙНЕР',
|
||||||
'Статус',
|
'ТЭГ',
|
||||||
'Время работы',
|
'СТАТУС',
|
||||||
'Image ID',
|
'ВРЕМЯ СОЗДАНИЯ',
|
||||||
'Image ТЭГ'
|
'ЦЕЛОСТНОСТЬ',
|
||||||
|
'ДЕЙСТВИЯ'
|
||||||
]
|
]
|
||||||
|
|
||||||
self.services_table.check_content(TableLocators.TABLE_WORK_AREA, expected_headers)
|
self.services_table.check_content(self.table_locator, expected_headers)
|
||||||
|
|
||||||
|
# Проверяем наличие кнопок и тултипов в последней ячейке строки
|
||||||
|
rows_count = self.services_table.get_rows_count(self.table_locator)
|
||||||
|
for i in range(rows_count):
|
||||||
|
row_locator = self.services_table.get_row_locator(self.table_locator, i)
|
||||||
|
|
||||||
|
layers_button = row_locator.get_by_role("button", name="Слои")
|
||||||
|
expect(layers_button).to_be_visible(), f"Layers button is missing in {i} row"
|
||||||
|
self.check_tooltip(layers_button, "Слои")
|
||||||
|
|
||||||
|
lounch_button = row_locator.get_by_role("button", name="Запуск", exact=True)
|
||||||
|
expect(lounch_button).to_be_visible(), f"Lounch button is missing in {i} row"
|
||||||
|
self.check_tooltip(lounch_button, "Запуск")
|
||||||
|
|
||||||
|
stop_button = row_locator.get_by_role("button", name="Остановка")
|
||||||
|
expect(stop_button).to_be_visible(), f"Stop button is missing in {i} row"
|
||||||
|
self.check_tooltip(stop_button, "Остановка")
|
||||||
|
|
||||||
|
restart_button = row_locator.get_by_role("button", name="Перезапуск", exact=True)
|
||||||
|
expect(restart_button).to_be_visible(), f"Restart button is missing in {i} row"
|
||||||
|
self.check_tooltip(restart_button, "Перезапуск")
|
||||||
|
|
||||||
|
|
||||||
|
def check_tooltip(self, button_locator: Locator, expected_text: str) -> None:
|
||||||
|
"""Проверка текста тултипа кнопки в последней ячейке строки"""
|
||||||
|
|
||||||
|
# Наведение на элемент для отображения подсказки
|
||||||
|
coordinates = self.calculate_coordinates(button_locator)
|
||||||
|
|
||||||
|
self.page.mouse.move(math.ceil(coordinates["center_x"]),
|
||||||
|
math.ceil(coordinates["center_y"]), steps=3)
|
||||||
|
|
||||||
|
# Получение элемента подсказки и ее текста
|
||||||
|
tooltip = self.iframe_locator.get_by_role("tooltip").locator("div.MuiTooltip-tooltip").first
|
||||||
|
tooltip.wait_for(state="visible", timeout=5000)
|
||||||
|
|
||||||
|
actual_text = tooltip.text_content().strip()
|
||||||
|
assert expected_text==actual_text, f"Should be tooltip with text: {expected_text}, got: {actual_text}"
|
||||||
|
|
||||||
def check_services_table_verticall_scrolling(self) -> bool:
|
def check_services_table_verticall_scrolling(self) -> bool:
|
||||||
"""Проверяет возможность вертикальной прокрутки таблицы.
|
"""Проверяет возможность вертикальной прокрутки таблицы.
|
||||||
|
|
@ -78,18 +240,26 @@ class ServiceStatusTab(BasePage):
|
||||||
bool: True если прокрутка возможна, иначе False.
|
bool: True если прокрутка возможна, иначе False.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return self.services_table.is_scrollable_vertically(
|
loc = self.iframe_locator.locator("div.MuiBox-root > div > div").first
|
||||||
TableLocators.TABLE_SCROLL_CONTAINER
|
return loc.evaluate("el => el.scrollHeight > el.clientHeight")
|
||||||
)
|
|
||||||
|
|
||||||
def check_services_table_first_row_visibility(self) -> None:
|
def check_services_tab_title_visibility(self) -> None:
|
||||||
"""Проверяет видимость первой строки таблицы.
|
"""Проверяет видимость заголовка вкладки.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
AssertionError: Если строка не видна.
|
AssertionError: Если строка не видна.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.services_table.check_first_row_visibility(TableLocators.TABLE_WORK_AREA)
|
self.tab_title.check_visibility("The services tab title is not visible")
|
||||||
|
|
||||||
|
# def check_services_table_first_row_visibility(self) -> None:
|
||||||
|
# """Проверяет видимость первой строки таблицы.
|
||||||
|
|
||||||
|
# Raises:
|
||||||
|
# AssertionError: Если строка не видна.
|
||||||
|
# """
|
||||||
|
|
||||||
|
# self.services_table.check_first_row_visibility(self.table_locator)
|
||||||
|
|
||||||
def check_services_table_last_row_visibility(self) -> None:
|
def check_services_table_last_row_visibility(self) -> None:
|
||||||
"""Проверяет видимость последней строки таблицы.
|
"""Проверяет видимость последней строки таблицы.
|
||||||
|
|
@ -98,7 +268,7 @@ class ServiceStatusTab(BasePage):
|
||||||
AssertionError: Если строка не видна.
|
AssertionError: Если строка не видна.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.services_table.check_last_row_visibility(TableLocators.TABLE_WORK_AREA)
|
self.services_table.check_last_row_visibility(self.table_locator)
|
||||||
|
|
||||||
def check_services_table_row_highlighting(self, row_index: int) -> None:
|
def check_services_table_row_highlighting(self, row_index: int) -> None:
|
||||||
"""Проверяет выделение указанной строки таблицы.
|
"""Проверяет выделение указанной строки таблицы.
|
||||||
|
|
@ -110,19 +280,27 @@ class ServiceStatusTab(BasePage):
|
||||||
AssertionError: Если строка не выделена.
|
AssertionError: Если строка не выделена.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.services_table.check_row_highlighting(
|
offsets_scales = self.get_offsets_scales()
|
||||||
TableLocators.TABLE_WORK_AREA,
|
|
||||||
row_index
|
self.services_table.check_mui_table_row_highlighting(
|
||||||
|
self.table_locator,
|
||||||
|
row_index,
|
||||||
|
offsets_scales["offset_x"],
|
||||||
|
offsets_scales["offset_y"],
|
||||||
|
offsets_scales["scale_x"],
|
||||||
|
offsets_scales["scale_y"]
|
||||||
)
|
)
|
||||||
|
|
||||||
def should_be_toolbar(self) -> None:
|
def should_be_tab_title(self) -> None:
|
||||||
"""Проверяет наличие тулбара на вкладке.
|
"""Проверяет наличие заголовка вкладки.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
AssertionError: Если тулбар отсутствует.
|
AssertionError: Если заголовок отсутствует.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.toolbar.check_toolbar_presence("Toolbar is missing")
|
self.tab_title.check_visibility(
|
||||||
|
"Title of service statuses tab is missing"
|
||||||
|
)
|
||||||
|
|
||||||
def should_be_services_table(self) -> None:
|
def should_be_services_table(self) -> None:
|
||||||
"""Проверяет наличие таблицы сервисов.
|
"""Проверяет наличие таблицы сервисов.
|
||||||
|
|
@ -132,6 +310,37 @@ class ServiceStatusTab(BasePage):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.services_table.check_visibility(
|
self.services_table.check_visibility(
|
||||||
TableLocators.TABLE_WORK_AREA,
|
self.table_locator,
|
||||||
"Service statuses table is missing"
|
"Service statuses table is missing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def should_be_analyzer_open_button(self) -> None:
|
||||||
|
"""Проверяет наличие кнопки 'navigate_before/navigate_next'.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
AssertionError: Если кнопка отсутствует.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.analyzer_open_button.check_visibility(
|
||||||
|
"Analyzer open button on bottom of service statuses tab is missing"
|
||||||
|
)
|
||||||
|
|
||||||
|
def should_be_expand_work_area_button(self) -> None:
|
||||||
|
"""Проверяет наличие кнопки расширения/сжатия рабочей области вкладки.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
AssertionError: Если кнопка отсутствует.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.expand_work_area_button.should_be_button()
|
||||||
|
|
||||||
|
def should_be_update_button(self) -> None:
|
||||||
|
"""Проверяет наличие кнопки 'Обновить'.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
AssertionError: Если кнопка отсутствует.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.update_button.check_visibility(
|
||||||
|
"Update button on top of service statuses tab is missing"
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -87,11 +87,12 @@ class UsersTab(BasePage):
|
||||||
|
|
||||||
add_user_window = self.get_modal_window("add_local_user")
|
add_user_window = self.get_modal_window("add_local_user")
|
||||||
|
|
||||||
auth_type = user_data.get("auth_type")
|
# skip as unsupported
|
||||||
if auth_type == "active_directory":
|
# auth_type = user_data.get("auth_type")
|
||||||
add_user_window.check_active_directory_checkbox()
|
# if auth_type == "active_directory":
|
||||||
self.add_modal_window("add_AD_user", "")
|
# add_user_window.check_active_directory_checkbox()
|
||||||
add_user_window = self.get_modal_window("add_AD_user")
|
# self.add_modal_window("add_AD_user", "")
|
||||||
|
# add_user_window = self.get_modal_window("add_AD_user")
|
||||||
|
|
||||||
add_user_window.new_user(user_data)
|
add_user_window.new_user(user_data)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
pytest -s -v --s="{'width': 300, 'height': 420}" test_navigation_panel.py
|
pytest -s -v --s="{'width': 300, 'height': 420}" test_navigation_panel.py
|
||||||
pytest -s -v --s="{'width': 300, 'height': 420}" test_services_table.py
|
pytest -s -v --s="{'width': 1500, 'height': 420}" test_services_table.py
|
||||||
pytest -s -v --s="{'width': 300, 'height': 420}" test_json_container.py
|
pytest -s -v --s="{'width': 300, 'height': 420}" test_json_container.py
|
||||||
pytest -s -v --s="{'width': 300, 'height': 420}" test_user_modal_window.py
|
pytest -s -v --s="{'width': 300, 'height': 420}" test_user_modal_window.py
|
||||||
pytest -s -v --s="{'width': 800, 'height': 200}" test_session_settings.py
|
pytest -s -v --s="{'width': 800, 'height': 200}" test_session_settings.py
|
||||||
|
|
@ -51,10 +51,10 @@ class TestServiceStatusTable:
|
||||||
is_scrollable_vertically = sst.check_services_table_verticall_scrolling()
|
is_scrollable_vertically = sst.check_services_table_verticall_scrolling()
|
||||||
assert is_scrollable_vertically, "Should be vertical scrolling"
|
assert is_scrollable_vertically, "Should be vertical scrolling"
|
||||||
|
|
||||||
sst.scroll_services_table_down()
|
sst.scroll_services_tab_down()
|
||||||
sst.check_services_table_last_row_visibility()
|
sst.check_services_table_last_row_visibility()
|
||||||
sst.wait_for_timeout(3000)
|
sst.wait_for_timeout(3000)
|
||||||
|
|
||||||
sst.scroll_services_table_up()
|
sst.scroll_services_tab_up()
|
||||||
sst.check_services_table_first_row_visibility()
|
sst.check_services_tab_title_visibility()
|
||||||
sst.wait_for_timeout(2000)
|
sst.wait_for_timeout(2000)
|
||||||
|
|
|
||||||
|
|
@ -88,11 +88,13 @@ class TestNavigationPanel:
|
||||||
mp.wait_for_timeout(2000)
|
mp.wait_for_timeout(2000)
|
||||||
|
|
||||||
# Открываем пункты панели с одинаковыми имнами, но разным расположением
|
# Открываем пункты панели с одинаковыми имнами, но разным расположением
|
||||||
|
mp.click_subpanel_item("Редактор")
|
||||||
mp.click_subpanel_item("Шаблоны")
|
mp.click_subpanel_item("Шаблоны")
|
||||||
mp.wait_for_timeout(2000)
|
mp.wait_for_timeout(2000)
|
||||||
|
|
||||||
mp.click_subpanel_item("Zero Touch Provisioning")
|
mp.click_subpanel_item("Zero Touch Provisioning")
|
||||||
mp.click_subpanel_item("Шаблоны", parent="Zero Touch Provisioning")
|
# unsupported
|
||||||
|
# mp.click_subpanel_item("Шаблоны", parent="Zero Touch Provisioning")
|
||||||
mp.wait_for_timeout(2000)
|
mp.wait_for_timeout(2000)
|
||||||
|
|
||||||
# Переходим к Объектам
|
# Переходим к Объектам
|
||||||
|
|
@ -103,22 +105,26 @@ class TestNavigationPanel:
|
||||||
mp.click_subpanel_item("test-zone")
|
mp.click_subpanel_item("test-zone")
|
||||||
mp.wait_for_timeout(3000)
|
mp.wait_for_timeout(3000)
|
||||||
|
|
||||||
# Переходим Здание ЦОД 4
|
# Переходим к Стойке
|
||||||
mp.click_subpanel_item("Здание ЦОД 4")
|
mp.click_subpanel_item("Test-Rack-01")
|
||||||
mp.wait_for_timeout(3000)
|
|
||||||
|
|
||||||
# Переходим к Стойка КСПД с указанием родителя
|
|
||||||
mp.click_subpanel_item("Стойка КСПД", parent="Здание ЦОД 4")
|
|
||||||
mp.wait_for_timeout(5000)
|
mp.wait_for_timeout(5000)
|
||||||
|
|
||||||
|
# Переходим Здание ЦОД 4
|
||||||
|
# mp.click_subpanel_item("Здание ЦОД 4")
|
||||||
|
# mp.wait_for_timeout(3000)
|
||||||
|
|
||||||
|
# # Переходим к Стойка КСПД с указанием родителя
|
||||||
|
# mp.click_subpanel_item("Стойка КСПД", parent="Здание ЦОД 4")
|
||||||
|
# mp.wait_for_timeout(5000)
|
||||||
|
|
||||||
# Переходим к Объектам
|
# Переходим к Объектам
|
||||||
mp.click_main_navigation_panel_item("Объекты")
|
mp.click_main_navigation_panel_item("Объекты")
|
||||||
mp.click_main_navigation_panel_item("Объекты") # баг
|
mp.click_main_navigation_panel_item("Объекты") # баг
|
||||||
mp.wait_for_timeout(5000)
|
mp.wait_for_timeout(5000)
|
||||||
|
|
||||||
mp.click_subpanel_item("Виртуальные устройства")
|
# mp.click_subpanel_item("Виртуальные устройства")
|
||||||
mp.wait_for_timeout(3000)
|
# mp.wait_for_timeout(3000)
|
||||||
|
|
||||||
# Переходим к Стойка систем питания с указанием родителя
|
# # Переходим к Стойка систем питания с указанием родителя
|
||||||
mp.click_subpanel_item("Стойка систем питания", parent="Виртуальные устройства")
|
# mp.click_subpanel_item("Стойка систем питания", parent="Виртуальные устройства")
|
||||||
mp.wait_for_timeout(5000)
|
# mp.wait_for_timeout(5000)
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ from pages.service_status_tab import ServiceStatusTab
|
||||||
from pages.main_page import MainPage
|
from pages.main_page import MainPage
|
||||||
from pages.login_page import LoginPage
|
from pages.login_page import LoginPage
|
||||||
|
|
||||||
pytest.skip("пропуск всех тестов в этом файле в связи с переходом на новый виджет", allow_module_level=True)
|
# pytest.skip("пропуск всех тестов в этом файле в связи с переходом на новый виджет", allow_module_level=True)
|
||||||
|
|
||||||
# @pytest.mark.smoke
|
# @pytest.mark.smoke
|
||||||
class TestServiceStatusTab:
|
class TestServiceStatusTab:
|
||||||
|
|
@ -20,6 +20,8 @@ class TestServiceStatusTab:
|
||||||
Тесты покрывают следующие сценарии:
|
Тесты покрывают следующие сценарии:
|
||||||
1. test_service_status_tab_content: Проверяет содержимое вкладки 'Статус обслуживания'
|
1. test_service_status_tab_content: Проверяет содержимое вкладки 'Статус обслуживания'
|
||||||
2. test_service_status_table_row_highlighting: Проверяет выделение строк в таблице сервисов
|
2. test_service_status_table_row_highlighting: Проверяет выделение строк в таблице сервисов
|
||||||
|
3. test_service_status_tab_resize: Проверяет возможность расширения/сжатия рабочей области вкладки
|
||||||
|
4. test_service_status_tab_reload: Проверяет возможность обновления контента после нажатия кнопки 'Обновить'
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@pytest.fixture(scope="function", autouse=True)
|
@pytest.fixture(scope="function", autouse=True)
|
||||||
|
|
@ -58,8 +60,17 @@ class TestServiceStatusTab:
|
||||||
|
|
||||||
sst = ServiceStatusTab(browser)
|
sst = ServiceStatusTab(browser)
|
||||||
|
|
||||||
# Проверка тулбара вкладки
|
# Проверка наличия заголовка вкладки
|
||||||
sst.should_be_toolbar()
|
sst.should_be_tab_title()
|
||||||
|
|
||||||
|
# Проверка наличия кнопки 'Обновить'
|
||||||
|
sst.should_be_update_button()
|
||||||
|
|
||||||
|
# Проверка наличия кнопки 'Открыть анализатор'
|
||||||
|
sst.should_be_analyzer_open_button()
|
||||||
|
|
||||||
|
# Проверка наличия кнопки расширения/сжатия рабочей области вкладки
|
||||||
|
sst.should_be_expand_work_area_button()
|
||||||
|
|
||||||
# Проверка наличия таблицы статусов сервисов
|
# Проверка наличия таблицы статусов сервисов
|
||||||
sst.should_be_services_table()
|
sst.should_be_services_table()
|
||||||
|
|
@ -70,6 +81,7 @@ class TestServiceStatusTab:
|
||||||
# Проверка содержимого таблицы сервисов
|
# Проверка содержимого таблицы сервисов
|
||||||
sst.check_services_table_content()
|
sst.check_services_table_content()
|
||||||
|
|
||||||
|
# @pytest.mark.develop
|
||||||
def test_service_status_table_row_highlighting(self, browser: Page):
|
def test_service_status_table_row_highlighting(self, browser: Page):
|
||||||
"""Проверяет выделение строк в таблице сервисов.
|
"""Проверяет выделение строк в таблице сервисов.
|
||||||
|
|
||||||
|
|
@ -79,9 +91,6 @@ class TestServiceStatusTab:
|
||||||
|
|
||||||
sst = ServiceStatusTab(browser)
|
sst = ServiceStatusTab(browser)
|
||||||
|
|
||||||
# Проверка тулбара вкладки
|
|
||||||
sst.should_be_toolbar()
|
|
||||||
|
|
||||||
# Проверка наличия таблицы статусов сервисов
|
# Проверка наличия таблицы статусов сервисов
|
||||||
sst.should_be_services_table()
|
sst.should_be_services_table()
|
||||||
|
|
||||||
|
|
@ -92,3 +101,72 @@ class TestServiceStatusTab:
|
||||||
sst.check_services_table_row_highlighting(0)
|
sst.check_services_table_row_highlighting(0)
|
||||||
sst.check_services_table_row_highlighting(rows_count - 1)
|
sst.check_services_table_row_highlighting(rows_count - 1)
|
||||||
sst.check_services_table_row_highlighting(int(rows_count / 2))
|
sst.check_services_table_row_highlighting(int(rows_count / 2))
|
||||||
|
|
||||||
|
# @pytest.mark.develop
|
||||||
|
def test_service_status_tab_resize(self, browser: Page):
|
||||||
|
"""Проверяет возможность расширения/сжатия рабочей области вкладки.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
browser: Экземпляр страницы Playwright.
|
||||||
|
"""
|
||||||
|
|
||||||
|
sst = ServiceStatusTab(browser)
|
||||||
|
|
||||||
|
# Проверка наличия таблицы статусов сервисов
|
||||||
|
sst.should_be_services_table()
|
||||||
|
|
||||||
|
# Проверка наличия кнопки расширения/сжатия рабочей области вкладки
|
||||||
|
sst.should_be_expand_work_area_button()
|
||||||
|
|
||||||
|
# Проверка возможности расширения рабочей области вкладки
|
||||||
|
sst.expand_tab()
|
||||||
|
|
||||||
|
# Получение количества строк в таблице
|
||||||
|
rows_count = sst.get_rows_count()
|
||||||
|
|
||||||
|
# Проверка выделения строк
|
||||||
|
sst.check_services_table_row_highlighting(rows_count - 1)
|
||||||
|
|
||||||
|
# Проверка возможности сжатия рабочей области вкладки
|
||||||
|
sst.reduce_tab()
|
||||||
|
|
||||||
|
# Проверка выделения строк
|
||||||
|
sst.check_services_table_row_highlighting(0)
|
||||||
|
|
||||||
|
# @pytest.mark.develop
|
||||||
|
def test_service_status_tab_reload(self, browser: Page):
|
||||||
|
"""Проверяет возможность обновления контента после нажатия кнопки 'Обновить'.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
browser: Экземпляр страницы Playwright.
|
||||||
|
"""
|
||||||
|
|
||||||
|
sst = ServiceStatusTab(browser)
|
||||||
|
|
||||||
|
# Проверка наличия кнопки 'Обновить'
|
||||||
|
sst.should_be_update_button()
|
||||||
|
|
||||||
|
# Проверка наличия таблицы статусов сервисов
|
||||||
|
sst.should_be_services_table()
|
||||||
|
|
||||||
|
# Сохраняем значения столбца 'Время создания'
|
||||||
|
creation_time_col_before = sst.get_column(4)
|
||||||
|
timestamps_before = sst.datetime2timestamp(creation_time_col_before)
|
||||||
|
|
||||||
|
# Нажатие на кнопку 'Обновить'
|
||||||
|
sst.click_update_button()
|
||||||
|
browser.wait_for_load_state('domcontentloaded')
|
||||||
|
|
||||||
|
# Проверка наличия таблицы статусов сервисов
|
||||||
|
sst.should_be_services_table()
|
||||||
|
|
||||||
|
# Ожидание перед проверкой содержимого таблицы сервисов
|
||||||
|
browser.wait_for_timeout(1000)
|
||||||
|
|
||||||
|
# Сохраняем значения столбца 'Время создания'
|
||||||
|
creation_time_col_after = sst.get_column(4)
|
||||||
|
timestamps_after = sst.datetime2timestamp(creation_time_col_after)
|
||||||
|
|
||||||
|
assert len(timestamps_before) == len(timestamps_after), "Bad tab reload"
|
||||||
|
for i in range(len(timestamps_before)):
|
||||||
|
assert timestamps_before[i] < timestamps_after[i], "Date value after reload should be must be more recent"
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ class TestTemplatesTab:
|
||||||
|
|
||||||
Выполняет:
|
Выполняет:
|
||||||
1. Авторизацию в системе
|
1. Авторизацию в системе
|
||||||
2. Переход на вкладку 'Шаблоны' через панель навигации
|
2. Переход на вкладку 'Редактор/Шаблоны' через панель навигации
|
||||||
"""
|
"""
|
||||||
# Авторизация в системе
|
# Авторизация в системе
|
||||||
login_page = LoginPage(browser)
|
login_page = LoginPage(browser)
|
||||||
|
|
@ -43,6 +43,7 @@ class TestTemplatesTab:
|
||||||
# Проверка и взаимодействие с элементами навигации
|
# Проверка и взаимодействие с элементами навигации
|
||||||
main_page.should_be_navigation_panel()
|
main_page.should_be_navigation_panel()
|
||||||
main_page.click_main_navigation_panel_item("Настройки")
|
main_page.click_main_navigation_panel_item("Настройки")
|
||||||
|
main_page.click_subpanel_item("Редактор")
|
||||||
main_page.click_subpanel_item("Шаблоны")
|
main_page.click_subpanel_item("Шаблоны")
|
||||||
|
|
||||||
# @pytest.mark.develop
|
# @pytest.mark.develop
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@ from pages.login_page import LoginPage
|
||||||
from pages.main_page import MainPage
|
from pages.main_page import MainPage
|
||||||
from pages.ztp_config_tab import ZTPConfigTab
|
from pages.ztp_config_tab import ZTPConfigTab
|
||||||
|
|
||||||
|
pytest.skip("Пропуск всех тестов в этом файле в связи исключением данной функциональности", allow_module_level=True)
|
||||||
|
|
||||||
# @pytest.mark.smoke
|
# @pytest.mark.smoke
|
||||||
class TestZTPConfigTab:
|
class TestZTPConfigTab:
|
||||||
"""Набор тестов для вкладки 'Конфигурация' в модуле Zero Touch Provisioning.
|
"""Набор тестов для вкладки 'Конфигурация' в модуле Zero Touch Provisioning.
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ from pages.login_page import LoginPage
|
||||||
from pages.main_page import MainPage
|
from pages.main_page import MainPage
|
||||||
from pages.ztp_templates_tab import ZTPTemplatesTab
|
from pages.ztp_templates_tab import ZTPTemplatesTab
|
||||||
|
|
||||||
|
pytest.skip("Пропуск всех тестов в этом файле в связи исключением данной функциональности", allow_module_level=True)
|
||||||
|
|
||||||
# @pytest.mark.smoke
|
# @pytest.mark.smoke
|
||||||
class TestZTPTemplatesTab:
|
class TestZTPTemplatesTab:
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@
|
||||||
работы с пользователями системы.
|
работы с пользователями системы.
|
||||||
"""
|
"""
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
from playwright.sync_api import Page
|
from playwright.sync_api import Page
|
||||||
from pages.users_tab import UsersTab
|
from pages.users_tab import UsersTab
|
||||||
|
|
@ -83,9 +82,13 @@ class TestUsersTabAddUser:
|
||||||
|
|
||||||
ut.open_add_user_window()
|
ut.open_add_user_window()
|
||||||
ut.check_add_user_window_content()
|
ut.check_add_user_window_content()
|
||||||
ut.transform_to_add_AD_user_window()
|
|
||||||
ut.check_add_AD_user_window_content()
|
# skip as unsupported
|
||||||
ut.close_add_AD_user_window()
|
# ut.transform_to_add_AD_user_window()
|
||||||
|
# ut.check_add_AD_user_window_content()
|
||||||
|
# ut.close_add_AD_user_window()
|
||||||
|
|
||||||
|
ut.close_add_user_window()
|
||||||
|
|
||||||
# @pytest.mark.develop
|
# @pytest.mark.develop
|
||||||
def test_add_user_window_close_buttons(self, browser: Page) -> None:
|
def test_add_user_window_close_buttons(self, browser: Page) -> None:
|
||||||
|
|
@ -104,13 +107,14 @@ class TestUsersTabAddUser:
|
||||||
ut.open_add_user_window()
|
ut.open_add_user_window()
|
||||||
ut.close_add_user_window()
|
ut.close_add_user_window()
|
||||||
|
|
||||||
ut.open_add_user_window()
|
# skip as unsupported
|
||||||
ut.transform_to_add_AD_user_window()
|
# ut.open_add_user_window()
|
||||||
ut.close_add_AD_user_window_by_toolbar_button()
|
# ut.transform_to_add_AD_user_window()
|
||||||
|
# ut.close_add_AD_user_window_by_toolbar_button()
|
||||||
|
|
||||||
ut.open_add_user_window()
|
# ut.open_add_user_window()
|
||||||
ut.transform_to_add_AD_user_window()
|
# ut.transform_to_add_AD_user_window()
|
||||||
ut.close_add_AD_user_window()
|
# ut.close_add_AD_user_window()
|
||||||
|
|
||||||
# @pytest.mark.develop
|
# @pytest.mark.develop
|
||||||
def test_add_local_user(self, browser: Page, cleanup_users: None) -> None:
|
def test_add_local_user(self, browser: Page, cleanup_users: None) -> None:
|
||||||
|
|
@ -125,6 +129,7 @@ class TestUsersTabAddUser:
|
||||||
self._add_user(browser, user_data)
|
self._add_user(browser, user_data)
|
||||||
|
|
||||||
# @pytest.mark.develop
|
# @pytest.mark.develop
|
||||||
|
@pytest.mark.skip(reason="This test is temporarily disabled as test unsupported feature")
|
||||||
def test_add_AD_user(self, browser: Page, cleanup_users: None) -> None:
|
def test_add_AD_user(self, browser: Page, cleanup_users: None) -> None:
|
||||||
"""Проверяет добавление пользователя Active Directory.
|
"""Проверяет добавление пользователя Active Directory.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,7 @@ class TestUserCard:
|
||||||
|
|
||||||
# Выход из системы текущего пользователя
|
# Выход из системы текущего пользователя
|
||||||
mp = MainPage(browser)
|
mp = MainPage(browser)
|
||||||
|
mp.wait_for_timeout(1000)
|
||||||
mp.do_logout()
|
mp.do_logout()
|
||||||
|
|
||||||
# Авторизация администратором для очистки
|
# Авторизация администратором для очистки
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue