From e3804bd9c9fd4e18a21f83b398bfe690fd987fbd Mon Sep 17 00:00:00 2001 From: nsubbot Date: Tue, 28 Apr 2026 13:34:05 +0300 Subject: [PATCH] =?UTF-8?q?=D0=90=D0=BA=D1=82=D1=83=D0=B0=D0=BB=D0=B8?= =?UTF-8?q?=D0=B7=D0=B0=D1=86=D0=B8=D1=8F=20=D1=82=D0=B5=D1=81=D1=82=D0=BE?= =?UTF-8?q?=D0=B2=20=D0=BF=D0=BE=D1=81=D0=BB=D0=B5=20=D0=BF=D0=B5=D1=80?= =?UTF-8?q?=D0=B5=D1=85=D0=BE=D0=B4=D0=B0=20=D0=BD=D0=B0=20=D0=B2=D0=B5?= =?UTF-8?q?=D1=80=D1=81=D0=B8=D1=8E=20=D0=BF=D1=80=D0=B8=D0=BB=D0=BE=D0=B6?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D1=8F=201.40?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.12 | 3 + components/checkbox_group_component.py | 31 +++++++ components_derived/modal_send_test_email.py | 13 +++ components_derived/selection_bar_component.py | 19 ++++- data/environment.py | 2 +- pages/email_notifications_settings_tab.py | 4 +- .../test_actions_events_container.py | 84 ++++++++++--------- .../test_email_notifications_settings_tab.py | 47 +++++++++-- .../test_push_notifications_settings_tab.py | 2 +- tests/e2e/test_backup_settings_tab.py | 12 +-- tests/e2e/test_certificates_tab.py | 1 + 11 files changed, 161 insertions(+), 57 deletions(-) create mode 100644 .env.12 diff --git a/.env.12 b/.env.12 new file mode 100644 index 0000000..7571ece --- /dev/null +++ b/.env.12 @@ -0,0 +1,3 @@ +ENV=test +AUTH_LOGIN = admin +AUTH_PASSWORD = enodemon-admin diff --git a/components/checkbox_group_component.py b/components/checkbox_group_component.py index 9f52260..fd21ca6 100644 --- a/components/checkbox_group_component.py +++ b/components/checkbox_group_component.py @@ -31,6 +31,37 @@ class CheckboxGroupComponent(BaseComponent): def get_checkbox_locator(self, text: str, container_locator: Locator | None = None) -> Locator: """Возвращает локатор чек-бокса с указанным текстом. + Args: + text (str): Текст элемента для выбора. + container_locator (Locator | None): Локатор контейнера с чек-боксами. + Если не указан, поиск по всей странице. + + Returns: + Locator: Локатор чек-бокса. + """ + + if container_locator: + listitem_locator = container_locator.get_by_role("listitem") + else: + listitem_locator = self.page.locator("//div[contains(@class, 'menuable__content__active')]"). \ + get_by_role("listitem") + + listitem_locator.last.scroll_into_view_if_needed() + listitem_locator.last.wait_for(state="visible") + + all_items = listitem_locator.all() + for i, item in enumerate(all_items): + if item.inner_text() == text: + checkbox_locator = item.get_by_role("checkbox") + expect(checkbox_locator).to_be_visible(), \ + f"Checkbox with text '{text}' is missing or not visible" + return checkbox_locator + + assert False, f"Checkbox locator for {text} has not been found" + + def get_checkbox_locator_or(self, text: str, container_locator: Locator | None = None) -> Locator: + """Возвращает локатор чек-бокса с указанным текстом. + Args: text (str): Текст элемента для выбора. container_locator (Locator | None): Локатор контейнера с чек-боксами. diff --git a/components_derived/modal_send_test_email.py b/components_derived/modal_send_test_email.py index 576e22f..c0ecd12 100644 --- a/components_derived/modal_send_test_email.py +++ b/components_derived/modal_send_test_email.py @@ -106,3 +106,16 @@ class SendTestEmailModalWindow(ModalWindowComponent): self.alert.check_alert_presence('\nТестовое сообщение\nотправлено\n') self.alert.check_alert_absence('\nТестовое сообщение\nотправлено\n') + + def should_be_error_alert(self, alert_text: str) -> None: + """Проверяет наличие сообщения об неуспешной отправке тестового сообщения. + + Raises: + AssertionError: Если тулбар отсутствует. + """ + + alert_type = self.alert.get_alert_type() + assert alert_type == "error", f"Expected error alert, but got {alert_type} alert" + + self.alert.check_alert_presence(alert_text) + self.alert.check_alert_absence(alert_text) diff --git a/components_derived/selection_bar_component.py b/components_derived/selection_bar_component.py index eb50036..be92d59 100644 --- a/components_derived/selection_bar_component.py +++ b/components_derived/selection_bar_component.py @@ -55,6 +55,10 @@ class SelectionBarComponent(BaseComponent): clear_button_locator = self.selection_bar_locator.locator( SelectionBarLocators.CLEAR_SELECTION_BUTTON ) + if clear_button_locator.count() == 0: + clear_button_locator = self.selection_bar_locator.locator("../..").locator( + SelectionBarLocators.CLEAR_SELECTION_BUTTON + ) clear_button_locator.click() def get_available_options(self) -> list[str]: @@ -87,8 +91,13 @@ class SelectionBarComponent(BaseComponent): def get_selection_bar_title(self) -> str: """Возвращает название панели выбора значения""" + title_text = "" title_locator = self.selection_bar_locator.locator(SelectionBarLocators.TITLE_LOCATOR) - return title_locator.text_content() + if title_locator.count() > 0: + title_text = title_locator.text_content() + else: + title_text = self.selection_bar_locator.get_attribute("placeholder") + return title_text def get_selected_values(self) -> list[str]: """Возвращает список выбранных значений""" @@ -96,6 +105,11 @@ class SelectionBarComponent(BaseComponent): selected_values_locator = self.selection_bar_locator.locator( SelectionBarLocators.PARAMETERS_SELECTED ) + if selected_values_locator.count() == 0: + selected_values_locator = self.selection_bar_locator.locator("../..").locator( + SelectionBarLocators.PARAMETERS_SELECTED + ) + print(selected_values_locator) selected_values = selected_values_locator.all_inner_texts() return selected_values[0].splitlines() @@ -153,6 +167,9 @@ class SelectionBarComponent(BaseComponent): self.selection_bar_locator.click(force=True) # Ждем появления выпадающего списка + if self.page.locator(SelectionBarLocators.LIST_ACTIVE).count() == 0: + assert False, "Values list is empty" + if self.page.locator(SelectionBarLocators.LIST_ACTIVE).count() > 1: self.page.locator(SelectionBarLocators.LIST_ACTIVE).last.wait_for(state="attached") else: diff --git a/data/environment.py b/data/environment.py index 9198990..1e2c78e 100644 --- a/data/environment.py +++ b/data/environment.py @@ -20,7 +20,7 @@ class Environment: DEVELOP: str = 'develop' URLS: Dict[str, str] = { - TEST: 'https://192.168.2.76/', + TEST: 'https://192.168.236.12/', DEVELOP: 'https://192.168.2.69/' } diff --git a/pages/email_notifications_settings_tab.py b/pages/email_notifications_settings_tab.py index b35c325..6f6fcfd 100644 --- a/pages/email_notifications_settings_tab.py +++ b/pages/email_notifications_settings_tab.py @@ -414,8 +414,8 @@ class EmailNotificationsSettingsTab(BasePage): if name == "checkbox_activate": is_activate_checked = item.is_checked() - assert not is_activate_checked, ( - "Checkbox 'Активировать' should not be checked by default" + assert is_activate_checked, ( + "Checkbox 'Активировать' should be checked" ) def _check_tls_settings_content(self): diff --git a/tests/e2e/event_panel/test_actions_events_container.py b/tests/e2e/event_panel/test_actions_events_container.py index 74a0f4e..e429d48 100644 --- a/tests/e2e/event_panel/test_actions_events_container.py +++ b/tests/e2e/event_panel/test_actions_events_container.py @@ -81,6 +81,8 @@ class TestActionsEventsContainer: browser: Экземпляр страницы Playwright. """ + rows_to_start_scrolling = 20 + lp = LoginPage(browser) lp.do_login() @@ -90,58 +92,62 @@ class TestActionsEventsContainer: actions_events_container = mp.click_events_panel_actions_tab() actions_events_container.click_archive_button() - events_panel_position = mp.get_events_panel_position() + rows_count = actions_events_container.get_events_table_rows_count() + if rows_count > rows_to_start_scrolling: + events_panel_position = mp.get_events_panel_position() - # Проверка, что панель с таблицей открыта - assert events_panel_position != "bottom", "Panel with actions events should be opened" + # Проверка, что панель с таблицей открыта + assert events_panel_position != "bottom", "Panel with actions events should be opened" - is_scrollable = actions_events_container.check_events_table_verticall_scrolling() + is_scrollable = actions_events_container.check_events_table_verticall_scrolling() - # Убеждаемся, что панель открыта наполовину и проверяем скроллинг - assert events_panel_position == "center",\ - "Panel with actions events should be located on the main page center" - assert is_scrollable, "Actions events table should be scrollable" + # Убеждаемся, что панель открыта наполовину и проверяем скроллинг + assert events_panel_position == "center",\ + "Panel with actions events should be located on the main page center" + assert is_scrollable, "Actions events table should be scrollable" - # Скроллинг вниз - actions_events_container.scroll_events_table_down() - browser.wait_for_timeout(1000) + # Скроллинг вниз + actions_events_container.scroll_events_table_down() + browser.wait_for_timeout(1000) - # Проверка видимости последней строки после прокрутки - actions_events_container.check_events_table_last_row_visibility() + # Проверка видимости последней строки после прокрутки + actions_events_container.check_events_table_last_row_visibility() - # Скроллинг вверх - actions_events_container.scroll_events_table_up() - browser.wait_for_timeout(1000) + # Скроллинг вверх + actions_events_container.scroll_events_table_up() + browser.wait_for_timeout(1000) - # Проверка видимости первой строки после прокрутки - actions_events_container.check_events_table_first_row_visibility() + # Проверка видимости первой строки после прокрутки + actions_events_container.check_events_table_first_row_visibility() - # Раскрываем панель полностью и проверяем скроллинг - assert mp.check_expand_more_button(), \ - "Expand more button should be present" - mp.click_events_panel_expand_more_button() - mp.wait_for_timeout(500) + # Раскрываем панель полностью и проверяем скроллинг + assert mp.check_expand_more_button(), \ + "Expand more button should be present" + mp.click_events_panel_expand_more_button() + mp.wait_for_timeout(500) - events_panel_position = mp.get_events_panel_position() - assert events_panel_position == "top",\ - "Panel with actions events should be located on the main page top" + events_panel_position = mp.get_events_panel_position() + assert events_panel_position == "top",\ + "Panel with actions events should be located on the main page top" - is_scrollable = actions_events_container.check_events_table_verticall_scrolling() - assert is_scrollable, "Actions events table should be scrollable in the full window" + is_scrollable = actions_events_container.check_events_table_verticall_scrolling() + assert is_scrollable, "Actions events table should be scrollable in the full window" - # Скроллинг вниз - actions_events_container.scroll_events_table_down() - browser.wait_for_timeout(1000) + # Скроллинг вниз + actions_events_container.scroll_events_table_down() + browser.wait_for_timeout(1000) - # Проверка видимости последней строки после прокрутки - actions_events_container.check_events_table_last_row_visibility() + # Проверка видимости последней строки после прокрутки + actions_events_container.check_events_table_last_row_visibility() - # Скроллинг вверх - actions_events_container.scroll_events_table_up() - browser.wait_for_timeout(1000) + # Скроллинг вверх + actions_events_container.scroll_events_table_up() + browser.wait_for_timeout(1000) - # Проверка видимости первой строки после прокрутки - actions_events_container.check_events_table_first_row_visibility() + # Проверка видимости первой строки после прокрутки + actions_events_container.check_events_table_first_row_visibility() + else: + print("Not enough data to check vertical scrolling") # @pytest.mark.develop def test_real_time_task_view(self, browser: Page): @@ -255,7 +261,7 @@ class TestActionsEventsContainer: # convert2timestamp=True) # assert is_descending_order, "Column data should be in descending order" - @pytest.mark.develop + # @pytest.mark.develop def test_real_time_events_table_pagination(self, browser: Page): """Проверяет возможность пагинации таблицы событий на примере вкладки 'Реальное время'. diff --git a/tests/e2e/notifications/test_email_notifications_settings_tab.py b/tests/e2e/notifications/test_email_notifications_settings_tab.py index 59746f3..c8f90c3 100644 --- a/tests/e2e/notifications/test_email_notifications_settings_tab.py +++ b/tests/e2e/notifications/test_email_notifications_settings_tab.py @@ -157,16 +157,14 @@ class TestEmailNotificationsSettingsTab: send_test_email_window.close_by_toolbar_button() # @pytest.mark.develop - # TO-DO: rewrite tescase after feature release - def test_send_test_email(self, browser: Page) -> None: + def test_send_test_email_successful(self, browser: Page) -> None: """Тест модального окна для посылки тестового E-mail. Проверяет: - Возможность посылки тестового E-mail. + Возможность посылки тестового E-mail по существующему адресу. """ # Адрес куда отправлять e-mail - # Пока фейковый - fake_address = "test@grandpas_village.com" + sent_address = "audiomine.platform@gmail.com" # Инициализация вкладки email_notification_settings_tab = EmailNotificationsSettingsTab(browser) @@ -176,17 +174,50 @@ class TestEmailNotificationsSettingsTab: send_test_email_window = email_notification_settings_tab.click_test_button() - send_test_email_window.input_email(fake_address) - + send_test_email_window.input_email(sent_address) + with browser.expect_response("**/e-nms/email/testEmail") as response_info: send_test_email_window.click_test_button() send_test_email_window.should_be_success_alert() - + response = response_info.value assert response.ok, "Unsuccessful test e-mail request" send_test_email_window.close() + # @pytest.mark.develop + def test_send_test_email_address_validation(self, browser: Page) -> None: + """Тест модального окна для посылки тестового E-mail. + + Проверяет: + Валидацию вводимого адреса E-mail. + """ + # Адрес куда отправлять e-mail - фейковый + incorrect_addresses = ["rrrrr", "@mail.ru", "rrrmail.ru", "rr@mail", "rrrr@@mail.ru", "rr@mailru", "rr@my_mail.ru"] + + # Инициализация вкладки + email_notification_settings_tab = EmailNotificationsSettingsTab(browser) + + send_test_email_window = email_notification_settings_tab.click_test_button() + + # Пустое поле ввода адреса + send_test_email_window.click_test_button() + send_test_email_window.should_be_error_alert('\nПоле должно быть заполнено\n') + + for address in incorrect_addresses: + send_test_email_window.input_email(address) + send_test_email_window.click_test_button() + send_test_email_window.should_be_error_alert('\nНекорректный e-mail\n') + + # нет проверки валидности домена + # fake_address = "test@grandpasvillage.com" + + # send_test_email_window.input_email(fake_address) + # send_test_email_window.click_test_button() + # send_test_email_window.should_be_error_alert('\Ошибка входа в систему\n') + + send_test_email_window.close() + def _get_default_value(self, setting_name: str, default_settings: dict) -> str| None: for setting in default_settings: if setting["name"] == setting_name: diff --git a/tests/e2e/notifications/test_push_notifications_settings_tab.py b/tests/e2e/notifications/test_push_notifications_settings_tab.py index aca57f2..a6f6715 100644 --- a/tests/e2e/notifications/test_push_notifications_settings_tab.py +++ b/tests/e2e/notifications/test_push_notifications_settings_tab.py @@ -62,7 +62,7 @@ class TestPushNotificationsSettingsTab: 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 + @pytest.mark.develop def test_send_push_notification(self, browser: Page) -> None: """Тест содержимого вкладки настройки Push уведомлений. diff --git a/tests/e2e/test_backup_settings_tab.py b/tests/e2e/test_backup_settings_tab.py index 9cc0b8f..4e9b34e 100644 --- a/tests/e2e/test_backup_settings_tab.py +++ b/tests/e2e/test_backup_settings_tab.py @@ -46,7 +46,7 @@ class TestBackupSettingsTab: main_page.click_subpanel_item("Обслуживание и диагностика") main_page.click_subpanel_item("Резервное копирование") - # @pytest.mark.develop + @pytest.mark.develop def test_backup_settings_tab_content(self, browser: Page) -> None: """Тест содержимого вкладки 'Резервное копирование'. @@ -166,10 +166,12 @@ class TestBackupSettingsTab: for setting in settings_list: expected = expected_sd_settings[setting] if dates.get(expected): - expected = dates[expected] + expected_setting = dates[expected] + else: + expected_setting = str(expected) actual = streaming_data_settings[setting] - assert actual == expected,\ - f"Actual value {actual} is not equal expected {expected} for field '{setting}'" + assert actual == expected_setting,\ + f"Actual value {actual} is not equal expected {expected_setting} for field '{setting}'" # @pytest.mark.develop def test_backup_settings_tab_check_backup_copies_amount(self, browser: Page) -> None: @@ -341,7 +343,7 @@ class TestBackupSettingsTab: backup_settings_tab.input_inventory_backups_number(orig_backup_limitation) backup_settings_tab.click_save_button() - @pytest.mark.develop + # @pytest.mark.develop def test_backup_settings_tab_set_streaming_data_settings(self, browser: Page) -> None: """Тест проверки возможности изменения значения настроек 'Потоковые данные'.""" diff --git a/tests/e2e/test_certificates_tab.py b/tests/e2e/test_certificates_tab.py index 72fcab2..3af02af 100644 --- a/tests/e2e/test_certificates_tab.py +++ b/tests/e2e/test_certificates_tab.py @@ -69,6 +69,7 @@ class TestCertificatesTab: certificates_tab = CertificatesTab(browser) certificates_tab.click_certificate_tab_button() + certificates_tab.wait_for_timeout(5000) viewed_certificate = certificates_tab.get_certificate() db_certificate_response = certificates_tab.send_get_api_request("api/certs/infoCert")