From 181dce3b92a4d17296a4f89868d16e7ff84691a1 Mon Sep 17 00:00:00 2001 From: AlexeyZh Date: Thu, 14 Nov 2024 18:07:28 +0300 Subject: [PATCH 01/10] feat: added update profile page --- tests/base/base_test.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/base/base_test.py b/tests/base/base_test.py index 8434d02..0d17f92 100644 --- a/tests/base/base_test.py +++ b/tests/base/base_test.py @@ -7,6 +7,7 @@ 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 BaseTest: @@ -19,6 +20,7 @@ class BaseTest: register_page: RegisterPage open_new_account_page: OpenNewAccountPage transfer_page: TransferPage + update_profile_page: UpdateProfilePage page: Page @pytest.fixture(autouse=True) @@ -31,3 +33,4 @@ def setup(cls, page: Page) -> None: cls.register_page = RegisterPage(page) cls.open_new_account_page = OpenNewAccountPage(page) cls.transfer_page = TransferPage(page) + cls.update_profile_page = UpdateProfilePage(page) From bd0b52918873a56ad17d66cc7d29147d02d38954 Mon Sep 17 00:00:00 2001 From: AlexeyZh Date: Thu, 14 Nov 2024 18:10:27 +0300 Subject: [PATCH 02/10] chore: added update profile url --- config/config.py | 1 + 1 file changed, 1 insertion(+) 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" From 5df6eb694b571278a24a3bce3b3fd046e1cced98 Mon Sep 17 00:00:00 2001 From: AlexeyZh Date: Fri, 15 Nov 2024 01:29:23 +0300 Subject: [PATCH 03/10] refactor: the "Factory" design pattern has been added --- tests/base/base_test.py | 91 ++++++++++++++++++++++++++++++++++------- 1 file changed, 77 insertions(+), 14 deletions(-) diff --git a/tests/base/base_test.py b/tests/base/base_test.py index 0d17f92..fc6d168 100644 --- a/tests/base/base_test.py +++ b/tests/base/base_test.py @@ -10,27 +10,90 @@ 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 - update_profile_page: UpdateProfilePage 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) - cls.update_profile_page = UpdateProfilePage(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 From 73bb29d71a570dfd90a89f8439d2f3ca33e7e450 Mon Sep 17 00:00:00 2001 From: AlexeyZh Date: Fri, 15 Nov 2024 21:37:52 +0300 Subject: [PATCH 04/10] feat: add clear_text method and update fill text, fix get_by_role_to_be_visible --- pages/base_page.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) 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: From fb5b6393117b08a626f4d8d873b114d14d00a272 Mon Sep 17 00:00:00 2001 From: AlexeyZh Date: Fri, 15 Nov 2024 21:39:41 +0300 Subject: [PATCH 05/10] refactor: moving methods from main to overview --- pages/main_page.py | 5 ----- pages/overview_page.py | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 5 deletions(-) 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 From c4626234d401f800c26d5b062585cffd81dd5680 Mon Sep 17 00:00:00 2001 From: AlexeyZh Date: Fri, 15 Nov 2024 21:40:54 +0300 Subject: [PATCH 06/10] feat: the update profile page and related methods have been added --- pages/update_profile_page.py | 46 ++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 pages/update_profile_page.py 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) From ab14b210afe9a3a46e25f2f25953ae15fd9bd085 Mon Sep 17 00:00:00 2001 From: AlexeyZh Date: Fri, 15 Nov 2024 21:41:19 +0300 Subject: [PATCH 07/10] test: added test_update_contact_info --- tests/ui/test_ui_elements.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) 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:

    • Registration button should be visible
    • Transfer funds button should be visible
    • +
    • Update contact info button should be visible
    """) 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 From 75def0b55ed1070d24c6540e366b1521e674784a Mon Sep 17 00:00:00 2001 From: AlexeyZh Date: Fri, 15 Nov 2024 21:46:15 +0300 Subject: [PATCH 08/10] test: added update profile --- tests/test_update_profile.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 tests/test_update_profile.py diff --git a/tests/test_update_profile.py b/tests/test_update_profile.py new file mode 100644 index 0000000..8827871 --- /dev/null +++ b/tests/test_update_profile.py @@ -0,0 +1,26 @@ +import time + +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 + + +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.transfer_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" + time.sleep(6) From da24b64468707e5e4061d67c40590bb3e7778f07 Mon Sep 17 00:00:00 2001 From: AlexeyZh Date: Fri, 15 Nov 2024 21:57:34 +0300 Subject: [PATCH 09/10] test: added allure description --- tests/test_update_profile.py | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/tests/test_update_profile.py b/tests/test_update_profile.py index 8827871..70fdb8e 100644 --- a/tests/test_update_profile.py +++ b/tests/test_update_profile.py @@ -1,4 +1,4 @@ -import time +import allure from config.config import BASE_URL, UPDATE_PROFILE_URL from data.user_data import UserData @@ -7,6 +7,29 @@ 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:

    +
      +
    • Update profile information with new data
    • +
    • Confirm the profile update is completed successfully
    • +
    +

    The test performs the following operations:

    +
      +
    • Navigate to the Update Profile page
    • +
    • Enter the new profile information
    • +
    • Click the Update button
    • +
    • Verify the profile update is marked as complete
    • +
    +

    Expected Results:

    +
      +
    • User can access the Update Profile page
    • +
    • Profile information is successfully updated
    • +
    • Update completion is confirmed on the page
    • +
    +""") class TestUpdateProfile(BaseTest): """Test update profile.""" @@ -17,10 +40,10 @@ def test_update_profile( ) -> None: """Test update profile.""" self.overview_page.click_update_profile() - assert self.transfer_page.expect_url( + 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" - time.sleep(6) + From c88940d201ffc514caa26e58866a67834fa1c727 Mon Sep 17 00:00:00 2001 From: AlexeyZh Date: Fri, 15 Nov 2024 21:59:25 +0300 Subject: [PATCH 10/10] fix: ruff style --- tests/test_update_profile.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_update_profile.py b/tests/test_update_profile.py index 70fdb8e..3765c81 100644 --- a/tests/test_update_profile.py +++ b/tests/test_update_profile.py @@ -46,4 +46,3 @@ def test_update_profile( 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" -