Skip to content

Commit 5b3947a

Browse files
authored
Merge pull request #6300 from emilghittasv/playwright-improve-user-related-tests
Playwright improve user profile related tests page objects, flows & tests
2 parents 4b40fed + 1136dcd commit 5b3947a

20 files changed

+947
-1032
lines changed

playwright_tests/core/basepage.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,8 @@ def _get_elements_count(self, xpath: str) -> int:
7272
"""
7373
return self._get_element_locator(xpath).count()
7474

75-
def _get_element_attribute_value(self, element: Union[str, Locator, list[Locator]],
76-
attribute: str) -> Union[str, list[str]]:
75+
def _get_element_attribute_value(self, element: Union[str, Locator, list[Locator],
76+
ElementHandle], attribute: str) -> Union[str, list[str]]:
7777
"""
7878
This helper function returns the given attribute of a given locator or web element.
7979
"""
@@ -118,6 +118,15 @@ def _get_text_content_of_all_locators(self, locator: Locator) -> list[str]:
118118
"""
119119
return locator.all_text_contents()
120120

121+
def _checkbox_interaction(self, xpath: str, check: bool):
122+
"""
123+
This helper function interacts with a checkbox element.
124+
"""
125+
if check:
126+
self._get_element_locator(xpath).check()
127+
else:
128+
self._get_element_locator(xpath).uncheck()
129+
121130
def _click(self, element: Union[str, Locator], with_wait=True, with_force=False,
122131
retries=3, delay=2000):
123132
"""
@@ -133,8 +142,8 @@ def _click(self, element: Union[str, Locator], with_wait=True, with_force=False,
133142
element.click(force=with_force)
134143
print(f"Click succeeded on attempt {attempt + 1}")
135144
break
136-
except PlaywrightTimeoutError as timeout_error:
137-
print(f"Click failed on attempt {attempt + 1}. Error: {timeout_error}")
145+
except (PlaywrightTimeoutError, Exception) as error:
146+
print(f"Click failed on attempt {attempt + 1}. Error: {error}")
138147
if attempt < retries - 1:
139148
self.page.wait_for_timeout(delay)
140149
else:

playwright_tests/core/utilities.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ def navigate_to_link(self, link: str):
161161
response = navigation_info.value
162162
self.wait_for_dom_to_load()
163163

164-
if response.status is not None:
164+
if response is not None and response.status is not None:
165165
if response.status >= 400:
166166
self.refresh_page()
167167

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

425425
def get_api_response(self, page: Page, api_url: str):
426426
return page.request.get(api_url)
427+
428+
def block_request(self, route):
429+
"""
430+
This function blocks a certain request
431+
"""
432+
route.abort()

playwright_tests/flows/user_profile_flows/edit_profile_data_flow.py

Lines changed: 81 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -19,63 +19,99 @@ def __init__(self, page: Page):
1919
self.profile_contribution_areas = MyProfileEditContributionAreasPage(page)
2020

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

25-
self._clear_input_fields()
26-
self.edit_profile_page.send_text_to_username_field(
27-
edit_test_data["valid_user_edit"]["username"]
28-
)
29-
self.edit_profile_page.send_text_to_display_name_field(
30-
edit_test_data["valid_user_edit"]["display_name"]
31-
)
32-
self.edit_profile_page.send_text_to_biography_field(
33-
edit_test_data["valid_user_edit"]["biography"]
34-
)
35-
self.edit_profile_page.send_text_to_website_field(
36-
edit_test_data["valid_user_edit"]["website"]
37-
)
38-
self.edit_profile_page.send_text_to_twitter_username_field(
39-
edit_test_data["valid_user_edit"]["twitter_username"]
40-
)
41-
self.edit_profile_page.send_text_to_community_portal_field(
42-
edit_test_data["valid_user_edit"]["community_portal_username"]
43-
)
44-
self.edit_profile_page.send_text_to_people_directory_username(
45-
edit_test_data["valid_user_edit"]["people_directory_username"]
46-
)
47-
self.edit_profile_page.send_text_to_matrix_nickname(
48-
edit_test_data["valid_user_edit"]["matrix_nickname"]
49-
)
50-
self.edit_profile_page.select_country_dropdown_option_by_value(
51-
edit_test_data["valid_user_edit"]["country_code"]
52-
)
53-
self.edit_profile_page.sent_text_to_city_field(edit_test_data["valid_user_edit"]["city"])
54-
self.edit_profile_page.select_timezone_dropdown_option_by_value(
55-
edit_test_data["valid_user_edit"]["timezone"]
56-
)
57-
self.edit_profile_page.select_preferred_language_dropdown_option_by_value(
58-
edit_test_data["valid_user_edit"]["preferred_language"]
59-
)
60-
self.edit_profile_page.select_involved_from_month_option_by_value(
61-
edit_test_data["valid_user_edit"]["involved_from_month_number"]
62-
)
63-
self.edit_profile_page.select_involved_from_year_option_by_value(
64-
edit_test_data["valid_user_edit"]["involved_from_year"]
65-
)
26+
self.clear_input_fields(info_only)
27+
28+
if not info_only:
29+
self._update_fields([
30+
("send_text_to_username_field", valid_user_edit["username"]),
31+
("send_text_to_display_name_field", valid_user_edit["display_name"]),
32+
("select_timezone_dropdown_option_by_value", valid_user_edit["timezone"]),
33+
("select_preferred_language_dropdown_option_by_value",
34+
valid_user_edit["preferred_language"])
35+
])
36+
37+
self._update_fields([
38+
("send_text_to_biography_field", valid_user_edit["biography"]),
39+
("send_text_to_website_field", valid_user_edit["website"]),
40+
("send_text_to_twitter_username_field", valid_user_edit["twitter_username"]),
41+
("send_text_to_community_portal_field", valid_user_edit["community_portal_username"]),
42+
("send_text_to_people_directory_username",
43+
valid_user_edit["people_directory_username"]),
44+
("send_text_to_matrix_nickname", valid_user_edit["matrix_nickname"]),
45+
("select_country_dropdown_option_by_value", valid_user_edit["country_code"]),
46+
("sent_text_to_city_field", valid_user_edit["city"]),
47+
("select_involved_from_month_option_by_value",
48+
valid_user_edit["involved_from_month_number"]),
49+
("select_involved_from_year_option_by_value", valid_user_edit["involved_from_year"])
50+
])
51+
52+
if submit_change:
53+
self.edit_profile_page.click_update_my_profile_button()
54+
55+
return {
56+
"username": valid_user_edit["username"],
57+
"display_name": valid_user_edit["display_name"],
58+
"biography": valid_user_edit["biography"],
59+
"website": valid_user_edit["website"],
60+
"twitter": valid_user_edit["twitter_username"],
61+
"community_portal": valid_user_edit["community_portal_username"],
62+
"people_directory": valid_user_edit["people_directory_username"],
63+
"matrix_nickname": valid_user_edit["matrix_nickname"],
64+
"country": valid_user_edit["country_value"],
65+
"city": valid_user_edit["city"],
66+
"timezone": valid_user_edit["timezone"],
67+
"preferred_language": valid_user_edit["preferred_language"],
68+
"involved_from_month": valid_user_edit["involved_from_month_value"],
69+
"involved_from_year": valid_user_edit["involved_from_year"]
70+
}
71+
72+
def _update_fields(self, fields: list[tuple[str, str]]):
73+
"""
74+
Updates the fields on the edit profile page.
75+
76+
Args:
77+
fields (list[tuple[str, str]]): A list of tuples where each tuple contains the method
78+
name and the value to be set.
79+
"""
80+
for method_name, value in fields:
81+
getattr(self.edit_profile_page, method_name)(value)
6682

6783
# Clear all profile edit input fields flow.
68-
def _clear_input_fields(self):
69-
self.edit_profile_page.clear_all_input_fields()
70-
self.edit_profile_page.clear_username_field()
84+
def clear_input_fields(self, only_profile_info=False, submit_change=False):
85+
"""
86+
Clears all profile edit input fields.
87+
88+
Args:
89+
only_profile_info (bool): If True, all profile info fields are cleared except username
90+
and display name.
91+
submit_change (bool): If True, submits the changes after clearing the fields.
92+
"""
93+
self.edit_profile_page.clear_all_input_fields(only_profile_info)
7194
self.edit_profile_page.clear_biography_textarea_field()
95+
if submit_change:
96+
self.edit_profile_page.click_update_my_profile_button()
7297

7398
def check_all_user_settings(self):
99+
"""
100+
Navigates to the settings profile option, checks all settings checkboxes,
101+
and updates the settings.
102+
"""
74103
self.top_navbar.click_on_settings_profile_option()
75104
self.edit_settings_page.click_on_all_settings_checkboxes()
76105
self.edit_settings_page.click_on_update_button()
77106

78107
def check_all_profile_contribution_areas(self, checked: bool):
108+
"""
109+
Navigates to the contribution areas section and checks or unchecks all contribution areas.
110+
111+
Args:
112+
checked (bool): If True, checks all contribution areas. If False, unchecks all
113+
contribution areas.
114+
"""
79115
self.top_navbar.click_on_settings_profile_option()
80116
self.profile_navbar.click_on_edit_contribution_areas_option()
81117

playwright_tests/pages/user_pages/my_profile_answers_page.py

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,31 @@
44

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

1012
def __init__(self, page: Page):
1113
super().__init__(page)
1214

1315
# My Profile Answers actions.
14-
def _get_page_header(self) -> str:
15-
return super()._get_text_of_element(self.__my_answers_page_header)
16+
def get_page_header(self) -> str:
17+
"""Get the header of the My Profile Answers page."""
18+
return self._get_text_of_element(self.MY_PROFILE_ANSWERS_LOCATORS["my_answers_page_"
19+
"header"])
1620

17-
def _get_text_of_question_subjects(self) -> list[str]:
18-
return super()._get_text_of_elements(self.__my_answers_question_subject_links)
21+
def get_text_of_question_subjects(self) -> list[str]:
22+
"""Get the text of the question subjects."""
23+
return self._get_text_of_elements(self.MY_PROFILE_ANSWERS_LOCATORS["my_answers_question_"
24+
"subject_links"])
1925

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

23-
def _get_my_answer_text(self, answer_id: str) -> str:
24-
return super()._get_text_of_element(f"//article[@id='profile']//"
25-
f"a[contains(@href, '{answer_id}')]/"
26-
f"following-sibling::blockquote")
30+
def get_my_answer_text(self, answer_id: str) -> str:
31+
"""Get the text of a specific answer."""
32+
return self._get_text_of_element(f"//article[@id='profile']//"
33+
f"a[contains(@href, '{answer_id}')]/"
34+
f"following-sibling::blockquote")

playwright_tests/pages/user_pages/my_profile_documents_page.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,23 @@
44

55
class MyProfileDocumentsPage(BasePage):
66
# My profile documents locators.
7-
__documents_link_list = "//main//a"
7+
MY_PROFILE_DOCUMENTS_LOCATORS = {
8+
"documents_link_list": "//main//a"
9+
}
810

911
def __init__(self, page: Page):
1012
super().__init__(page)
1113

1214
# My profile documents actions.
13-
def _click_on_a_particular_document(self, document_name: str):
14-
super()._click(f"//main//a[contains(text(),'{document_name}')]")
15+
def click_on_a_particular_document(self, document_name: str):
16+
"""Click on a particular document"""
17+
self._click(f"//main//a[contains(text(),'{document_name}')]")
1518

16-
def _get_text_of_document_links(self) -> list[str]:
17-
return super()._get_text_of_elements(self.__documents_link_list)
19+
def get_text_of_document_links(self) -> list[str]:
20+
"""Get text of all document links"""
21+
return self._get_text_of_elements(self.MY_PROFILE_DOCUMENTS_LOCATORS["documents_link_"
22+
"list"])
1823

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

0 commit comments

Comments
 (0)