From 082488144e5884007418e6ae012658ef7e9cecd1 Mon Sep 17 00:00:00 2001 From: Emil Ghitta Date: Tue, 8 Oct 2024 00:42:28 +0300 Subject: [PATCH] - Refactoring the footer.py and homepage.py page objects by marking the page functions as public, replacing super() with self and updating function calls in tests. - Adding a couple of synonyms inside the search_synonym.py page which are used in tests. - Adding a WordNetLemmatizer instance in utilities.py which is going to help extracting the singular form of words when searching for synonyms of the search term inside the search results. - Extending playwright coverage with the first batch of search tests inside the test_main_searchbar.py. - Adding searchTests marker inside pytest.ini and playwright.yml --- .github/workflows/playwright.yml | 6 +- playwright_tests/core/utilities.py | 18 +- playwright_tests/pages/footer.py | 12 +- playwright_tests/pages/homepage.py | 35 +-- playwright_tests/pytest.ini | 1 + playwright_tests/test_data/search_synonym.py | 4 +- .../tests/footer_tests/test_footer.py | 6 +- .../tests/homepage_tests/test_homepage.py | 18 +- .../tests/search_tests/test_main_searchbar.py | 259 ++++++++++++++++++ 9 files changed, 314 insertions(+), 45 deletions(-) create mode 100644 playwright_tests/tests/search_tests/test_main_searchbar.py diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 23ad61b0997..d6564ace9b9 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -36,6 +36,7 @@ on: - recentRevisionsDashboard - kbDashboard - exploreByTopics + - searchTests env: TEST_ACCOUNT_12: ${{secrets.AUTOMATION_TEST_ACCOUNT_12}} @@ -71,6 +72,9 @@ jobs: sudo apt-get update pip3 install --user poetry poetry install + - name: Download NLTK Data + run: | + poetry run python -m nltk.downloader wordnet omw-1.4 - name: Ensure Playwright browsers are installed run: | pip3 install playwright @@ -98,7 +102,7 @@ jobs: if: success() || failure() && steps.create-sessions.outcome == 'success' run: | declare dispatch_test_suite="${{inputs.TestSuite}}" - declare all_test_suites=("homePageTests" "topNavbarTests" "footerSectionTests" "contributePagesTests" "messagingSystem" "messagingSystemCleanup" "userContributionTests" "userProfile" "userSettings" "editUserProfileTests" "userQuestions" "contactSupportPage" "productSolutionsPage" "productTopicsPage" "aaqPage" "postedQuestions" "kbProductsPage" "kbArticleCreationAndAccess" "beforeThreadTests" "articleThreads" "afterThreadTests" "kbArticleShowHistory" "recentRevisionsDashboard" "kbDashboard" "kbRestrictedVisibility" "kbArticleTranslation" "exploreByTopics") + declare all_test_suites=("homePageTests" "topNavbarTests" "footerSectionTests" "contributePagesTests" "messagingSystem" "messagingSystemCleanup" "userContributionTests" "userProfile" "userSettings" "editUserProfileTests" "userQuestions" "contactSupportPage" "productSolutionsPage" "productTopicsPage" "aaqPage" "postedQuestions" "kbProductsPage" "kbArticleCreationAndAccess" "beforeThreadTests" "articleThreads" "afterThreadTests" "kbArticleShowHistory" "recentRevisionsDashboard" "kbDashboard" "kbRestrictedVisibility" "kbArticleTranslation" "exploreByTopics", "searchTests") if [ "$dispatch_test_suite" == "All" ] || [ "${{ github.event_name}}" == "schedule" ] ; then for test in "${all_test_suites[@]}"; do if ! poetry run pytest -m ${test} --numprocesses 2 --browser ${{ env.BROWSER }} --reruns 1; then diff --git a/playwright_tests/core/utilities.py b/playwright_tests/core/utilities.py index 155f9b291ee..7d84f4ef362 100644 --- a/playwright_tests/core/utilities.py +++ b/playwright_tests/core/utilities.py @@ -1,14 +1,12 @@ -from typing import Any, Union - +import os import requests import time import re import json import random -import os +from typing import Any, Union from datetime import datetime - -from nltk import SnowballStemmer +from nltk import SnowballStemmer, WordNetLemmatizer from playwright.sync_api import Page from playwright_tests.messages.homepage_messages import HomepageMessages from requests.exceptions import HTTPError @@ -384,15 +382,19 @@ def _contains_synonym(self, search_result_lower, search_term: Union[str, list[st (split term) are present in the search result. """ synonyms = None + lemmenizer = WordNetLemmatizer() if isinstance(search_term, list): for term in search_term: - synonyms = SearchSynonyms.synonym_dict.get(term.lower(), []) + synonyms = SearchSynonyms.synonym_dict.get(lemmenizer.lemmatize(term.lower(), + pos='n'), []) else: - synonyms = SearchSynonyms.synonym_dict.get(search_term.lower(), []) + synonyms = SearchSynonyms.synonym_dict.get(lemmenizer.lemmatize(search_term.lower(), + pos='n'), []) for term in search_term_split: - synonyms.extend(SearchSynonyms.synonym_dict.get(term, [])) + synonyms.extend(SearchSynonyms.synonym_dict.get(lemmenizer.lemmatize(term, pos='n'), + [])) for synonym in synonyms: if synonym.lower() in search_result_lower: diff --git a/playwright_tests/pages/footer.py b/playwright_tests/pages/footer.py index 737b3f33ebf..f15190489b1 100644 --- a/playwright_tests/pages/footer.py +++ b/playwright_tests/pages/footer.py @@ -13,12 +13,12 @@ def __init__(self, page: Page): super().__init__(page) # Footer section actions. - def _get_all_footer_links(self) -> list[ElementHandle]: - return super()._get_element_handles(self.__all_footer_links) + def get_all_footer_links(self) -> list[ElementHandle]: + return self._get_element_handles(self.__all_footer_links) - def _get_all_footer_locales(self) -> list[str]: - return super()._get_element_attribute_value(super()._get_elements_locators( + def get_all_footer_locales(self) -> list[str]: + return self._get_element_attribute_value(self._get_elements_locators( self.__language_selector_options), "value") - def _switch_to_a_locale(self, locale: str): - super()._select_option_by_value(self.__language_selector, locale) + def switch_to_a_locale(self, locale: str): + self._select_option_by_value(self.__language_selector, locale) diff --git a/playwright_tests/pages/homepage.py b/playwright_tests/pages/homepage.py index c523ace8b65..4b019cc9610 100644 --- a/playwright_tests/pages/homepage.py +++ b/playwright_tests/pages/homepage.py @@ -23,28 +23,31 @@ def __init__(self, page: Page): super().__init__(page) # Product Cards - def _get_text_of_product_card_titles(self) -> list[str]: - return super()._get_text_of_elements(self.__product_card_titles) + def get_text_of_product_card_titles(self) -> list[str]: + return self._get_text_of_elements(self.__product_card_titles) - def _click_on_product_card(self, element_number): - super()._click_on_an_element_by_index(self.__product_list, element_number) + def click_on_product_card(self, element_number): + self._click_on_an_element_by_index(self.__product_list, element_number) + + def click_on_product_card_by_title(self, card_title: str): + self._click(f"//h3[@class='card--title']/a[normalize-space(text())='{card_title}']") # Featured articles - def _get_number_of_featured_articles(self) -> int: - return super()._get_elements_count(self.__featured_articles_list) + def get_number_of_featured_articles(self) -> int: + return self._get_elements_count(self.__featured_articles_list) - def _get_featured_articles_titles(self) -> list[str]: - return super()._get_text_of_elements(self.__featured_articles_card_titles) + def get_featured_articles_titles(self) -> list[str]: + return self._get_text_of_elements(self.__featured_articles_card_titles) - def _click_on_a_featured_card(self, element_number: int): - super()._click_on_an_element_by_index(self.__featured_articles_card_items, element_number) + def click_on_a_featured_card(self, element_number: int): + self._click_on_an_element_by_index(self.__featured_articles_card_items, element_number) # Learn More - def _click_learn_more_option(self): - super()._click(self.__learn_more_option) + def click_learn_more_option(self): + self._click(self.__learn_more_option) - def _get_community_card_title(self) -> str: - return super()._get_text_of_element(self.__join_our_community_card_title) + def get_community_card_title(self) -> str: + return self._get_text_of_element(self.__join_our_community_card_title) - def _get_community_card_description(self) -> str: - return super()._get_text_of_element(self.__join_our_community_card_description) + def get_community_card_description(self) -> str: + return self._get_text_of_element(self.__join_our_community_card_description) diff --git a/playwright_tests/pytest.ini b/playwright_tests/pytest.ini index 3a8dc2f91ad..6a1aa5ab5bd 100644 --- a/playwright_tests/pytest.ini +++ b/playwright_tests/pytest.ini @@ -31,4 +31,5 @@ markers = deleteAllRestrictedTestArticles: Deleting all restricted test articles. create_delete_article: Fixture used for creating and deleting test articles. exploreByTopics: Tests belonging to the explore help articles by topics page. + searchTests: Tests belonging to the search functionality. addopts = --alluredir=./reports/allure_reports --tb=no diff --git a/playwright_tests/test_data/search_synonym.py b/playwright_tests/test_data/search_synonym.py index c010a08e8e2..bef690e61d7 100644 --- a/playwright_tests/test_data/search_synonym.py +++ b/playwright_tests/test_data/search_synonym.py @@ -104,8 +104,8 @@ class SearchSynonyms: 'addon': ['extension', 'theme'], 'add-on': ['extension', 'theme'], 'add-ons': ['extensions', 'themes', "addon"], - 'extension': ['addon', 'theme'], - 'theme': ['addon', 'extension'], + 'extension': ['addon', 'theme', 'add-on'], + 'theme': ['addon', 'extension', 'add-on'], 'awesome bar': ['address bar', 'url bar', 'location bar', 'location field', 'url field'], 'address bar': ['awesome bar', 'url bar', 'location bar', 'location field', 'url field'], 'url bar': ['awesome bar', 'address bar', 'location bar', 'location field', 'url field'], diff --git a/playwright_tests/tests/footer_tests/test_footer.py b/playwright_tests/tests/footer_tests/test_footer.py index f526306044d..0517e637c1b 100644 --- a/playwright_tests/tests/footer_tests/test_footer.py +++ b/playwright_tests/tests/footer_tests/test_footer.py @@ -12,7 +12,7 @@ @pytest.mark.footerSectionTests def test_all_footer_links_are_working(page: Page): sumo_pages = SumoPages(page) - for link in sumo_pages.footer_section._get_all_footer_links(): + for link in sumo_pages.footer_section.get_all_footer_links(): relative_url = link.get_attribute("href") # Verify if URL is absolute, and construct the full URL if it's not @@ -48,8 +48,8 @@ def test_locale_selector(page: Page): sumo_pages = SumoPages(page) with allure.step("Verifying that all footer select options are redirecting the user to the " "correct page locale"): - for locale in sumo_pages.footer_section._get_all_footer_locales(): - sumo_pages.footer_section._switch_to_a_locale(locale) + for locale in sumo_pages.footer_section.get_all_footer_locales(): + sumo_pages.footer_section.switch_to_a_locale(locale) expect( page ).to_have_url(re.compile(f".*{locale}")) diff --git a/playwright_tests/tests/homepage_tests/test_homepage.py b/playwright_tests/tests/homepage_tests/test_homepage.py index b552025554a..9e18265a7cf 100644 --- a/playwright_tests/tests/homepage_tests/test_homepage.py +++ b/playwright_tests/tests/homepage_tests/test_homepage.py @@ -17,7 +17,7 @@ def test_join_our_community_card_learn_more_redirects_to_contribute_page(page: P sumo_pages = SumoPages(page) utilities = Utilities(page) with allure.step("Clicking on the 'Learn More' option"): - sumo_pages.homepage._click_learn_more_option() + sumo_pages.homepage.click_learn_more_option() with allure.step("Verifying that we are redirected to the 'Contribute' page successfully"): assert ( @@ -34,9 +34,9 @@ def test_join_our_community_card_has_the_correct_content(page: Page): "Verifying that the 'Join Our Community' card has the correct strings applied" ): assert ( - sumo_pages.homepage._get_community_card_title() + sumo_pages.homepage.get_community_card_title() == HomepageMessages.JOIN_OUR_COMMUNITY_CARD_TITLE - and sumo_pages.homepage._get_community_card_description() + and sumo_pages.homepage.get_community_card_description() == HomepageMessages.JOIN_OUR_COMMUNITY_CARD_DESCRIPTION ), "Incorrect strings are displayed" @@ -49,15 +49,15 @@ def test_homepage_feature_articles_are_available_and_interactable(page: Page): with check, allure.step( "Verifying if the correct number of featured articles are present on the homepage" ): - assert sumo_pages.homepage._get_number_of_featured_articles( + assert sumo_pages.homepage.get_number_of_featured_articles( ) is HomepageMessages.EXPECTED_FEATURED_ARTICLES_COUNT with allure.step("Clicking on each featured article card and verifying that the user is" "redirected to the correct article page."): counter = 0 - for featured_article in sumo_pages.homepage._get_featured_articles_titles(): - articles_names = sumo_pages.homepage._get_featured_articles_titles() - sumo_pages.homepage._click_on_a_featured_card(counter) + for featured_article in sumo_pages.homepage.get_featured_articles_titles(): + articles_names = sumo_pages.homepage.get_featured_articles_titles() + sumo_pages.homepage.click_on_a_featured_card(counter) assert ( sumo_pages.kb_article_page.get_text_of_article_title().strip() == articles_names[counter].strip() @@ -75,11 +75,11 @@ def test_product_cards_are_functional_and_redirect_to_the_proper_support_page(pa sumo_pages = SumoPages(page) utilities = Utilities(page) with allure.step("Verifying that the product cards redirect to the correct support page"): - card_titles = sumo_pages.homepage._get_text_of_product_card_titles() + card_titles = sumo_pages.homepage.get_text_of_product_card_titles() counter = 0 for product_card in card_titles: expected_product_title = card_titles[counter] + SupportPageMessages.TITLE_CONTAINS - sumo_pages.homepage._click_on_product_card(counter) + sumo_pages.homepage.click_on_product_card(counter) assert ( expected_product_title == sumo_pages.product_support_page._get_product_support_title_text() diff --git a/playwright_tests/tests/search_tests/test_main_searchbar.py b/playwright_tests/tests/search_tests/test_main_searchbar.py new file mode 100644 index 00000000000..49216115ab3 --- /dev/null +++ b/playwright_tests/tests/search_tests/test_main_searchbar.py @@ -0,0 +1,259 @@ +import time + +import allure +import pytest +from playwright.sync_api import Page +from playwright_tests.core.utilities import Utilities +from playwright_tests.messages.homepage_messages import HomepageMessages +from playwright_tests.pages.sumo_pages import SumoPages +from pytest_check import check + + +# C1329220 +@pytest.mark.searchTests +def test_popular_searches_homepage(page: Page): + sumo_pages = SumoPages(page) + utilities = Utilities(page) + + with allure.step("Clicking on all popular searches for the homepage searchbar"): + for popular_search in sumo_pages.search_page.get_list_of_popular_searches(): + sumo_pages.search_page.click_on_a_popular_search(popular_search) + with check, allure.step("Verifying that the correct text is added inside the search " + "field"): + utilities.wait_for_given_timeout(2000) + assert sumo_pages.search_page.get_text_of_searchbar_field() == popular_search + + with check, allure.step("Verifying that 'All products' is highlighted inside the " + "side-navbar"): + assert sumo_pages.search_page.get_the_highlighted_side_nav_item() == "All Products" + with check, allure.step("Verifying that search results contain the correct keyword"): + if not sumo_pages.search_page.get_all_search_results_article_titles(): + assert False, (f"Search results contains 0 results for the {popular_search} " + f"search term") + for title in sumo_pages.search_page.get_all_search_results_article_titles(): + content = sumo_pages.search_page.get_all_search_results_article_bolded_content( + title + ) + assert _verify_search_results(page, popular_search, 'english', title, content) + with allure.step("Navigating back"): + utilities.navigate_back() + + +# Skipping because of https://github.com/mozilla/sumo/issues/1924 failure. +# This tests also causes a failure (driver becomes unresponsive) on rerun which needs to be +# investigated. +@pytest.mark.skip +def test_popular_searches_products(page: Page): + sumo_pages = SumoPages(page) + utilities = Utilities(page) + with allure.step("Navigating to each product support page"): + for product, support_link in utilities.general_test_data['product_support'].items(): + print(support_link) + utilities.navigate_to_link(support_link) + for popular_search in sumo_pages.search_page.get_list_of_popular_searches(): + sumo_pages.search_page.click_on_a_popular_search(popular_search) + with check, allure.step("Verifying that the correct text was added inside the " + "search field"): + utilities.wait_for_given_timeout(2000) + assert sumo_pages.search_page.get_text_of_searchbar_field() == popular_search + + with check, allure.step("Verifying that the correct product is listed in the side " + "navbar"): + assert sumo_pages.search_page.get_the_highlighted_side_nav_item( + ) == product.strip() + if not sumo_pages.search_page.get_all_search_results_article_titles(): + assert False, (f"Search results contains 0 results for the" + f" {popular_search} search term on the {product}" + f" product!") + + for title in sumo_pages.search_page.get_all_search_results_article_titles(): + content = sumo_pages.search_page.get_all_search_results_article_bolded_content( + title) + assert _verify_search_results(page, popular_search, 'english', title, content) + + with allure.step("Navigating back"): + utilities.navigate_back() + + +# C1329221 +@pytest.mark.searchTests +def test_stopwords(page: Page): + sumo_pages = SumoPages(page) + utilities = Utilities(page) + search_term = 'oricând instalez firefox' + + with allure.step("Switching the SUMO version to RO"): + sumo_pages.footer_section.switch_to_a_locale("ro") + utilities.wait_for_given_timeout(2000) + + with check, allure.step("Adding a search term inside the search bar which contains a stopword:" + " 'oricând' and verifying that it does not impact the search result"): + sumo_pages.search_page.fill_into_searchbar(search_term) + utilities.wait_for_given_timeout(2000) + for title in sumo_pages.search_page.get_all_search_results_article_titles(): + content = sumo_pages.search_page.get_all_search_results_article_bolded_content(title) + assert _verify_search_results(page, search_term.replace('oricând', ''), + 'romanian', title, content) + + +# C1329222 +@pytest.mark.searchTests +def test_synonyms(page: Page): + sumo_pages = SumoPages(page) + utilities = Utilities(page) + search_terms = ['addon', 'add-on', 'add', 'how to add bookmarks', 'how to add themes', + 'pop-ups', 'popups', 'pop ups'] + with check, allure.step("Adding a search term and validating the search results"): + for search_term in search_terms: + sumo_pages.search_page.fill_into_searchbar(search_term) + utilities.wait_for_given_timeout(2000) + for title in sumo_pages.search_page.get_all_search_results_article_titles(): + content = sumo_pages.search_page.get_all_search_results_article_bolded_content( + title) + + assert _verify_search_results(page, search_term, 'english', title, content) + + sumo_pages.search_page.clear_the_searchbar() + + +# C1329223 +@pytest.mark.searchTests +def test_brand_synonyms(page: Page): + sumo_pages = SumoPages(page) + utilities = Utilities(page) + search_terms = ['mozilla', 'modzilla', 'mozzila', 'mozzilla', 'mozila', 'ios', 'ipad', + 'iphone', 'ipod'] + with check, allure.step("Adding a search term inside the search bar and validating search " + "results"): + for search_term in search_terms: + sumo_pages.search_page.fill_into_searchbar(search_term) + utilities.wait_for_given_timeout(2000) + for title in sumo_pages.search_page.get_all_search_results_article_titles(): + content = sumo_pages.search_page.get_all_search_results_article_bolded_content( + title) + + assert _verify_search_results(page, search_term, 'english', title, content) + sumo_pages.search_page.clear_the_searchbar() + + +# C1329234 +@pytest.mark.searchTests +def test_searchbar_content_during_navigation(page: Page): + sumo_pages = SumoPages(page) + utilities = Utilities(page) + search_term = 'test' + + with allure.step("Typing inside the searchbar and navigating back"): + sumo_pages.search_page.fill_into_searchbar(search_term) + utilities.wait_for_given_timeout(2000) + utilities.navigate_back() + + with check, allure.step("Verifying that the searchbar is empty"): + assert sumo_pages.search_page.get_text_of_searchbar_field() == '' + + with check, allure.step("Navigating forward and verifying that the previously added search " + "term is displayed inside the searchbar"): + utilities.navigate_forward() + assert sumo_pages.search_page.get_text_of_searchbar_field() == search_term + + +# C1329236 +@pytest.mark.searchTests +def test_logo_redirect_during_search(page: Page): + sumo_pages = SumoPages(page) + utilities = Utilities(page) + search_term = 'test' + + with allure.step("Typing inside the searchbar and clicking on the logo"): + sumo_pages.search_page.fill_into_searchbar(search_term) + utilities.wait_for_given_timeout(2000) + sumo_pages.top_navbar.click_on_sumo_nav_logo() + + with check, allure.step("Verifying that the user was redirected to the homepage and that the " + "searchbar is empty"): + assert utilities.get_page_url() == HomepageMessages.STAGE_HOMEPAGE_URL_EN_US + assert sumo_pages.search_page.get_text_of_searchbar_field() == '' + + with check, allure.step("Navigating back and verifying that the searchbar contains the " + "previously used search term"): + utilities.navigate_back() + assert sumo_pages.search_page.get_text_of_searchbar_field() == search_term + + +# C1329239 +@pytest.mark.searchTests +def test_searchbar_search_update(page: Page): + sumo_pages = SumoPages(page) + utilities = Utilities(page) + search_terms = ['Mozilla', 'Mozilla themes', 'install'] + + with check, allure.step("Adding a search term inside the search bar and validating search " + "results"): + for search_term in search_terms: + sumo_pages.search_page.fill_into_searchbar(search_term) + utilities.wait_for_given_timeout(3000) + for title in sumo_pages.search_page.get_all_search_results_article_titles(): + content = sumo_pages.search_page.get_all_search_results_article_bolded_content( + title) + assert _verify_search_results(page, search_term, 'english', title, content) + sumo_pages.search_page.clear_the_searchbar() + + +# C1329243 +@pytest.mark.searchTests +def test_search_from_products_page(page: Page): + sumo_pages = SumoPages(page) + utilities = Utilities(page) + search_term = 'Mozilla' + + with allure.step("Clicking on each product card from the SUMO homepage"): + card_titles = sumo_pages.homepage.get_text_of_product_card_titles() + for product_card in card_titles: + with check, allure.step("Verifying that the search results are filtered by the " + "correct product and the search term is present"): + sumo_pages.homepage.click_on_product_card_by_title(product_card) + sumo_pages.search_page.clear_the_searchbar() + sumo_pages.search_page.fill_into_searchbar(search_term) + utilities.wait_for_given_timeout(2000) + assert sumo_pages.search_page.get_the_highlighted_side_nav_item() == product_card + for title in sumo_pages.search_page.get_all_search_results_article_titles(): + content = sumo_pages.search_page.get_all_search_results_article_bolded_content( + title) + assert _verify_search_results(page, search_term, 'english', title, content) + + with allure.step("Navigating back to the homepage"): + sumo_pages.top_navbar.click_on_sumo_nav_logo() + + +# C1329243 +@pytest.mark.searchTests +def test_filter_switching(page: Page): + sumo_pages = SumoPages(page) + utilities = Utilities(page) + search_term = "Firefox Focus" + with check, allure.step("Adding a search term inside the search bar and validating search " + "results"): + sumo_pages.search_page.fill_into_searchbar(search_term) + sumo_pages.search_page.click_on_a_particular_side_nav_item('Firefox for Android') + utilities.wait_for_given_timeout(2000) + for title in sumo_pages.search_page.get_all_search_results_article_titles(): + content = sumo_pages.search_page.get_all_search_results_article_bolded_content(title) + assert _verify_search_results(page, search_term, 'english', title, content) + + with allure.step("Switching the product filter to something that returns 0 results"): + sumo_pages.search_page.click_on_a_particular_side_nav_item('Firefox Relay') + time.sleep(2) + assert not sumo_pages.search_page.is_search_content_section_displayed() + + +def _verify_search_results(page: Page, search_term: str, locale: str, search_result_title: str, + search_result_bolded_content: list[str], exact_phrase=False) -> bool: + utilities = Utilities(page) + in_title = utilities.search_result_check(search_result_title, search_term, locale, + exact_phrase) + in_content = False + for content in search_result_bolded_content: + if utilities.search_result_check(content, search_term, locale, exact_phrase): + in_content = True + break + return in_title or in_content