Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Playwright improve user profile related tests page objects, flows & tests #6300

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions playwright_tests/core/basepage.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ def _get_elements_count(self, xpath: str) -> int:
"""
return self._get_element_locator(xpath).count()

def _get_element_attribute_value(self, element: Union[str, Locator, list[Locator]],
attribute: str) -> Union[str, list[str]]:
def _get_element_attribute_value(self, element: Union[str, Locator, list[Locator],
ElementHandle], attribute: str) -> Union[str, list[str]]:
"""
This helper function returns the given attribute of a given locator or web element.
"""
Expand Down Expand Up @@ -118,6 +118,15 @@ def _get_text_content_of_all_locators(self, locator: Locator) -> list[str]:
"""
return locator.all_text_contents()

def _checkbox_interaction(self, xpath: str, check: bool):
"""
This helper function interacts with a checkbox element.
"""
if check:
self._get_element_locator(xpath).check()
else:
self._get_element_locator(xpath).uncheck()

def _click(self, element: Union[str, Locator], with_wait=True, with_force=False,
retries=3, delay=2000):
"""
Expand All @@ -133,8 +142,8 @@ def _click(self, element: Union[str, Locator], with_wait=True, with_force=False,
element.click(force=with_force)
print(f"Click succeeded on attempt {attempt + 1}")
break
except PlaywrightTimeoutError as timeout_error:
print(f"Click failed on attempt {attempt + 1}. Error: {timeout_error}")
except (PlaywrightTimeoutError, Exception) as error:
print(f"Click failed on attempt {attempt + 1}. Error: {error}")
if attempt < retries - 1:
self.page.wait_for_timeout(delay)
else:
Expand Down
8 changes: 7 additions & 1 deletion playwright_tests/core/utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ def navigate_to_link(self, link: str):
response = navigation_info.value
self.wait_for_dom_to_load()

if response.status is not None:
if response is not None and response.status is not None:
if response.status >= 400:
self.refresh_page()

Expand Down Expand Up @@ -424,3 +424,9 @@ def _exact_phrase_check(self, search_result: str, search_term: str) -> bool:

def get_api_response(self, page: Page, api_url: str):
return page.request.get(api_url)

def block_request(self, route):
"""
This function blocks a certain request
"""
route.abort()
126 changes: 81 additions & 45 deletions playwright_tests/flows/user_profile_flows/edit_profile_data_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,63 +19,99 @@ def __init__(self, page: Page):
self.profile_contribution_areas = MyProfileEditContributionAreasPage(page)

# Editing a profile with data flow.
def edit_profile_with_test_data(self):
def edit_profile_with_test_data(self, info_only=False, submit_change=False) -> dict[str, str]:
edit_test_data = self.utilities.profile_edit_test_data
valid_user_edit = edit_test_data["valid_user_edit"]

self._clear_input_fields()
self.edit_profile_page.send_text_to_username_field(
edit_test_data["valid_user_edit"]["username"]
)
self.edit_profile_page.send_text_to_display_name_field(
edit_test_data["valid_user_edit"]["display_name"]
)
self.edit_profile_page.send_text_to_biography_field(
edit_test_data["valid_user_edit"]["biography"]
)
self.edit_profile_page.send_text_to_website_field(
edit_test_data["valid_user_edit"]["website"]
)
self.edit_profile_page.send_text_to_twitter_username_field(
edit_test_data["valid_user_edit"]["twitter_username"]
)
self.edit_profile_page.send_text_to_community_portal_field(
edit_test_data["valid_user_edit"]["community_portal_username"]
)
self.edit_profile_page.send_text_to_people_directory_username(
edit_test_data["valid_user_edit"]["people_directory_username"]
)
self.edit_profile_page.send_text_to_matrix_nickname(
edit_test_data["valid_user_edit"]["matrix_nickname"]
)
self.edit_profile_page.select_country_dropdown_option_by_value(
edit_test_data["valid_user_edit"]["country_code"]
)
self.edit_profile_page.sent_text_to_city_field(edit_test_data["valid_user_edit"]["city"])
self.edit_profile_page.select_timezone_dropdown_option_by_value(
edit_test_data["valid_user_edit"]["timezone"]
)
self.edit_profile_page.select_preferred_language_dropdown_option_by_value(
edit_test_data["valid_user_edit"]["preferred_language"]
)
self.edit_profile_page.select_involved_from_month_option_by_value(
edit_test_data["valid_user_edit"]["involved_from_month_number"]
)
self.edit_profile_page.select_involved_from_year_option_by_value(
edit_test_data["valid_user_edit"]["involved_from_year"]
)
self.clear_input_fields(info_only)

if not info_only:
self._update_fields([
("send_text_to_username_field", valid_user_edit["username"]),
("send_text_to_display_name_field", valid_user_edit["display_name"]),
("select_timezone_dropdown_option_by_value", valid_user_edit["timezone"]),
("select_preferred_language_dropdown_option_by_value",
valid_user_edit["preferred_language"])
])

self._update_fields([
("send_text_to_biography_field", valid_user_edit["biography"]),
("send_text_to_website_field", valid_user_edit["website"]),
("send_text_to_twitter_username_field", valid_user_edit["twitter_username"]),
("send_text_to_community_portal_field", valid_user_edit["community_portal_username"]),
("send_text_to_people_directory_username",
valid_user_edit["people_directory_username"]),
("send_text_to_matrix_nickname", valid_user_edit["matrix_nickname"]),
("select_country_dropdown_option_by_value", valid_user_edit["country_code"]),
("sent_text_to_city_field", valid_user_edit["city"]),
("select_involved_from_month_option_by_value",
valid_user_edit["involved_from_month_number"]),
("select_involved_from_year_option_by_value", valid_user_edit["involved_from_year"])
])

if submit_change:
self.edit_profile_page.click_update_my_profile_button()

return {
"username": valid_user_edit["username"],
"display_name": valid_user_edit["display_name"],
"biography": valid_user_edit["biography"],
"website": valid_user_edit["website"],
"twitter": valid_user_edit["twitter_username"],
"community_portal": valid_user_edit["community_portal_username"],
"people_directory": valid_user_edit["people_directory_username"],
"matrix_nickname": valid_user_edit["matrix_nickname"],
"country": valid_user_edit["country_value"],
"city": valid_user_edit["city"],
"timezone": valid_user_edit["timezone"],
"preferred_language": valid_user_edit["preferred_language"],
"involved_from_month": valid_user_edit["involved_from_month_value"],
"involved_from_year": valid_user_edit["involved_from_year"]
}

def _update_fields(self, fields: list[tuple[str, str]]):
"""
Updates the fields on the edit profile page.

Args:
fields (list[tuple[str, str]]): A list of tuples where each tuple contains the method
name and the value to be set.
"""
for method_name, value in fields:
getattr(self.edit_profile_page, method_name)(value)

# Clear all profile edit input fields flow.
def _clear_input_fields(self):
self.edit_profile_page.clear_all_input_fields()
self.edit_profile_page.clear_username_field()
def clear_input_fields(self, only_profile_info=False, submit_change=False):
"""
Clears all profile edit input fields.

Args:
only_profile_info (bool): If True, all profile info fields are cleared except username
and display name.
submit_change (bool): If True, submits the changes after clearing the fields.
"""
self.edit_profile_page.clear_all_input_fields(only_profile_info)
self.edit_profile_page.clear_biography_textarea_field()
if submit_change:
self.edit_profile_page.click_update_my_profile_button()

def check_all_user_settings(self):
"""
Navigates to the settings profile option, checks all settings checkboxes,
and updates the settings.
"""
self.top_navbar.click_on_settings_profile_option()
self.edit_settings_page.click_on_all_settings_checkboxes()
self.edit_settings_page.click_on_update_button()

def check_all_profile_contribution_areas(self, checked: bool):
"""
Navigates to the contribution areas section and checks or unchecks all contribution areas.

Args:
checked (bool): If True, checks all contribution areas. If False, unchecks all
contribution areas.
"""
self.top_navbar.click_on_settings_profile_option()
self.profile_navbar.click_on_edit_contribution_areas_option()

Expand Down
32 changes: 20 additions & 12 deletions playwright_tests/pages/user_pages/my_profile_answers_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,31 @@

class MyProfileAnswersPage(BasePage):
# My Profile Answers.
__my_answers_page_header = "//h2[@class='sumo-page-subheading']"
__my_answers_question_subject_links = "//article[@id='profile']//li/a"
MY_PROFILE_ANSWERS_LOCATORS = {
"my_answers_page_header": "//h2[@class='sumo-page-subheading']",
"my_answers_question_subject_links": "//article[@id='profile']//li/a"
}

def __init__(self, page: Page):
super().__init__(page)

# My Profile Answers actions.
def _get_page_header(self) -> str:
return super()._get_text_of_element(self.__my_answers_page_header)
def get_page_header(self) -> str:
"""Get the header of the My Profile Answers page."""
return self._get_text_of_element(self.MY_PROFILE_ANSWERS_LOCATORS["my_answers_page_"
"header"])

def _get_text_of_question_subjects(self) -> list[str]:
return super()._get_text_of_elements(self.__my_answers_question_subject_links)
def get_text_of_question_subjects(self) -> list[str]:
"""Get the text of the question subjects."""
return self._get_text_of_elements(self.MY_PROFILE_ANSWERS_LOCATORS["my_answers_question_"
"subject_links"])

def _click_on_specific_answer(self, answer_id: str):
super()._click(f"//article[@id='profile']//a[contains(@href, '{answer_id}')]")
def click_on_specific_answer(self, answer_id: str):
"""Click on a specific answer"""
self._click(f"//article[@id='profile']//a[contains(@href, '{answer_id}')]")

def _get_my_answer_text(self, answer_id: str) -> str:
return super()._get_text_of_element(f"//article[@id='profile']//"
f"a[contains(@href, '{answer_id}')]/"
f"following-sibling::blockquote")
def get_my_answer_text(self, answer_id: str) -> str:
"""Get the text of a specific answer."""
return self._get_text_of_element(f"//article[@id='profile']//"
f"a[contains(@href, '{answer_id}')]/"
f"following-sibling::blockquote")
20 changes: 13 additions & 7 deletions playwright_tests/pages/user_pages/my_profile_documents_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,23 @@

class MyProfileDocumentsPage(BasePage):
# My profile documents locators.
__documents_link_list = "//main//a"
MY_PROFILE_DOCUMENTS_LOCATORS = {
"documents_link_list": "//main//a"
}

def __init__(self, page: Page):
super().__init__(page)

# My profile documents actions.
def _click_on_a_particular_document(self, document_name: str):
super()._click(f"//main//a[contains(text(),'{document_name}')]")
def click_on_a_particular_document(self, document_name: str):
"""Click on a particular document"""
self._click(f"//main//a[contains(text(),'{document_name}')]")

def _get_text_of_document_links(self) -> list[str]:
return super()._get_text_of_elements(self.__documents_link_list)
def get_text_of_document_links(self) -> list[str]:
"""Get text of all document links"""
return self._get_text_of_elements(self.MY_PROFILE_DOCUMENTS_LOCATORS["documents_link_"
"list"])

def _get_a_particular_document_locator(self, document_name: str) -> Locator:
return super()._get_element_locator(f"//main//a[contains(text(),'{document_name}')]")
def get_a_particular_document_locator(self, document_name: str) -> Locator:
"""Get a particular document locator"""
return self._get_element_locator(f"//main//a[contains(text(),'{document_name}')]")
Loading