diff --git a/components_derived/modal_change_password.py b/components_derived/modal_change_password.py index 34f4593..7b00164 100644 --- a/components_derived/modal_change_password.py +++ b/components_derived/modal_change_password.py @@ -10,6 +10,7 @@ from locators.text_input_locators import TextInputLocators from locators.modal_window_locators import ModalWindowLocators from elements.text_element import Text from elements.text_input_element import TextInput +from elements.icon_element import Icon from data.environment import host from components.modal_window_component import ModalWindowComponent from components.alert_component import AlertComponent @@ -40,14 +41,29 @@ class ChangePasswordModalWindow(ModalWindowComponent): loc = modal_window_locator.get_by_label("Введите текущий пароль *") old_password_input = TextInput(page, loc, "old_password_input") self.add_content_item("old_password_input", old_password_input) + + icon_locator = loc.locator("xpath=../..").locator(TextInputLocators.ICON_PASSWORD_HIDING) + old_password_hidden_icon = Icon(page, icon_locator, + "old password hidden icon") + self.add_content_item("old_password_hidden_icon", old_password_hidden_icon) loc = modal_window_locator.get_by_label("Введите новый пароль *") new_password_input = TextInput(page, loc, "new_password_input") self.add_content_item("new_password_input", new_password_input) + + icon_locator = loc.locator("xpath=../..").locator(TextInputLocators.ICON_PASSWORD_HIDING) + new_password_hidden_icon = Icon(page, icon_locator, + "new password hidden icon") + self.add_content_item("new_password_hidden_icon", new_password_hidden_icon) loc = modal_window_locator.get_by_label("Введите повторно новый пароль *") confirm_password_input = TextInput(page, loc, "confirm_password_input") self.add_content_item("confirm_password_input", confirm_password_input) + + icon_locator = loc.locator("xpath=../..").locator(TextInputLocators.ICON_PASSWORD_HIDING) + confirm_password_hidden_icon = Icon(page, icon_locator, + "confirm password hidden icon") + self.add_content_item("confirm_password_hidden_icon", confirm_password_hidden_icon) input_form_error_message = Text(page, modal_window_locator.locator(TextInputLocators.INPUT_FORM_MESSAGE), @@ -69,6 +85,21 @@ class ChangePasswordModalWindow(ModalWindowComponent): """Нажимает кнопку 'Отменить'""" self.get_button_by_name("cancel").click() + + def click_old_password_hidden_icon(self) -> None: + """Нажатие на иконку скрытия старого пароля.""" + + self.get_content_item("old_password_hidden_icon").click() + + def click_new_password_hidden_icon(self) -> None: + """Нажатие на иконку скрытия нового пароля.""" + + self.get_content_item("new_password_hidden_icon").click() + + def click_confirm_password_hidden_icon(self) -> None: + """Нажатие на иконку скрытия пароля подтверждения.""" + + self.get_content_item("confirm_password_hidden_icon").click() def change_password(self, old_password: str, new_password: str): """Заполняет элементы формы, нажимает кнопку 'Сохранить'""" @@ -124,12 +155,32 @@ class ChangePasswordModalWindow(ModalWindowComponent): self.get_content_item("old_password_input").check_visibility( "Old password input form is missing" ) + old_password_hidden_icon = self.get_content_item("old_password_hidden_icon") + old_password_hidden_icon.check_visibility( + "Old password hidden icon is missing" + ) + is_hidden_state = old_password_hidden_icon.is_password_hidden() + assert is_hidden_state, "Old password hidden icon should be in hidden state" + self.get_content_item("new_password_input").check_visibility( "New password input form is missing" ) + new_password_hidden_icon = self.get_content_item("new_password_hidden_icon") + new_password_hidden_icon.check_visibility( + "New password hidden icon is missing" + ) + is_hidden_state = new_password_hidden_icon.is_password_hidden() + assert is_hidden_state, "New password hidden icon should be in hidden state" + self.get_content_item("confirm_password_input").check_visibility( "Confirm password input form is missing" ) + confirm_password_hidden_icon = self.get_content_item("confirm_password_hidden_icon") + confirm_password_hidden_icon.check_visibility( + "Confirm password hidden icon is missing" + ) + is_hidden_state = confirm_password_hidden_icon.is_password_hidden() + assert is_hidden_state, "Confirm password hidden icon should be in hidden state" self.check_button_visibility("cancel") diff --git a/elements/icon_element.py b/elements/icon_element.py new file mode 100644 index 0000000..291a582 --- /dev/null +++ b/elements/icon_element.py @@ -0,0 +1,40 @@ +"""Модуль icon_element содержит класс для работы с элементом типа иконка. + +Класс Icon наследует базовый функционал BaseElement и предоставляет +методы для работы с текстовыми элементами на странице. +""" + +from tools.logger import get_logger +from elements.base_element import BaseElement + +logger = get_logger("ICON") + +class Icon(BaseElement): + """Класс для работы с элементом типа иконка. + + Наследует функциональность BaseElement и добавляет специфичные + методы для взаимодействия с элементом типа иконка. + """ + + @property + def type_of(self) -> str: + """Возвращает тип элемента ('иконка'). + + Returns: + str: Тип элемента - 'иконка'. + """ + + return "icon" + + # Действия: + # (Методы действий будут добавлены по мере необходимости) + + # Проверки: + def is_password_hidden(self) -> bool: + """Проверяет состояние иконки скрытия пароля.""" + + state_text = self.locator.text_content() + state = True + if state_text == "visibility": + state = False + return state diff --git a/elements/text_input_element.py b/elements/text_input_element.py index 10a3720..b9c9a65 100644 --- a/elements/text_input_element.py +++ b/elements/text_input_element.py @@ -39,6 +39,15 @@ class TextInput(BaseElement): logger.info(f'Getting value from text input "{self.name}"') return self.locator.input_value() + def get_input_type(self) -> str: + """Возвращает значение аттрибута type поля ввода. + + Returns: + str: Значение аттрибута type. + """ + + return self.locator.get_attribute("type") + def input_value(self, value: str) -> None: """Вводит указанное значение в поле. diff --git a/locators/text_input_locators.py b/locators/text_input_locators.py index 3a67296..9585ae2 100644 --- a/locators/text_input_locators.py +++ b/locators/text_input_locators.py @@ -12,3 +12,4 @@ class TextInputLocators: """ INPUT_FORM_MESSAGE = "//div[contains(@class,'v-messages__message')]" + ICON_PASSWORD_HIDING = "//i[contains(@class,'v-icon')]" diff --git a/pages/login_page.py b/pages/login_page.py index eb5fe15..8a8826d 100644 --- a/pages/login_page.py +++ b/pages/login_page.py @@ -5,7 +5,9 @@ """ from playwright.sync_api import Page +from locators.text_input_locators import TextInputLocators from elements.text_input_element import TextInput +from elements.icon_element import Icon from elements.button_element import Button from data.environment import host from data.constants import Constants @@ -34,11 +36,21 @@ class LoginPage(BasePage): self.login_input = TextInput(page, page.get_by_label("Имя пользователя"), "login input") self.password_input = TextInput(page, page.get_by_label("Пароль"), "password input") + + icon_locator = page.get_by_label("Пароль").locator("xpath=../..").locator(TextInputLocators.ICON_PASSWORD_HIDING) + self.password_hidden_icon = Icon(page, + icon_locator, + "password hidden icon") self.login_button = Button(page, page.get_by_role("button"), "login button") self.alert = AlertComponent(page) # Действия: + def click_password_hidden_icon(self) -> None: + """Нажатие на иконку скрытия пароля.""" + + self.password_hidden_icon.click() + def do_login(self, username: str = None, password: str = None) -> None: """Выполняет вход в систему. @@ -103,3 +115,22 @@ class LoginPage(BasePage): self.alert.check_alert_presence("Неверная пара логин/пароль") self.alert.check_alert_absence("Неверная пара логин/пароль") + + # Проверки: + def is_password_icon_hidden(self) -> bool: + """Проверяет состояние иконки скрытия символов пароля. + Возвращает True если пароль будет скрыт, иначе False + """ + + return self.password_hidden_icon.is_password_hidden() + + def is_password_hidden(self) -> bool: + """Проверяет видимость символов пароля.""" + + input_type = self.password_input.get_input_type() + if input_type == "password": + return True + elif input_type == "text": + return False + else: + assert False, "Check password hidden error" diff --git a/tests/e2e/test_login.py b/tests/e2e/test_login.py index e5e0782..b455a55 100644 --- a/tests/e2e/test_login.py +++ b/tests/e2e/test_login.py @@ -4,6 +4,7 @@ входа и выхода из системы. """ +# import pytest from playwright.sync_api import Page from pages.main_page import MainPage from pages.login_page import LoginPage @@ -15,6 +16,7 @@ class TestLogin: 1. test_successful_login: Проверяет успешный вход в систему 2. test_unsuccessful_login: Проверяет вход с неверными учетными данными 3. test_successful_login_and_logout: Проверяет успешный вход и выход из системы + 4. test_password_visibility: Проверяет видимость символов пароля """ def test_successful_login(self, browser: Page) -> None: @@ -49,3 +51,38 @@ class TestLogin: mp = MainPage(browser) mp.do_logout() + + def test_password_visibility(self, browser: Page) -> None: + """Проверяет видимость символов пароля. + + Args: + browser: Экземпляр страницы Playwright. + """ + + lp = LoginPage(browser) + lp.open("") + + is_hidden_icon = lp.is_password_icon_hidden() + + if is_hidden_icon: + is_hidden = lp.is_password_hidden() + assert is_hidden, "Password should be hidden" + + # Нажатие на иконку скрытия пароля, пароль видим + lp.click_password_hidden_icon() + is_hidden_icon = lp.is_password_icon_hidden() + is_hidden = lp.is_password_hidden() + + assert not is_hidden_icon, "Password hidden icon should not be in hidden state" + assert not is_hidden, "Password should be visible" + else: + is_hidden = lp.is_password_hidden() + assert not is_hidden, "Password should be visible" + + # Нажатие на иконку скрытия пароля, пароль скрыт + lp.click_password_hidden_icon() + is_hidden_icon = lp.is_password_icon_hidden() + is_hidden = lp.is_password_hidden() + + assert is_hidden_icon, "Password hidden icon should be in hidden state" + assert is_hidden, "Password should be hidden" diff --git a/tests/e2e/test_user_card.py b/tests/e2e/test_user_card.py index c3f4f20..ca81888 100644 --- a/tests/e2e/test_user_card.py +++ b/tests/e2e/test_user_card.py @@ -4,7 +4,7 @@ карточки пользователя в приложении. """ -import pytest +# import pytest from playwright.sync_api import Page from pages.users_tab import UsersTab from pages.main_page import MainPage @@ -18,6 +18,11 @@ class TestUserCard: Тесты покрывают следующие сценарии: 1. test_user_card_content: Проверяет содержимое карточки пользователя + 2. test_open_close_user_settings_window: Проверяет возможность открытия и закрытия диалогового окна просмотра сессионных данных пользователя + 3. test_user_settings_window_content: Проверяет наличие и корректность элементов диалогового окна просмотра сессионных данных пользователя + 4. test_change_password_window_content: Проверяет наличие и корректность элементов окна изменения пароля текущего пользователя + 5. test_change_password_successful: Проверяет успешное изменение пароля текущего пользователя + 6. test_change_password_unsuccessful: Проверяет неуспешное изменение пароля текущего пользователя Атрибуты: browser: Фикстура для работы с браузером. @@ -167,7 +172,7 @@ class TestUserCard: is_changed, error = change_password_window.change_password(user_data["password"], user_data["new_password"]) assert is_changed, f"Unsucessful attempt to change password: {error}" - @pytest.mark.develop + # @pytest.mark.develop def test_change_password_unsuccessful(self, browser: Page, create_user: None, cleanup_user: None) -> None: