Skip to content

Commit

Permalink
Merge pull request #6359 from emilghittasv/playwright-add-more-group-…
Browse files Browse the repository at this point in the history
…tests

Playwright handle wait timeouts and add new AAQ topics for AAQ test coverage
  • Loading branch information
emilghittasv authored Nov 20, 2024
2 parents c760798 + c6316d9 commit 247ccc3
Show file tree
Hide file tree
Showing 17 changed files with 264 additions and 162 deletions.
50 changes: 34 additions & 16 deletions playwright_tests/core/basepage.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ def _get_element_locator(self, xpath: str, with_wait=True) -> Locator:
This helper function returns the element locator from a given xpath.
"""
if with_wait:
self._wait_for_dom_load_to_finish()
self.wait_for_dom_to_load()
return self.page.locator(xpath)

def _get_elements_locators(self, xpath: str) -> list[Locator]:
"""
This helper function returns a list of element locators from a given xpath.
"""
self._wait_for_dom_load_to_finish()
self.wait_for_dom_to_load()
return self.page.locator(xpath).all()

def _get_current_page_url(self) -> str:
Expand Down Expand Up @@ -80,7 +80,7 @@ def _get_element_attribute_value(self, element: Union[str, Locator, list[Locator
if isinstance(element, str):
return self._get_element_locator(element).get_attribute(attribute)
elif isinstance(element, list):
self._wait_for_dom_load_to_finish()
self.wait_for_dom_to_load()
values = []
for element in element:
values.append(element.get_attribute(attribute))
Expand Down Expand Up @@ -127,7 +127,7 @@ def _checkbox_interaction(self, element: [str, ElementHandle], check: bool, retr
element (Union[str, ElementHandle]): The element locator to interact with.
check (bool): Whether to check or uncheck the checkbox.
"""
self.page.wait_for_load_state("networkidle")
self.wait_for_networkidle()
for attempt in range(retries):
try:
locator = self._get_element_locator(element) if isinstance(
Expand Down Expand Up @@ -155,7 +155,7 @@ def _click(self, element: Union[str, Locator, ElementHandle], expected_locator=N
expected_url (str): The expected URL to wait for after the click.
with_force (bool): Whether to force the click.
"""
self.page.wait_for_load_state("networkidle")
self.wait_for_networkidle()
for attempt in range(retries):
try:
element_locator = self._get_element_locator(element) if isinstance(
Expand All @@ -178,14 +178,14 @@ def _click_on_an_element_by_index(self, xpath: str, index: int):
"""
This helper function clicks on a given element locator based on a given index.
"""
self.page.wait_for_load_state("networkidle")
self.wait_for_networkidle()
self._get_element_locator(xpath).nth(index).click()

def _click_on_first_item(self, xpath: str):
"""
This helper function clicks on the first item from a given web element locator list.
"""
self.page.wait_for_load_state("networkidle")
self.wait_for_networkidle()
self._get_element_locator(xpath).first.click()

def _fill(self, xpath: str, text: str):
Expand Down Expand Up @@ -267,15 +267,6 @@ def _is_checkbox_checked(self, xpath: str) -> bool:
"""
return self._get_element_locator(xpath).is_checked()

def _wait_for_dom_load_to_finish(self):
"""
This helper function performs two waits:
1. Waits for the dom load to finish.
2. Waits for the load event to be fired when the whole page, including resources has loaded
"""
self.page.wait_for_load_state("domcontentloaded")
self.page.wait_for_load_state("load")

def _wait_for_selector(self, xpath: str, timeout=3500):
"""
This helper function waits for a given element locator to be visible based on a given
Expand All @@ -295,3 +286,30 @@ def _move_mouse_to_location(self, x: int, y: int):
y (int): The y-coordinate.
"""
self.page.mouse.move(x, y)

def wait_for_page_to_load(self):
"""
This helper function awaits for the load event to be fired.
"""
try:
self.page.wait_for_load_state("load")
except PlaywrightTimeoutError:
print("Load event was not fired. Continuing...")

def wait_for_dom_to_load(self):
"""
This helper function awaits for the DOMContentLoaded event to be fired.
"""
try:
self.page.wait_for_load_state("domcontentloaded")
except PlaywrightTimeoutError:
print("DOMContentLoaded event was not fired. Continuing...")

def wait_for_networkidle(self):
"""
This helper function waits until there are no network connections for at least 500ms.
"""
try:
self.page.wait_for_load_state("networkidle")
except PlaywrightTimeoutError:
print("Network idle state was not reached. Continuing...")
45 changes: 40 additions & 5 deletions playwright_tests/core/utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ def navigate_to_homepage(self):
"""
This helper function navigates directly to the SUMO hompage.
"""
self.page.goto(HomepageMessages.STAGE_HOMEPAGE_URL)
self.navigate_to_link(HomepageMessages.STAGE_HOMEPAGE_URL)

def navigate_to_link(self, link: str):
"""
Expand Down Expand Up @@ -262,19 +262,28 @@ def wait_for_page_to_load(self):
"""
This helper function awaits for the load event to be fired.
"""
self.page.wait_for_load_state("load")
try:
self.page.wait_for_load_state("load")
except PlaywrightTimeoutError:
print("Load event was not fired. Continuing...")

def wait_for_dom_to_load(self):
"""
This helper function awaits for the DOMContentLoaded event to be fired.
"""
self.page.wait_for_load_state("domcontentloaded")
try:
self.page.wait_for_load_state("domcontentloaded")
except PlaywrightTimeoutError:
print("DOMContentLoaded event was not fired. Continuing...")

def wait_for_networkidle(self):
"""
This helper function waits until there are no network connections for at least 500ms.
"""
self.page.wait_for_load_state("networkidle")
try:
self.page.wait_for_load_state("networkidle")
except PlaywrightTimeoutError:
print("Network idle state was not reached. Continuing...")

def store_session_cookies(self, session_file_name: str):
"""
Expand Down Expand Up @@ -340,7 +349,10 @@ def refresh_page(self):
"""
This helper function performs a page reload.
"""
self.page.reload(wait_until="networkidle")
try:
self.page.reload(wait_until="networkidle")
except PlaywrightTimeoutError:
print("Network idle state was not reached. Continuing...")

def get_user_agent(self) -> str:
"""
Expand Down Expand Up @@ -575,6 +587,22 @@ def get_api_response(self, page: Page, api_url: str):
"""
return page.request.get(api_url)

def post_api_request(self, page: Page, api_url: str, data: dict):
"""Post the API request
Args:
page (Page): The page object
api_url (str): The API URL
data (dict): The data to be posted
"""

# It seems that playwright doesn't send the correct origin header by default.
headers = {
'origin': HomepageMessages.STAGE_HOMEPAGE_URL
}

return page.request.post(api_url, form=data, headers=headers)

def block_request(self, route):
"""
This function blocks a certain request
Expand All @@ -601,3 +629,10 @@ def re_call_function_on_error(self, func, *args, **kwargs):
if attempt < 2:
continue
break

def get_csrfmiddlewaretoken(self) -> str:
"""
This helper function fetches the csrfmiddlewaretoken from the page.
"""
return self.page.evaluate("document.querySelector('input[name=csrfmiddlewaretoken]')"
".value")
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
from playwright.sync_api import Page
from typing import Any

from slugify import slugify

from playwright_tests.core.utilities import Utilities
from playwright_tests.flows.explore_articles_flows.article_flows.add_kb_media_flow import \
AddKbMediaFlow
Expand Down Expand Up @@ -48,7 +51,7 @@ def submit_simple_kb_article(self,
approve_first_revision=False,
ready_for_localization=False
) -> dict[str, Any]:
self.page.goto(KBArticlePageMessages.CREATE_NEW_KB_ARTICLE_STAGE_URL)
self.utilities.navigate_to_link(KBArticlePageMessages.CREATE_NEW_KB_ARTICLE_STAGE_URL)

kb_article_test_data = self.utilities.kb_article_test_data

Expand Down Expand Up @@ -314,3 +317,56 @@ def submit_new_kb_revision(self,
"revision_time": revision_time,
"changes_description": self.utilities.kb_article_test_data['changes_description']
}

def kb_article_creation_via_api(self, page: Page, approve_revision=False,
is_template=False) -> dict[str, Any]:
kb_article_test_data = self.utilities.kb_article_test_data
self.utilities.navigate_to_link(KBArticlePageMessages.CREATE_NEW_KB_ARTICLE_STAGE_URL)
if is_template:
kb_title = (kb_article_test_data["kb_template_title"] + self.utilities.
generate_random_number(0, 5000))
category = "60"
else:
kb_title = (kb_article_test_data["kb_article_title"] + self.utilities.
generate_random_number(0, 5000))
category = "10"

slug = slugify(kb_title)

form_data = {
"csrfmiddlewaretoken": self.utilities.get_csrfmiddlewaretoken(),
"title": kb_title,
"slug": slug,
"category": category,
"is_localizable": "on",
"products": "1",
"topics": "383",
"allow_discussion": "on",
"keywords": kb_article_test_data["keywords"],
"summary": kb_article_test_data["search_result_summary"],
"content": kb_article_test_data["article_content"],
"expires": "",
"based_on": "",
"comment": kb_article_test_data["changes_description"]
}

response = self.utilities.post_api_request(
page, KBArticlePageMessages.CREATE_NEW_KB_ARTICLE_STAGE_URL, data=form_data
)
print(response)
self.utilities.navigate_to_link(response.url)

first_revision_id = self.kb_article_show_history_page.get_last_revision_id()
if approve_revision:
self.approve_kb_revision(first_revision_id)

return {"article_title": kb_title,
"article_content": kb_article_test_data["article_content"],
"article_slug": slug,
"keyword": kb_article_test_data["keywords"],
"search_results_summary": kb_article_test_data["search_result_summary"],
"article_url": response.url.removesuffix("/history"),
"article_show_history_url": self.utilities.get_page_url(),
"first_revision_id": first_revision_id,
"article_review_description": kb_article_test_data["changes_description"]
}
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,4 @@ def fill_needs_change_textarea(self, text: str):

def click_on_save_changes_button(self):
self._click(self.__save_changes_button)
self._wait_for_dom_load_to_finish()
self.wait_for_page_to_load()
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ def delete_all_inbox_messages_via_delete_selected_button(self, excerpt='', expec
excerpt: The excerpt of the message.
expected_url: The expected URL after deleting all the messages.
"""
self._wait_for_dom_load_to_finish()
self.wait_for_dom_to_load()
if excerpt != '':
inbox_messages_count = self._inbox_message_element_handles(excerpt)
else:
Expand Down
12 changes: 10 additions & 2 deletions playwright_tests/test_data/aaq_question.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,12 @@
},
"Firefox for Enterprise": {
"Accounts": "accounts",
"Browse": "browse",
"Installation and updates": "installation-and-updates",
"Performance and connectivity": "performance-and-connectivity",
"Privacy and security": "privacy-and-security",
"Settings": "settings",
"default_slug": "none"
"default_slug": "firefox-enterprise"
},
"Thunderbird": {
"Accessibility": "accessibility",
Expand All @@ -98,16 +100,22 @@
"Privacy and security": "privacy-and-security",
"Search, tag, and share": "search-tag-and-share",
"Settings": "settings",
"default_slug": "none"
"default_slug": "thunderbird"
},
"Thunderbird for Android": {
"Accessibility": "accessibility",
"Accounts": "accounts",
"Email and messaging": "email-and-messaging",
"Installation and updates": "installation-and-updates",
"Passwords and sign in": "passwords-and-sign-in",
"Performance and connectivity": "performance-and-connectivity",
"Privacy and security": "privacy-and-security",
"Search, tag, and share": "search-tag-and-share",
"Settings": "settings",
"default_slug": "thunderbird-android"
},
"Firefox Focus": {
"Browse": "browse",
"Installation and updates": "installation-and-updates",
"Performance and connectivity": "performance-and-connectivity",
"Privacy and security": "privacy-and-security",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import allure
import pytest
from pytest_check import check

from playwright.sync_api import expect, TimeoutError, Page
from playwright_tests.core.utilities import Utilities
from playwright_tests.messages.ask_a_question_messages.AAQ_messages.aaq_widget import (
Expand All @@ -13,7 +12,9 @@

# C890370, C890374
@pytest.mark.productSolutionsPage
def test_featured_articles_redirect(page: Page):
def test_featured_articles_redirect(page: Page, is_chromium):
if is_chromium:
pytest.skip("Skipping this test for chromium browser")
utilities = Utilities(page)
sumo_pages = SumoPages(page)
with allure.step("Accessing the contact support page via the top navbar Get Help > "
Expand Down
6 changes: 3 additions & 3 deletions playwright_tests/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ def navigate_to_homepage(page: Page):
object.
"""
utilities = Utilities(page)
# Set default navigation timeout to 2 minutes.
page.set_default_navigation_timeout(120000)
# Set default navigation timeout to 30 seconds.
page.set_default_navigation_timeout(30000)

# Block pontoon requests in the current page context.
page.route("**/pontoon.mozilla.org/**", utilities.block_request)
Expand All @@ -36,7 +36,7 @@ def handle_502_error(response):
page.context.on("response", handle_502_error)

# Navigate to the SUMO stage homepage.
page.goto(HomepageMessages.STAGE_HOMEPAGE_URL)
utilities.navigate_to_link(HomepageMessages.STAGE_HOMEPAGE_URL)

return page

Expand Down
Loading

0 comments on commit 247ccc3

Please sign in to comment.