diff --git a/components_derived/container_system_log_events.py b/components_derived/container_system_log_events.py index 34f977c..bc99f6c 100644 --- a/components_derived/container_system_log_events.py +++ b/components_derived/container_system_log_events.py @@ -47,7 +47,6 @@ class SystemLogEventsContainer(EventsContainerComponent): events_tab = self.get_toolbar_tab_button("events") events_tab_text = events_tab.get_text(0) - assert events_tab_text.find("chevron_right") != -1, "Should be icon 'chevron_right'" assert events_tab_text.find("События") != -1, "Tab button with text События is missing on toolbar" self.should_be_events_table() diff --git a/components_derived/interactive_dropdown_list.py b/components_derived/interactive_dropdown_list.py new file mode 100644 index 0000000..f53bbf2 --- /dev/null +++ b/components_derived/interactive_dropdown_list.py @@ -0,0 +1,78 @@ +"""Модуль interactive_dropdown_list_component содержит класс для работы с интерактивными выпадающими списками, +позволяющими сделать выбор нескольких элементов. + +Класс InteractiveDropdownList наследует базовый функционал BaseComponent и добавляет +методы для взаимодействия с интерактивными выпадающими списками на странице. +""" + +from playwright.sync_api import Page, Locator, expect +from tools.logger import get_logger +from components.base_component import BaseComponent + +logger = get_logger("INTERACTIVE_DROPDOWN_LIST") + +class InteractiveDropdownList(BaseComponent): + """Класс для работы с выпадающими списками. + + Наследует функциональность BaseElement и добавляет специфичные + методы для выбора и проверки элементов списка. + """ + + def __init__(self, page: Page) -> None: + """Инициализирует компонент интерактивного выпадающего списка. + + Args: + page: Экземпляр страницы Playwright. + """ + + super().__init__(page) + + # Действия: + def get_checkbox_locator(self, text: str) -> Locator: + """Возвращает локатор чек-бокса для элемента списка с указанным текстом. + + Args: + text (str): Текст элемента для выбора. + """ + + checkbox_locator = self.get_locator('div.v-list__tile__title').get_by_text(text). \ + locator("../..").locator("//input[@role='checkbox']") + expect(checkbox_locator).to_be_visible(), \ + f"Checkbox for dropdown list item with text {text} is missing" + return checkbox_locator + + def deselect_item_with_text(self, text: str) -> None: + """Выбирает элемент списка по указанному тексту. + + Args: + text (str): Текст элемента для выбора. + """ + + self.get_checkbox_locator(text).uncheck(force=True) + + def select_item_with_text(self, text: str) -> None: + """Выбирает элемент списка по указанному тексту. + + Args: + text (str): Текст элемента для выбора. + """ + self.get_checkbox_locator(text).check(force=True) + + def get_selected_items(self, locator: str|Locator) -> list[str]: + """Возвращает список отмеченных элементов.""" + + selected_items = [] + + list_locator = self.get_locator(locator) + + items = list_locator.get_by_role("listitem").all() + + for item in items: + if item.get_by_role("checkbox").is_checked(): + item_text = item.text_content().strip() + if item_text: + selected_items.append(item_text) + + return selected_items + + # Проверки: diff --git a/components/settings_form_component.py b/components_derived/settings_form_component.py similarity index 83% rename from components/settings_form_component.py rename to components_derived/settings_form_component.py index ed20ced..59ec226 100644 --- a/components/settings_form_component.py +++ b/components_derived/settings_form_component.py @@ -4,11 +4,12 @@ from playwright.sync_api import Page, Locator from tools.logger import get_logger from locators.settings_form_locators import SettingsFormLocators +from elements.tooltip_button_element import TooltipButton from elements.button_element import Button from components.toolbar_component import ToolbarComponent from components.base_component import BaseComponent -logger = get_logger("MODAL_WINDOW") +logger = get_logger("SETTINGS_FORM") class SettingsFormComponent(BaseComponent): @@ -42,7 +43,12 @@ class SettingsFormComponent(BaseComponent): self.buttons.append(Button(self.page, locator, name)) - def get_button_by_name(self, name: str) -> Button | None: + def add_tooltip_button(self, locator: str, name: str) -> None: + """Добавляет кнопку в форму.""" + + self.buttons.append(TooltipButton(self.page, locator, name)) + + def get_button_by_name(self, name: str) -> Button | TooltipButton | None: """Ищет и возвращает кнопку по имени или None, если не найдена.""" for button in self.buttons: @@ -64,6 +70,14 @@ class SettingsFormComponent(BaseComponent): return self.is_scrollable_vertically(locator) + def check_button_tooltip(self, name: str, tooltip: str) -> None: + """Проверяет текст подсказки кнопки. """ + + button = self.get_button_by_name(name) + if button is None: + raise AssertionError(f"Unsupported button name {name}") + button.check_tooltip_with_text(tooltip) + def check_button_visibility(self, name: str) -> None: """Проверяет наличие кнопки по имени. Вызывает ошибку, если не найдена.""" diff --git a/locators/settings_form_locators.py b/locators/settings_form_locators.py index 905012a..7b7356d 100644 --- a/locators/settings_form_locators.py +++ b/locators/settings_form_locators.py @@ -7,10 +7,17 @@ class SettingsFormLocators: """Локаторы для компонента формы ввода и отображения полей настроек. - Содержит XPath локаторы для: + Содержит XPath/CSS локаторы для: SETTTINGS_FORM_SCROLL_CONTAINER (str): контейнера с прокруткой модального окна SETTTINGS_FORM_TITLE (str): заголовка тулбара + DROPDOWN_LIST (str): выпадающего списка + SELECTED_VALUES (str): строки с выбранными из списка значениями + CLEAR_SELECTION_BUTTON (str): кнопки удаления строки с выбранными из списка значениями """ SETTTINGS_FORM_SCROLL_CONTAINER = "//div[contains(@class, 'scrollarea__body')]" SETTTINGS_FORM_TITLE = f"{SETTTINGS_FORM_SCROLL_CONTAINER}//div[contains(@class, 'v-toolbar__title')]" + + DROPDOWN_LIST = "//div[contains(@class, 'menuable__content__active')]" + SELECTED_VALUES = "//div[@class='v-select__selections']" + CLEAR_SELECTION_BUTTON = "div.v-input__icon--clear" diff --git a/mkdocs.yml b/mkdocs.yml deleted file mode 100644 index 7e35ab7..0000000 --- a/mkdocs.yml +++ /dev/null @@ -1,111 +0,0 @@ -site_name: Документация тестов eNODE-Мониторинг -theme: - name: material - -plugins: - - search - - mkdocstrings: - default_handler: python - handlers: - python: - paths: [".", "pages"] - options: - show_source: true - -nav: - - Главная: index.md - - Данные и конфигурации: - - Constants: data/constants.md - - Environment: data/environment.md - - Roles_dict: data/roles_dict.md - - Фикстуры Pytest: - - Browser Fixtures: fixtures/pages.md - - Элементы UI: - - BaseElement: elements/base_element.md - - Button: elements/button_element.md - - Checkbox: elements/checkbox_element.md - - Icon: elements/icon_element.md - - TabButton: elements/tab_button_element.md - - Text: elements/text_element.md - - TextInput: elements/text_input_element.md - - ToolbarButton: elements/tooltip_button_element.md - - Компоненты UI: - - AlertComponent: components/alert_component.md - - BaseComponent: components/base_component.md - - CardComponent: components/card_component.md - - ConfirmComponent: components/confirm_component.md - - DropdownList: components/dropdown_list_component.md - - EventPanelComponent: components/eventbar_component.md - - EventsContainerComponent: components/events_container_component.md - - JsonContainerComponent: components/json_container_component.md - - ModalWindowComponent: components/modal_window_component.md - - NavigationPanelComponent: components/navbar_component.md - - TableComponent: components/table_component.md - - ToolbarComponent: components/toolbar_component.md - - Компоненты производные UI: - - SystemLogEventsContainer: components_derived/container_system_log_events.md - - AddADUserModalWindow: components_derived/modal_add_AD_user.md - - AddLocalUserModalWindow: components_derived/modal_add_local_user.md - - ChangePasswordModalWindow: components_derived/modal_change_password.md - - EditUserModalWindow: components_derived/modal_edit_user.md - - ViewTemplateModalWindow: components_derived/modal_view_template.md - - ViewZTPTemplateModalWindow: components_derived/modal_view_ztp_template.md - - UserCard: components_derived/user_card.md - - Локаторы: - - ButtonLocators: locators/button_locators.md - - ConfirmLocators: locators/confirm_locators.md - - EventPanelLocators: locators/event_panel_locators.md - - InputLocators: locators/input_locators.md - - JsonContainerLocators: locators/json_container_locators.md - - ModalWindowLocators: locators/modal_window_locators.md - - NavigationPanelLocators: locators/navigation_panel_locators.md - - RackLocators: locators/rack_locators.md # new - - SettingsFormLocators: locators/settings_form_locators.md # new - - TableLocators: locators/table_locators.md - - TextInputLocators: locators/text_input_locators.md - - TextLocators: locators/text_locators.md - - ToolbarLocators: locators/toolbar_locators.md - - UserCardLocators: locators/user_card_locators.md - - Страницы приложения: - - BasePage: pages/base_page.md - - LicenseTab: pages/license_tab.md - - LoginPage: pages/login_page.md - - MainPage: pages/main_page.md - - ServiceStatusTab: pages/service_status_tab.md - - CurrentSessionsTab: pages/current_session_tab.md # new - - SessionSettingsTab: pages/session_settings_tab.md # new - - TemplatesTab: pages/templates_tab.md - - UsersTab: pages/users_tab.md - - ZTPConfigTab: pages/ztp_config_tab.md - - ZTPTemplatesTab: pages/ztp_templates_tab.md - - Тесты: - - End-to-End: - - Sessions: - - TestCurrentSessionsTab: tests/e2e/sessions/test_current_sessions_tab.md # new - - TestCurrentSettingsTab: tests/e2e/sessions/test_session_settings_tab.md # new - - Users: - - TestUsersTabAddUser: tests/e2e/users/test_add_user.md - - TestUsersTabEditUser: tests/e2e/users/test_edit_user.md - - TestUserCard: tests/e2e/users/test_user_card.md - - TestUsersTab: tests/e2e/users/test_users_tab.md - - TestEventPanel: tests/e2e/test_event_panel.md - - TestNavigationPanel: tests/e2e/test_expand_navigation_panel.md - - TestLicenseTab: tests/e2e/test_license_tab.md - - TestLogin: tests/e2e/test_login.md - - TestServiceStatusTab: tests/e2e/test_service_status_tab.md - - TestSystemLogEventsContainer: tests/e2e/test_system_log_events_container.md - - TestTemplatesTab: tests/e2e/test_templates_tab.md - - TestZTPConfigTab: tests/e2e/test_ztp_config_tab.md - - TestZTPTemplatesTab: tests/e2e/test_ztp_templates_tab.md - - Компоненты: - - TestJsonContainer: tests/components/test_json_container.md - - TestNavigationPanel: tests/components/test_navigation_panel.md - - TestServiceStatusTable: tests/components/test_services_table.md - - TestUsersModalWindow: tests/components/test_user_modal_window.md - - Утилиты: - - Logging: tools/logger.md - - Python Project Fixer: tools/fix_python_project.md - - Инструкции: - - Документация проекта MkDocs: config/mkdocs_guide.md - - Требования при добавлении docstring: config/add_docstring.md - - Процесс разработки кода: config/code_development_process.md \ No newline at end of file diff --git a/pages/push_notifications_settings_tab.py b/pages/push_notifications_settings_tab.py new file mode 100644 index 0000000..a5c1c59 --- /dev/null +++ b/pages/push_notifications_settings_tab.py @@ -0,0 +1,195 @@ +"""Модуль вкладки настройки Push уведомлений. + +Содержит класс PushNotificationsSettings для работы с вкладкой настройки Push уведомлений. +Позволяет проверять состояние и взаимодействовать с элементами вкладки. +""" + +import re +from playwright.sync_api import Page +from locators.settings_form_locators import SettingsFormLocators +from elements.text_input_element import TextInput +from elements.text_element import Text +from components.toolbar_component import ToolbarComponent +from components.alert_component import AlertComponent +from components_derived.settings_form_component import SettingsFormComponent +from components_derived.interactive_dropdown_list import InteractiveDropdownList +from pages.base_page import BasePage + + +class PushNotificationsSettingsTab(BasePage): + """Класс для работы с вкладкой настройки Push уведомлений. + + Предоставляет методы для взаимодействия с вкладкой настройки Push уведомлений. + + Args: + page: Экземпляр страницы Playwright. + """ + + def __init__(self, page: Page) -> None: + """Инициализирует компоненты вкладки настройки Push уведомлений.""" + + super().__init__(page) + + + self.toolbar = ToolbarComponent(page, "Push уведомления") + + # Форма для отображения/редактирования полей настроек Push уведомлений + self.settings_form = SettingsFormComponent(page) + self.settings_form.add_toolbar_title("Общие") + + message_setting_label = Text(page, + page.locator(SettingsFormLocators.SETTTINGS_FORM_SCROLL_CONTAINER).\ + get_by_text('Сообщение'), + "message_setting_label") + self.settings_form.add_content_item("message_setting_label", message_setting_label) + + loc_message_input = page.locator(SettingsFormLocators.SETTTINGS_FORM_SCROLL_CONTAINER).\ + get_by_label('Сообщение').nth(1) + message_setting_input = TextInput(page, loc_message_input, "message_setting_input") + self.settings_form.add_content_item("message_setting_input", message_setting_input) + + users_settings_locator = page.locator(SettingsFormLocators.SETTTINGS_FORM_SCROLL_CONTAINER).\ + get_by_label('Пользователи') + users_setting_label = Text(page, users_settings_locator, "users_setting_label") + self.settings_form.add_content_item("users_setting_label", users_setting_label) + + users_setting_input = TextInput(page, + page.locator(SettingsFormLocators.SETTTINGS_FORM_SCROLL_CONTAINER).\ + get_by_role("combobox"), + "users_setting_input") + self.settings_form.add_content_item("users_setting_input", users_setting_input) + self.settings_form.add_content_item("users_list", InteractiveDropdownList(page)) + + self.settings_form.add_tooltip_button(page.locator(SettingsFormLocators.SETTTINGS_FORM_SCROLL_CONTAINER).\ + get_by_role("button", name='Отправить'), + "submit_button") + + self.alert = AlertComponent(page) + + # Действия: + def clear_users_setting_value(self) -> None: + """Очищает текущее значение поля настроек 'Пользователи'.""" + + selected_users = self.get_users_setting_value() + if len(selected_users) > 0: + clear_selection_button = self.page.locator(SettingsFormLocators.SETTTINGS_FORM_SCROLL_CONTAINER).\ + get_by_role("combobox").locator(SettingsFormLocators.CLEAR_SELECTION_BUTTON) + clear_selection_button.click() + + def click_submit_button(self) -> None: + """Нажатие кнопки 'Отправить' в форме ввода настроек.""" + + self.settings_form.check_button_visibility("submit_button") + self.settings_form.get_button_by_name("submit_button").click() + + def get_message_setting_value(self) -> str: + """Возвращает текущее значение поля настроек 'Сообщение'. + + Returns: + str : Текущее значение поля настроек 'Сообщение'. + """ + + input_field = self.settings_form.get_content_item("message_setting_input") + return input_field.get_input_value().strip() + + def get_users_setting_value(self) -> str: + """Возвращает текущее значение поля настроек 'Пользователи'. + + Returns: + str : Текущее значение поля настроек 'Пользователи'. + """ + + users_setting_field_loc = self.page.locator(SettingsFormLocators.SETTTINGS_FORM_SCROLL_CONTAINER).\ + get_by_role("combobox").locator(SettingsFormLocators.SELECTED_VALUES) + + return users_setting_field_loc.text_content().strip() + + def input_message(self, text: str) -> None: + """Заполнение поля 'Сообщение'.""" + + message_input = self.settings_form.get_content_item("message_setting_input") + message_input.clear() + message_input.input_value(text) + + def deselect_users(self, users: list[str]) -> None: + """Изменение значения поля 'Пользователи' путем отмены выбора из выпадающего списка заданных имен.""" + + assert len(users) != 0, "Users list should not be empty" + + self.settings_form.get_content_item("users_setting_input").click() + users_list = self.settings_form.get_content_item("users_list") + + for user in users: + users_list.deselect_item_with_text(user) + + # Закрываем выпадающий список (кликаем вне его) + self.page.mouse.click(10, 10) + + def select_users(self, users: list[str]) -> None: + """Заполнение поля 'Пользователи' путем выбора из выпадающего списка заданных имен.""" + + assert len(users) != 0, "Users list should not be empty" + + self.settings_form.get_content_item("users_setting_input").click() + users_list = self.settings_form.get_content_item("users_list") + + for user in users: + users_list.select_item_with_text(user) + + # Закрываем выпадающий список (кликаем вне его) + self.page.mouse.click(10, 10) + + # Проверки: + def check_content(self): + """Проверяет наличие и корректность всех элементов страницы.""" + + self.should_be_toolbar() + + self.should_be_form_toolbar() + + for name in self.settings_form.content_items.keys(): + if name == "users_list": + self.settings_form.get_content_item("users_setting_input").click() + users_list = self.settings_form.get_content_item(name) + selected_users = users_list.get_selected_items(SettingsFormLocators.DROPDOWN_LIST) + assert len(selected_users) == 0, "There should be no selected users" + else: + item = self.settings_form.get_content_item(name) + item.check_visibility( + f"Push notifications settings input form item with name '{name}' is missing" + ) + + self.settings_form.check_button_visibility("submit_button") + self.settings_form.check_button_tooltip("submit_button", "Отправить Push уведомление") + + def should_be_toolbar(self) -> None: + """Проверяет наличие тулбара страницы. + + Raises: + AssertionError: Если тулбар или кнопка редактирования отсутствуют. + """ + loc = self.page.get_by_role("navigation").filter( + has_text=re.compile("Push уведомления")).locator("div").nth(1) + self.toolbar.check_toolbar_presence_by_locator(loc, "Toolbar with title 'Push уведомления' is missing") + + def should_be_form_toolbar(self) -> None: + """Проверяет наличие тулбара формы редактирования настроек. + + Raises: + AssertionError: Если тулбар отсутствует. + """ + + self.settings_form.should_be_toolbar() + + def should_be_success_alert(self) -> None: + """Проверяет наличие сообщения об успешной отправке push-уведомления. + + Raises: + AssertionError: Если тулбар отсутствует. + """ + + alert_type = self.alert.get_alert_type() + assert alert_type == "success", f"Expected success alert, but got {alert_type} alert" + + self.alert.check_alert_presence('\nPush-уведомление\nуспешно отправлено\n') + self.alert.check_alert_absence('\nPush-уведомление\nуспешно отправлено\n') diff --git a/pages/session_settings_tab.py b/pages/session_settings_tab.py index 7d6181a..93e8dcf 100644 --- a/pages/session_settings_tab.py +++ b/pages/session_settings_tab.py @@ -9,8 +9,8 @@ from locators.settings_form_locators import SettingsFormLocators from elements.text_input_element import TextInput from elements.text_element import Text from components.toolbar_component import ToolbarComponent -from components.settings_form_component import SettingsFormComponent from components.alert_component import AlertComponent +from components_derived.settings_form_component import SettingsFormComponent from pages.base_page import BasePage diff --git a/tests/e2e/test_push_notifications_settings_tab.py b/tests/e2e/test_push_notifications_settings_tab.py new file mode 100644 index 0000000..9cfecca --- /dev/null +++ b/tests/e2e/test_push_notifications_settings_tab.py @@ -0,0 +1,124 @@ +"""Модуль тестов вкладки настройки Push уведомлений. + +Содержит тесты для проверки корректности отображения +и функциональности элементов страницы настройки Push уведомлений. +""" + +import pytest +from playwright.sync_api import Page +from pages.login_page import LoginPage +from pages.main_page import MainPage +from pages.push_notifications_settings_tab import PushNotificationsSettingsTab + + +# @pytest.mark.smoke +class TestPushNotificationsSettingsTab: + """Набор тестов для вкладки настройки Push уведомлений. + + Проверяет корректность отображения и функциональность элементов вкладки настройки Push уведомлений. + + Тесты покрывают следующие сценарии: + 1. test_session_settings_tab_content: Тест содержимого вкладки настройки Push уведомлений + + """ + + @pytest.fixture(scope="function", autouse=True) + def setup(self, browser: Page) -> None: + """Фикстура для подготовки тестового окружения. + + Выполняет: + 1. Авторизацию в системе + 2. Переход на вкладку настройки Push уведомлений через панель навигации + """ + # Авторизация в системе + login_page = LoginPage(browser) + login_page.do_login() + + # Инициализация главной страницы + main_page = MainPage(browser) + + # Проверка и взаимодействие с элементами навигации + main_page.should_be_navigation_panel() + main_page.click_main_navigation_panel_item("Настройки") + main_page.click_subpanel_item("Уведомления") + main_page.click_subpanel_item("Push уведомления") + + # @pytest.mark.develop + def test_push_notifications_settings_tab_content(self, browser: Page) -> None: + """Тест содержимого вкладки настройки Push уведомлений. + + Проверяет: + Наличие и корректность элементов интерфейса + """ + expected_msg_value = "test" + + # Инициализация вкладки + push_notification_settings_tab = PushNotificationsSettingsTab(browser) + + # Проверка элементов интерфейса + push_notification_settings_tab.check_content() + + msg_value = push_notification_settings_tab.get_message_setting_value() + assert msg_value == expected_msg_value, \ + f"Actual message field value {msg_value} is not equal expected message field value {expected_msg_value}" + + # @pytest.mark.develop + def test_send_push_notification(self, browser: Page) -> None: + """Тест содержимого вкладки настройки Push уведомлений. + + Проверяет: + Заполнение полей и отправку Push уведомления + """ + receivers = ["admin", "manager", "operator"] + + # Инициализация вкладки + push_notification_settings_tab = PushNotificationsSettingsTab(browser) + + expected_msg_value = "My test message" + push_notification_settings_tab.input_message(expected_msg_value) + msg_value = push_notification_settings_tab.get_message_setting_value() + assert msg_value == expected_msg_value, \ + f"Actual message field value {msg_value} is not equal expected message field value {expected_msg_value}" + + push_notification_settings_tab.select_users(receivers) + + sep = ", " + expected_users = sep.join(receivers) + selected_users = push_notification_settings_tab.get_users_setting_value() + assert selected_users == expected_users, \ + f"Actual users field value {selected_users} is not equal expected users field value {expected_users}" + + push_notification_settings_tab.click_submit_button() + push_notification_settings_tab.should_be_success_alert() + + # @pytest.mark.develop + def test_users_setting_input(self, browser: Page) -> None: + """Тест содержимого вкладки настройки Push уведомлений. + + Проверяет: + Заполнение и очистку поля 'Пользователи' + """ + receivers = ["manager", "operator", "collector"] + sep = ", " + + # Инициализация вкладки + push_notification_settings_tab = PushNotificationsSettingsTab(browser) + + push_notification_settings_tab.select_users(receivers) + + expected_users = sep.join(receivers) + selected_users = push_notification_settings_tab.get_users_setting_value() + assert selected_users == expected_users, \ + f"Actual users field value {selected_users} is not equal expected users field value {expected_users}" + + receivers.remove("collector") + push_notification_settings_tab.deselect_users(["collector"]) + + expected_users = sep.join(receivers) + selected_users = push_notification_settings_tab.get_users_setting_value() + assert selected_users == expected_users, \ + f"Actual users field value {selected_users} is not equal expected users field value {expected_users}" + + push_notification_settings_tab.clear_users_setting_value() + selected_users = push_notification_settings_tab.get_users_setting_value() + assert len(selected_users) == 0, "There should be no selected users" diff --git a/tests/e2e/test_service_status_tab.py b/tests/e2e/test_service_status_tab.py index 0a42e88..4be4768 100644 --- a/tests/e2e/test_service_status_tab.py +++ b/tests/e2e/test_service_status_tab.py @@ -11,6 +11,7 @@ from pages.service_status_tab import ServiceStatusTab from pages.main_page import MainPage from pages.login_page import LoginPage +pytest.skip("пропуск всех тестов в этом файле в связи с переходом на новый виджет", allow_module_level=True) # @pytest.mark.smoke class TestServiceStatusTab: