diff --git a/config/config.py b/config/config.py index 229e37c..3c1b547 100644 --- a/config/config.py +++ b/config/config.py @@ -4,3 +4,4 @@ REGISTER_URL = "register.htm" OPEN_NEW_ACCOUNT_URL = "openaccount.htm" TRANSFER_FUNDS_URL = "transfer.htm" +UPDATE_PROFILE_URL = "updateprofile.htm" diff --git a/pages/base_page.py b/pages/base_page.py index 29f988f..a9f9ce9 100644 --- a/pages/base_page.py +++ b/pages/base_page.py @@ -2,6 +2,7 @@ import allure from playwright.sync_api import Locator, Page, expect +from playwright.sync_api import TimeoutError as PlaywrightTimeoutError from config.config import BASE_URL @@ -51,6 +52,10 @@ def contains_text(self, selector: str, text: str) -> bool: else: return True + def clear_text(self, selector: str) -> None: + """Clear text in element.""" + self.find_element(selector).clear() + def fill_text(self, selector: str, text: str) -> None: """Fill text in element. @@ -58,7 +63,10 @@ def fill_text(self, selector: str, text: str) -> None: selector (str): Element selector text (str): Text to fill """ - self.find_element(selector).fill(text) + element: Locator = self.find_element(selector) + self.page.on("dialog", lambda dialog: dialog.accept()) + element.clear() + element.fill(text) @allure.step("Click element by role {role} with name {name}") def click_by_role(self, role: str, name: str) -> None: @@ -73,11 +81,10 @@ def click_by_role(self, role: str, name: str) -> None: def get_by_role_to_be_visible(self, role: str, name: str) -> bool: """Checks that the element is visible.""" try: - expect(self.page.get_by_role(role, name=name)) # type: ignore - except AssertionError: + element: Locator = self.page.get_by_role(role, name=name) # type: ignore + return element.is_visible() + except PlaywrightTimeoutError: return False - else: - return True @allure.step("Select option {value} in dropdown {selector}") def select_option(self, selector: str, value: str) -> None: diff --git a/pages/main_page.py b/pages/main_page.py index c05d4aa..0b85e49 100644 --- a/pages/main_page.py +++ b/pages/main_page.py @@ -64,8 +64,3 @@ def login(self, username: str, password: str) -> None: def is_register_button_visible(self) -> bool: """Checks that the register button is visible.""" return self.get_by_role_to_be_visible("link", "Register") - - @property - def is_transfer_button_visible(self) -> bool: - """Checks that the transfer funds button is visible.""" - return self.get_by_role_to_be_visible("link", "Transfer Funds") diff --git a/pages/overview_page.py b/pages/overview_page.py index 0704b6f..1eee1b5 100644 --- a/pages/overview_page.py +++ b/pages/overview_page.py @@ -25,6 +25,16 @@ def is_logged_in(self) -> bool: self.ACCOUNT_OVERVIEW_HEADER, self.ACCOUNT_OVERVIEW_HEADER_TEXT ) + @property + def is_transfer_button_visible(self) -> bool: + """Checks that the transfer funds button is visible.""" + return self.get_by_role_to_be_visible("link", "Transfer Funds") + + @property + def is_update_contact_info(self) -> bool: + """Checks that the update contact info button is visible.""" + return self.get_by_role_to_be_visible("link", "Update Contact Info") + def click_open_new_account(self) -> None: """Click on Open new account button.""" self.click_by_role("link", "Open New Account") # type: ignore @@ -32,3 +42,7 @@ def click_open_new_account(self) -> None: def click_transfer_funds(self) -> None: """Click transfer funds button.""" self.click_by_role("link", self.TRANSFER_FUNDS_BUTTON) # type: ignore + + def click_update_profile(self) -> None: + """Click update profile button.""" + self.click_by_role("link", "Update Contact Info") # type: ignore diff --git a/pages/update_profile_page.py b/pages/update_profile_page.py new file mode 100644 index 0000000..597f07a --- /dev/null +++ b/pages/update_profile_page.py @@ -0,0 +1,46 @@ +import allure +from playwright.sync_api import Page + +from config.config import UPDATE_PROFILE_URL +from data.user_data import UserData +from pages.base_page import BasePage + + +class UpdateProfilePage(BasePage): + """The update profile page.""" + + def __init__(self, page: Page) -> None: + """The update profile page.""" + super().__init__(page, url=UPDATE_PROFILE_URL) + + FIRST_NAME_INPUT = '[id="customer.firstName"]' + LAST_NAME_INPUT = '[id="customer.lastName"]' + STREET_INPUT = '[id="customer.address.street"]' + CITY_INPUT = '[id="customer.address.city"]' + STATE_INPUT = '[id="customer.address.state"]' + ZIP_CODE_INPUT = '[id="customer.address.zipCode"]' + PHONE_INPUT = '[id="customer.phoneNumber"]' + UPDATE_BUTTON = "Update Profile" + UPDATE_MESSAGE_LOCATOR = "#updateProfileResult" + UPDATE_MESSAGE = "Profile Updated" + + @allure.step("Fill registration form") + def update_profile(self, user_data: UserData) -> None: + """Fill all fields in registration form.""" + self.page.on("dialog", lambda dialog: dialog.accept()) + self.fill_text(self.FIRST_NAME_INPUT, user_data.first_name) + self.fill_text(self.LAST_NAME_INPUT, user_data.last_name) + self.fill_text(self.STREET_INPUT, user_data.street) + self.fill_text(self.CITY_INPUT, user_data.city) + self.fill_text(self.STATE_INPUT, user_data.state) + self.fill_text(self.ZIP_CODE_INPUT, user_data.zip_code) + self.fill_text(self.PHONE_INPUT, user_data.phone) + + def click_update_button(self) -> None: + """Click update button.""" + self.click_by_role("button", self.UPDATE_BUTTON) # type: ignore + + @property + def is_updated(self) -> bool: + """Check if profile is updated.""" + return self.contains_text(self.UPDATE_MESSAGE_LOCATOR, self.UPDATE_MESSAGE) diff --git a/tests/base/base_test.py b/tests/base/base_test.py index 8434d02..fc6d168 100644 --- a/tests/base/base_test.py +++ b/tests/base/base_test.py @@ -7,27 +7,93 @@ from pages.overview_page import OverviewPage from pages.register_page import RegisterPage from pages.transfer_page import TransferPage +from pages.update_profile_page import UpdateProfilePage + + +class PageFactory: + """Factory for creating pages.""" + + def __init__(self, page: Page) -> None: + """Constructor.""" + self.page: Page = page + + def create_main_page(self) -> MainPage: + """Create main page.""" + return MainPage(self.page) + + def create_overview_page(self) -> OverviewPage: + """Create overview page.""" + return OverviewPage(self.page) + + def create_register_page(self) -> RegisterPage: + """Create register page.""" + return RegisterPage(self.page) + + def create_open_new_account_page(self) -> OpenNewAccountPage: + """Create open new account page.""" + return OpenNewAccountPage(self.page) + + def create_transfer_page(self) -> TransferPage: + """Create transfer page.""" + return TransferPage(self.page) + + def create_update_profile_page(self) -> UpdateProfilePage: + """Create update profile page.""" + return UpdateProfilePage(self.page) class BaseTest: """The base class for all tests.""" data: Data - - main_page: MainPage - overview_page: OverviewPage - register_page: RegisterPage - open_new_account_page: OpenNewAccountPage - transfer_page: TransferPage page: Page @pytest.fixture(autouse=True) def setup(cls, page: Page) -> None: - """The setup function.""" + """Function that is called before each test.""" cls.page = page cls.data = Data() - cls.main_page = MainPage(page) - cls.overview_page = OverviewPage(page) - cls.register_page = RegisterPage(page) - cls.open_new_account_page = OpenNewAccountPage(page) - cls.transfer_page = TransferPage(page) + + factory = PageFactory(page) + + # Initialize pages using the factory + cls._main_page: MainPage = factory.create_main_page() + cls._overview_page: OverviewPage = factory.create_overview_page() + cls._register_page: RegisterPage = factory.create_register_page() + cls._open_new_account_page: OpenNewAccountPage = ( + factory.create_open_new_account_page() + ) + cls._transfer_page: TransferPage = factory.create_transfer_page() + cls._update_profile_page: UpdateProfilePage = ( + factory.create_update_profile_page() + ) + + @property + def main_page(self) -> MainPage: + """Get the main page instance.""" + return self._main_page + + @property + def overview_page(self) -> OverviewPage: + """Get the overview page instance.""" + return self._overview_page + + @property + def register_page(self) -> RegisterPage: + """Get the register page instance.""" + return self._register_page + + @property + def open_new_account_page(self) -> OpenNewAccountPage: + """Get the open new account page instance.""" + return self._open_new_account_page + + @property + def transfer_page(self) -> TransferPage: + """Get the transfer page instance.""" + return self._transfer_page + + @property + def update_profile_page(self) -> UpdateProfilePage: + """Get the update profile page instance.""" + return self._update_profile_page diff --git a/tests/test_update_profile.py b/tests/test_update_profile.py new file mode 100644 index 0000000..3765c81 --- /dev/null +++ b/tests/test_update_profile.py @@ -0,0 +1,48 @@ +import allure + +from config.config import BASE_URL, UPDATE_PROFILE_URL +from data.user_data import UserData +from pages.main_page import MainPage +from pages.overview_page import OverviewPage +from tests.base.base_test import BaseTest + + +@allure.epic("Banking Application") +@allure.feature("Update Profile") +@allure.description_html(""" +

Testing Update Profile Functionality

+

This test verifies the ability to update a user's profile information:

+ +

The test performs the following operations:

+ +

Expected Results:

+ +""") +class TestUpdateProfile(BaseTest): + """Test update profile.""" + + def test_update_profile( + self, + login: tuple[MainPage, OverviewPage], # noqa: ARG002 + random_user_data: UserData, + ) -> None: + """Test update profile.""" + self.overview_page.click_update_profile() + assert self.update_profile_page.expect_url( + f"{BASE_URL}{UPDATE_PROFILE_URL}" + ), "URL does not match expected value" + self.update_profile_page.update_profile(random_user_data) # type: ignore + self.update_profile_page.click_update_button() + assert self.update_profile_page.is_updated, "Profile is not updated" diff --git a/tests/ui/test_ui_elements.py b/tests/ui/test_ui_elements.py index 6715501..99e1cdd 100644 --- a/tests/ui/test_ui_elements.py +++ b/tests/ui/test_ui_elements.py @@ -1,5 +1,7 @@ import allure +from pages.main_page import MainPage +from pages.overview_page import OverviewPage from tests.base.base_test import BaseTest @@ -12,11 +14,13 @@
  • Navigation to main page
  • Checking registration button presence
  • Checking transfer funds button presence
  • +
  • Checking update contact info button presence
  • Expected Results:

    """) class TestUIElements(BaseTest): @@ -31,7 +35,14 @@ def test_register_button(self) -> None: @allure.story("Transfer Funds Button Visibility") @allure.severity(severity_level="MINOR") - def test_transfer_funds(self) -> None: + def test_transfer_funds(self, login: tuple[MainPage, OverviewPage]) -> None: # noqa: ARG002 """The test checks that the register button is visible.""" - self.main_page.navigate() - assert self.main_page.is_transfer_button_visible + self.overview_page.navigate() + assert self.overview_page.is_transfer_button_visible + + @allure.story("Update Contact Info Button Visibility") + @allure.severity(severity_level="MINOR") + def test_update_contact_info(self, login: tuple[MainPage, OverviewPage]) -> None: # noqa: ARG002 + """The test checks that the update contact info button is visible.""" + self.overview_page.navigate() + assert self.overview_page.is_update_contact_info