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(""" +
This test verifies the ability to update a user's profile information:
+The test performs the following operations:
+Expected Results:
+Expected Results: