diff --git a/SELECTOR_INFO.md b/SELECTOR_INFO.md index f838f7671..376401225 100644 --- a/SELECTOR_INFO.md +++ b/SELECTOR_INFO.md @@ -935,6 +935,13 @@ Location: about:preferences#general Language subsection Path to .json: modules/data/about_prefs.components.json ``` ``` +Selector Name: search-suggestion-in-private-windows +Selector Data: "showSearchSuggestionsPrivateWindows" +Description: Show search suggestions in Private Windows +Location: about:preferences#search +Path to .json: modules/data/about_prefs.components.json +``` +``` Selector Name: language-settings-search Selector Data: "menuitem[value='search']" Description: In the Language Set Alternatives dialog, the Select a language to add, Search for more languages… option diff --git a/modules/browser_object_navigation.py b/modules/browser_object_navigation.py index 66b58fe09..f0a60bbca 100644 --- a/modules/browser_object_navigation.py +++ b/modules/browser_object_navigation.py @@ -291,6 +291,60 @@ def open_tracker_panel(self) -> BasePage: self.get_element("shield-icon").click() return self + def wait_for_suggestions_present(self, at_least: int = 1): + """Wait until the suggestion list has at least one visible item.""" + self.set_chrome_context() + self.expect(lambda _: len(self.get_elements("suggestion-titles")) >= at_least) + return self + + def wait_for_suggestions_absent(self): + """Wait for the suggestions list to disappear (for non-general engines).""" + self.set_chrome_context() + self.element_not_visible("suggestion-titles") + return self + + def open_usb_and_select_engine(self, engine_title: str): + """Click the USB icon and select a search engine by its title.""" + self.get_element("searchmode-switcher").click() + self.get_element("search-mode-switcher-option", labels=[engine_title]).click() + return self + + def assert_search_mode_chip_visible(self): + """Ensure the search mode indicator (chip) is visible on the left.""" + self.set_chrome_context() + self.get_element("search-mode-span") + return self + + def click_first_suggestion_row(self): + """ + Clicks the first visible suggestion row in the list, using robust scrolling and fallback. + """ + from selenium.webdriver.common.by import By + from selenium.webdriver.common.action_chains import ActionChains + + self.set_chrome_context() + driver = self.driver + + try: + # Prefer Firefox Suggest row if present + row = self.get_element("firefox-suggest") + except Exception: + titles = self.get_elements("suggestion-titles") + assert titles, "No visible suggestion items found." + target = next((t for t in titles if t.is_displayed()), titles[0]) + try: + row = target.find_element(By.XPATH, "ancestor::*[contains(@class,'urlbarView-row')][1]") + except Exception: + row = target + + driver.execute_script("arguments[0].scrollIntoView({block:'center'});", row) + try: + ActionChains(driver).move_to_element(row).click().perform() + except Exception: + driver.execute_script("arguments[0].click();", row) + + return self + @BasePage.context_chrome def click_file_download_warning_panel(self) -> BasePage: """exit file download warning panel if present""" diff --git a/modules/data/about_prefs.components.json b/modules/data/about_prefs.components.json index e655a47fa..5e4b90840 100644 --- a/modules/data/about_prefs.components.json +++ b/modules/data/about_prefs.components.json @@ -370,6 +370,11 @@ "selectorData": "BrowserLanguagesDialog", "strategy": "id", "groups": [] + }, + "search-suggestion-in-private-windows": { + "selectorData": "showSearchSuggestionsPrivateWindows", + "strategy": "id", + "groups": [] }, "language-settings-select": { "selectorData": "[data-l10n-id='browser-languages-select-language']", diff --git a/modules/page_object_prefs.py b/modules/page_object_prefs.py index b54024ba2..f36482089 100644 --- a/modules/page_object_prefs.py +++ b/modules/page_object_prefs.py @@ -65,6 +65,14 @@ def find_in_settings(self, term: str) -> BasePage: search_input.send_keys(term) return self + def enable_private_window_suggestions(self): + """Enable 'Show search suggestions in Private Windows' if not already checked.""" + + checkbox = self.get_element("search-suggestion-in-private-windows") + if checkbox.get_attribute("checked") != "true": + checkbox.click() + return self + def set_alternative_language(self, lang_code: str) -> BasePage: """Changes the browser language""" self.get_element("language-set-alternative-button").click() @@ -692,6 +700,8 @@ def handle_unknown_content_dialog(self) -> BasePage: return self + + class AboutAddons(BasePage): """ The POM for the about:addons page diff --git a/tests/address_bar_and_search/test_search_mode_appears_after_input.py b/tests/address_bar_and_search/test_search_mode_appears_after_input.py new file mode 100644 index 000000000..a95cc8ac8 --- /dev/null +++ b/tests/address_bar_and_search/test_search_mode_appears_after_input.py @@ -0,0 +1,86 @@ +import pytest +from selenium.webdriver.support import expected_conditions as EC + +from modules.browser_object import Navigation +from modules.page_object_prefs import AboutPrefs +from modules.util import BrowserActions + +GENERAL_ENGINE = "Bing" +QUERY = "cobra kai" + + +@pytest.fixture() +def test_case(): + return "3028715" + + +def test_search_mode_appears_and_suggestions_update(driver): + """ + Steps: + 1) Open a new tab. + 2) Start typing in the address bar. + 3) Suggestions list starts to populate using the default search engine. + 4) Click the USB and select another search engine from the list. + 5) Search mode appears (left side). Check suggestions list. + 6) Search terms are retained and new suggestions are returned. + 7) Click one of the search suggestions. + 8) A search is done using the engine selected from step 4. + """ + nav = Navigation(driver) + actions = BrowserActions(driver) + + # 1) Open a new tab + nav.open_and_switch_to_new_window("tab") + + # 2) Start typing (no Enter) + actions.search(QUERY, with_enter=False) + + # 3) Suggestions list populates (default engine) + nav.wait_for_suggestions_present() + + # 4) Click the USB and select another engine + nav.open_usb_and_select_engine(GENERAL_ENGINE) + + # 5) Search mode chip appears + nav.assert_search_mode_chip_visible() + nav.wait_for_suggestions_present() + + # 6–7) Click a visible suggestion + nav.click_first_suggestion_row() + + # 8) Verify search executed with selected engine + nav.expect_in_content(EC.url_contains(GENERAL_ENGINE.lower())) + nav.clear_awesome_bar() + + +def test_private_mode_repeat_after_enabling_pref(driver): + """ + - Enable “Show search suggestions in Private Windows”. + - Open Private Window. + - Repeat steps 1–5 (verify search works with selected engine). + """ + nav = Navigation(driver) + actions = BrowserActions(driver) + + # Enable PBM suggestions pref by its known ID + AboutPrefs(driver, category="search").open().enable_private_window_suggestions() + + # Open Private Window using BasePage method + nav.open_and_switch_to_new_window("private") + + try: + # Repeat steps 1–8 in Private Mode + nav.open_and_switch_to_new_window("tab") + actions.search(QUERY, with_enter=False) + + nav.wait_for_suggestions_present() + nav.open_usb_and_select_engine(GENERAL_ENGINE) + nav.assert_search_mode_chip_visible() + nav.wait_for_suggestions_present() + + nav.click_first_suggestion_row() + nav.expect_in_content(EC.url_contains(GENERAL_ENGINE.lower())) + nav.clear_awesome_bar() + finally: + driver.close() + driver.switch_to.window(driver.window_handles[0])